summaryrefslogtreecommitdiff
path: root/source4/rpc_server
diff options
context:
space:
mode:
Diffstat (limited to 'source4/rpc_server')
-rw-r--r--source4/rpc_server/common/common.h37
-rw-r--r--source4/rpc_server/common/server_info.c217
-rw-r--r--source4/rpc_server/common/share_info.c117
-rw-r--r--source4/rpc_server/config.mk212
-rw-r--r--source4/rpc_server/dcerpc_server.c1439
-rw-r--r--source4/rpc_server/dcerpc_server.h372
-rw-r--r--source4/rpc_server/dcesrv_auth.c529
-rw-r--r--source4/rpc_server/dcesrv_mgmt.c102
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.c818
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.h37
-rw-r--r--source4/rpc_server/echo/rpc_echo.c204
-rw-r--r--source4/rpc_server/epmapper/rpc_epmapper.c264
-rw-r--r--source4/rpc_server/handles.c91
-rw-r--r--source4/rpc_server/lsa/dcesrv_lsa.c3176
-rw-r--r--source4/rpc_server/lsa/lsa.h68
-rw-r--r--source4/rpc_server/lsa/lsa_init.c243
-rw-r--r--source4/rpc_server/lsa/lsa_lookup.c932
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c1320
-rw-r--r--source4/rpc_server/remote/README38
-rw-r--r--source4/rpc_server/remote/dcesrv_remote.c326
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c4317
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.h69
-rw-r--r--source4/rpc_server/samr/samr_password.c601
-rw-r--r--source4/rpc_server/service_rpc.c486
-rw-r--r--source4/rpc_server/spoolss/dcesrv_spoolss.c1560
-rw-r--r--source4/rpc_server/srvsvc/dcesrv_srvsvc.c2327
-rw-r--r--source4/rpc_server/srvsvc/srvsvc_ntvfs.c139
-rw-r--r--source4/rpc_server/unixinfo/dcesrv_unixinfo.c242
-rw-r--r--source4/rpc_server/winreg/README3
-rw-r--r--source4/rpc_server/winreg/rpc_winreg.c665
-rw-r--r--source4/rpc_server/wkssvc/dcesrv_wkssvc.c416
31 files changed, 21367 insertions, 0 deletions
diff --git a/source4/rpc_server/common/common.h b/source4/rpc_server/common/common.h
new file mode 100644
index 0000000000..af2d96cb3e
--- /dev/null
+++ b/source4/rpc_server/common/common.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common macros for the dcerpc server interfaces
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+struct share_config;
+struct dcesrv_context;
+enum srvsvc_ShareType dcesrv_common_get_share_type(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+enum srvsvc_PlatformId dcesrv_common_get_platform_id(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx);
+const char *dcesrv_common_get_domain_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx);
+const char *dcesrv_common_get_lan_root(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx);
+const char *dcesrv_common_get_server_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, const char *server_unc);
+uint32_t dcesrv_common_get_version_major(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+uint32_t dcesrv_common_get_version_minor(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+uint32_t dcesrv_common_get_version_build(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+uint32_t dcesrv_common_get_share_permissions(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+uint32_t dcesrv_common_get_share_current_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+const char *dcesrv_common_get_share_path(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+
+struct dcesrv_context;
diff --git a/source4/rpc_server/common/server_info.c b/source4/rpc_server/common/server_info.c
new file mode 100644
index 0000000000..da034e85ea
--- /dev/null
+++ b/source4/rpc_server/common/server_info.c
@@ -0,0 +1,217 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common server info functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/svcctl.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "param/param.h"
+
+/*
+ Here are common server info functions used by some dcerpc server interfaces
+*/
+
+/* This hardcoded value should go into a ldb database! */
+enum srvsvc_PlatformId dcesrv_common_get_platform_id(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ enum srvsvc_PlatformId id;
+
+ id = lp_parm_int(dce_ctx->lp_ctx, NULL, "server_info", "platform_id", PLATFORM_ID_NT);
+
+ return id;
+}
+
+const char *dcesrv_common_get_server_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, const char *server_unc)
+{
+ const char *p = server_unc;
+
+ /* if there's no string return our NETBIOS name */
+ if (!p) {
+ return talloc_strdup(mem_ctx, lp_netbios_name(dce_ctx->lp_ctx));
+ }
+
+ /* if there're '\\\\' in front remove them otherwise just pass the string */
+ if (p[0] == '\\' && p[1] == '\\') {
+ p += 2;
+ }
+
+ return talloc_strdup(mem_ctx, p);
+}
+
+const char *dcesrv_common_get_domain_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, lp_workgroup(dce_ctx->lp_ctx));
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_version_major(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return lp_parm_int(lp_ctx, NULL, "server_info", "version_major", 5);
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_version_minor(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return lp_parm_int(lp_ctx, NULL, "server_info", "version_minor", 2);
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_version_build(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return lp_parm_int(lp_ctx, NULL, "server_info", "version_build", 3790);
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_server_type(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, struct dcesrv_context *dce_ctx)
+{
+ int default_server_announce = 0;
+ default_server_announce |= SV_TYPE_WORKSTATION;
+ default_server_announce |= SV_TYPE_SERVER;
+ default_server_announce |= SV_TYPE_SERVER_UNIX;
+
+ switch (lp_announce_as(dce_ctx->lp_ctx)) {
+ case ANNOUNCE_AS_NT_SERVER:
+ default_server_announce |= SV_TYPE_SERVER_NT;
+ /* fall through... */
+ case ANNOUNCE_AS_NT_WORKSTATION:
+ default_server_announce |= SV_TYPE_NT;
+ break;
+ case ANNOUNCE_AS_WIN95:
+ default_server_announce |= SV_TYPE_WIN95_PLUS;
+ break;
+ case ANNOUNCE_AS_WFW:
+ default_server_announce |= SV_TYPE_WFW;
+ break;
+ default:
+ break;
+ }
+
+ switch (lp_server_role(dce_ctx->lp_ctx)) {
+ case ROLE_DOMAIN_MEMBER:
+ default_server_announce |= SV_TYPE_DOMAIN_MEMBER;
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ {
+ struct ldb_context *samctx;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ break;
+ }
+ /* open main ldb */
+ samctx = samdb_connect(tmp_ctx, event_ctx, dce_ctx->lp_ctx, anonymous_session(tmp_ctx, event_ctx, dce_ctx->lp_ctx));
+ if (samctx == NULL) {
+ DEBUG(2,("Unable to open samdb in determining server announce flags\n"));
+ } else {
+ /* Determine if we are the pdc */
+ bool is_pdc = samdb_is_pdc(samctx);
+ if (is_pdc) {
+ default_server_announce |= SV_TYPE_DOMAIN_CTRL;
+ } else {
+ default_server_announce |= SV_TYPE_DOMAIN_BAKCTRL;
+ }
+ }
+ /* Close it */
+ talloc_free(tmp_ctx);
+ break;
+ }
+ case ROLE_STANDALONE:
+ default:
+ break;
+ }
+ if (lp_time_server(dce_ctx->lp_ctx))
+ default_server_announce |= SV_TYPE_TIME_SOURCE;
+
+ if (lp_host_msdfs(dce_ctx->lp_ctx))
+ default_server_announce |= SV_TYPE_DFS_SERVER;
+
+
+#if 0
+ {
+ /* TODO: announce us as print server when we are a print server */
+ bool is_print_server = false;
+ if (is_print_server) {
+ default_server_announce |= SV_TYPE_PRINTQ_SERVER;
+ }
+ }
+#endif
+ return default_server_announce;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_lan_root(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, "");
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return -1;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_disc(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 15;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_hidden(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_announce(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 240;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_anndelta(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 3000;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_licenses(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_userpath(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, "c:\\");
+}
+
+#define INVALID_SHARE_NAME_CHARS " \"*+,./:;<=>?[\\]|"
+
+bool dcesrv_common_validate_share_name(TALLOC_CTX *mem_ctx, const char *share_name)
+{
+ if (strpbrk(share_name, INVALID_SHARE_NAME_CHARS)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/rpc_server/common/share_info.c b/source4/rpc_server/common/share_info.c
new file mode 100644
index 0000000000..b27dc37949
--- /dev/null
+++ b/source4/rpc_server/common/share_info.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common share info functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "librpc/gen_ndr/srvsvc.h"
+#include "rpc_server/dcerpc_server.h"
+
+/*
+ Here are common server info functions used by some dcerpc server interfaces
+*/
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_permissions(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_current_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 1;
+}
+
+/* This hardcoded value should go into a ldb database! */
+enum srvsvc_ShareType dcesrv_common_get_share_type(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ /* for disk share 0x00000000
+ * for print share 0x00000001
+ * for IPC$ share 0x00000003
+ *
+ * administrative shares:
+ * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
+ * this ones are hidden in NetShareEnum, but shown in NetShareEnumAll
+ */
+ enum srvsvc_ShareType share_type = 0;
+ const char *sharetype;
+
+ if (!share_bool_option(scfg, SHARE_BROWSEABLE, SHARE_BROWSEABLE_DEFAULT)) {
+ share_type |= STYPE_HIDDEN;
+ }
+
+ sharetype = share_string_option(scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+ if (sharetype && strcasecmp(sharetype, "IPC") == 0) {
+ share_type |= STYPE_IPC;
+ return share_type;
+ }
+
+ if (sharetype && strcasecmp(sharetype, "PRINTER") == 0) {
+ share_type |= STYPE_PRINTQ;
+ return share_type;
+ }
+
+ share_type |= STYPE_DISKTREE;
+
+ return share_type;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_share_path(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ const char *sharetype;
+ char *p;
+
+ sharetype = share_string_option(scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+
+ if (sharetype && strcasecmp(sharetype, "IPC") == 0) {
+ return talloc_strdup(mem_ctx, "");
+ }
+
+ p = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_PATH, ""));
+ if (!p) {
+ return NULL;
+ }
+ if (p[0] == '\0') {
+ return p;
+ }
+ all_string_sub(p, "/", "\\", 0);
+
+ return talloc_asprintf(mem_ctx, "C:%s", p);
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_dfs_flags(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_unknown(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+struct security_descriptor *dcesrv_common_get_security_descriptor(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return NULL;
+}
diff --git a/source4/rpc_server/config.mk b/source4/rpc_server/config.mk
new file mode 100644
index 0000000000..6b1813544e
--- /dev/null
+++ b/source4/rpc_server/config.mk
@@ -0,0 +1,212 @@
+# DCERPC Server subsystem
+
+################################################
+# Start SUBSYSTEM DCERPC_COMMON
+[SUBSYSTEM::DCERPC_COMMON]
+#
+# End SUBSYSTEM DCERPC_COMMON
+################################################
+
+DCERPC_COMMON_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/common/, server_info.o share_info.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/common/proto.h,$(DCERPC_COMMON_OBJ_FILES:.o=.c)))
+
+PUBLIC_HEADERS += $(rpc_serversrcdir)/common/common.h
+
+################################################
+# Start MODULE dcerpc_rpcecho
+[MODULE::dcerpc_rpcecho]
+INIT_FUNCTION = dcerpc_server_rpcecho_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = NDR_ECHO
+# End MODULE dcerpc_rpcecho
+################################################
+
+dcerpc_rpcecho_OBJ_FILES = $(rpc_serversrcdir)/echo/rpc_echo.o
+
+################################################
+# Start MODULE dcerpc_epmapper
+[MODULE::dcerpc_epmapper]
+INIT_FUNCTION = dcerpc_server_epmapper_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = NDR_EPMAPPER
+# End MODULE dcerpc_epmapper
+################################################
+
+dcerpc_epmapper_OBJ_FILES = $(rpc_serversrcdir)/epmapper/rpc_epmapper.o
+
+################################################
+# Start MODULE dcerpc_remote
+[MODULE::dcerpc_remote]
+INIT_FUNCTION = dcerpc_server_remote_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB NDR_TABLE
+# End MODULE dcerpc_remote
+################################################
+
+dcerpc_remote_OBJ_FILES = $(rpc_serversrcdir)/remote/dcesrv_remote.o
+
+################################################
+# Start MODULE dcerpc_srvsvc
+[MODULE::dcerpc_srvsvc]
+INIT_FUNCTION = dcerpc_server_srvsvc_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON NDR_SRVSVC share
+# End MODULE dcerpc_srvsvc
+################################################
+
+
+dcerpc_srvsvc_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/srvsvc/, dcesrv_srvsvc.o srvsvc_ntvfs.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/srvsvc/proto.h,$(dcerpc_srvsvc_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE dcerpc_wkssvc
+[MODULE::dcerpc_wkssvc]
+INIT_FUNCTION = dcerpc_server_wkssvc_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON NDR_WKSSVC
+# End MODULE dcerpc_wkssvc
+################################################
+
+dcerpc_wkssvc_OBJ_FILES = $(rpc_serversrcdir)/wkssvc/dcesrv_wkssvc.o
+
+################################################
+# Start MODULE dcerpc_unixinfo
+[MODULE::dcerpc_unixinfo]
+INIT_FUNCTION = dcerpc_server_unixinfo_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ SAMDB \
+ NDR_UNIXINFO \
+ NSS_WRAPPER \
+ LIBWBCLIENT
+# End MODULE dcerpc_unixinfo
+################################################
+
+dcerpc_unixinfo_OBJ_FILES = $(rpc_serversrcdir)/unixinfo/dcesrv_unixinfo.o
+
+################################################
+# Start MODULE dcesrv_samr
+[MODULE::dcesrv_samr]
+INIT_FUNCTION = dcerpc_server_samr_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ DCERPC_COMMON \
+ NDR_SAMR
+# End MODULE dcesrv_samr
+################################################
+
+dcesrv_samr_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/samr/, dcesrv_samr.o samr_password.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/samr/proto.h,$(dcesrv_samr_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE dcerpc_winreg
+[MODULE::dcerpc_winreg]
+INIT_FUNCTION = dcerpc_server_winreg_init
+SUBSYSTEM = DCESRV
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = \
+ registry NDR_WINREG
+# End MODULE dcerpc_winreg
+################################################
+
+dcerpc_winreg_OBJ_FILES = $(rpc_serversrcdir)/winreg/rpc_winreg.o
+
+################################################
+# Start MODULE dcerpc_netlogon
+[MODULE::dcerpc_netlogon]
+INIT_FUNCTION = dcerpc_server_netlogon_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ SCHANNELDB \
+ NDR_NETLOGON \
+ auth_sam
+# End MODULE dcerpc_netlogon
+################################################
+
+dcerpc_netlogon_OBJ_FILES = $(rpc_serversrcdir)/netlogon/dcerpc_netlogon.o
+
+################################################
+# Start MODULE dcerpc_lsa
+[MODULE::dcerpc_lsarpc]
+INIT_FUNCTION = dcerpc_server_lsa_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ DCERPC_COMMON \
+ NDR_LSA \
+ LIBCLI_AUTH \
+ NDR_DSSETUP
+# End MODULE dcerpc_lsa
+################################################
+
+dcerpc_lsarpc_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/lsa/, dcesrv_lsa.o lsa_init.o lsa_lookup.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/lsa/proto.h,$(dcerpc_lsarpc_OBJ_FILES:.o=.c)))
+
+
+################################################
+# Start MODULE dcerpc_spoolss
+[MODULE::dcerpc_spoolss]
+INIT_FUNCTION = dcerpc_server_spoolss_init
+SUBSYSTEM = DCESRV
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ NDR_SPOOLSS \
+ ntptr \
+ RPC_NDR_SPOOLSS
+# End MODULE dcerpc_spoolss
+################################################
+
+dcerpc_spoolss_OBJ_FILES = $(rpc_serversrcdir)/spoolss/dcesrv_spoolss.o
+
+################################################
+# Start MODULE dcerpc_drsuapi
+[MODULE::dcerpc_drsuapi]
+INIT_FUNCTION = dcerpc_server_drsuapi_init
+SUBSYSTEM = DCESRV
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ DCERPC_COMMON \
+ NDR_DRSUAPI
+# End MODULE dcerpc_drsuapi
+################################################
+
+dcerpc_drsuapi_OBJ_FILES = $(rpc_serversrcdir)/drsuapi/dcesrv_drsuapi.o
+
+################################################
+# Start SUBSYSTEM dcerpc_server
+[SUBSYSTEM::dcerpc_server]
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_AUTH \
+ LIBNDR \
+ dcerpc
+
+dcerpc_server_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/, \
+ dcerpc_server.o \
+ dcesrv_auth.o \
+ dcesrv_mgmt.o \
+ handles.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/dcerpc_server_proto.h,$(dcerpc_server_OBJ_FILES:.o=.c)))
+
+# End SUBSYSTEM DCERPC
+################################################
+
+PUBLIC_HEADERS += $(rpc_serversrcdir)/dcerpc_server.h
+
+[MODULE::DCESRV]
+INIT_FUNCTION = server_service_rpc_init
+SUBSYSTEM = smbd
+PRIVATE_DEPENDENCIES = dcerpc_server
+
+DCESRV_OBJ_FILES = $(rpc_serversrcdir)/service_rpc.o
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
new file mode 100644
index 0000000000..e5f59d0cf9
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.c
@@ -0,0 +1,1439 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc core code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/events/events.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "system/filesys.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+#define SAMBA_ACCOC_GROUP 0x12345678
+
+extern const struct dcesrv_interface dcesrv_mgmt_interface;
+
+/*
+ see if two endpoints match
+*/
+static bool endpoints_match(const struct dcerpc_binding *ep1,
+ const struct dcerpc_binding *ep2)
+{
+ if (ep1->transport != ep2->transport) {
+ return false;
+ }
+
+ if (!ep1->endpoint || !ep2->endpoint) {
+ return ep1->endpoint == ep2->endpoint;
+ }
+
+ if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
+ return false;
+
+ return true;
+}
+
+/*
+ find an endpoint in the dcesrv_context
+*/
+static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
+ const struct dcerpc_binding *ep_description)
+{
+ struct dcesrv_endpoint *ep;
+ for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
+ if (endpoints_match(ep->ep_description, ep_description)) {
+ return ep;
+ }
+ }
+ return NULL;
+}
+
+/*
+ find a registered context_id from a bind or alter_context
+*/
+static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
+ uint32_t context_id)
+{
+ struct dcesrv_connection_context *c;
+ for (c=conn->contexts;c;c=c->next) {
+ if (c->context_id == context_id) return c;
+ }
+ return NULL;
+}
+
+/*
+ see if a uuid and if_version match to an interface
+*/
+static bool interface_match(const struct dcesrv_interface *if1,
+ const struct dcesrv_interface *if2)
+{
+ return (if1->syntax_id.if_version == if2->syntax_id.if_version &&
+ GUID_equal(&if1->syntax_id.uuid, &if2->syntax_id.uuid));
+}
+
+/*
+ find the interface operations on an endpoint
+*/
+static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
+ const struct dcesrv_interface *iface)
+{
+ struct dcesrv_if_list *ifl;
+ for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
+ if (interface_match(&(ifl->iface), iface)) {
+ return &(ifl->iface);
+ }
+ }
+ return NULL;
+}
+
+/*
+ see if a uuid and if_version match to an interface
+*/
+static bool interface_match_by_uuid(const struct dcesrv_interface *iface,
+ const struct GUID *uuid, uint32_t if_version)
+{
+ return (iface->syntax_id.if_version == if_version &&
+ GUID_equal(&iface->syntax_id.uuid, uuid));
+}
+
+/*
+ find the interface operations on an endpoint by uuid
+*/
+static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
+ const struct GUID *uuid, uint32_t if_version)
+{
+ struct dcesrv_if_list *ifl;
+ for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
+ if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
+ return &(ifl->iface);
+ }
+ }
+ return NULL;
+}
+
+/*
+ find the earlier parts of a fragmented call awaiting reassembily
+*/
+static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
+{
+ struct dcesrv_call_state *c;
+ for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
+ if (c->pkt.call_id == call_id) {
+ return c;
+ }
+ }
+ return NULL;
+}
+
+/*
+ register an interface on an endpoint
+*/
+_PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
+ const char *ep_name,
+ const struct dcesrv_interface *iface,
+ const struct security_descriptor *sd)
+{
+ struct dcesrv_endpoint *ep;
+ struct dcesrv_if_list *ifl;
+ struct dcerpc_binding *binding;
+ bool add_ep = false;
+ NTSTATUS status;
+
+ status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
+ return status;
+ }
+
+ /* check if this endpoint exists
+ */
+ if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
+ ep = talloc(dce_ctx, struct dcesrv_endpoint);
+ if (!ep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(ep);
+ ep->ep_description = talloc_reference(ep, binding);
+ add_ep = true;
+
+ /* add mgmt interface */
+ ifl = talloc(dce_ctx, struct dcesrv_if_list);
+ if (!ifl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(ifl->iface), &dcesrv_mgmt_interface,
+ sizeof(struct dcesrv_interface));
+
+ DLIST_ADD(ep->interface_list, ifl);
+ }
+
+ /* see if the interface is already registered on te endpoint */
+ if (find_interface(ep, iface)!=NULL) {
+ DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
+ iface->name, ep_name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /* talloc a new interface list element */
+ ifl = talloc(dce_ctx, struct dcesrv_if_list);
+ if (!ifl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the given interface struct to the one on the endpoints interface list */
+ memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
+
+ /* if we have a security descriptor given,
+ * we should see if we can set it up on the endpoint
+ */
+ if (sd != NULL) {
+ /* if there's currently no security descriptor given on the endpoint
+ * we try to set it
+ */
+ if (ep->sd == NULL) {
+ ep->sd = security_descriptor_copy(dce_ctx, sd);
+ }
+
+ /* if now there's no security descriptor given on the endpoint
+ * something goes wrong, either we failed to copy the security descriptor
+ * or there was already one on the endpoint
+ */
+ if (ep->sd != NULL) {
+ DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
+ " on endpoint '%s'\n",
+ iface->name, ep_name));
+ if (add_ep) free(ep);
+ free(ifl);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ /* finally add the interface on the endpoint */
+ DLIST_ADD(ep->interface_list, ifl);
+
+ /* if it's a new endpoint add it to the dcesrv_context */
+ if (add_ep) {
+ DLIST_ADD(dce_ctx->endpoint_list, ep);
+ }
+
+ DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
+ iface->name, ep_name));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
+ DATA_BLOB *session_key)
+{
+ if (p->auth_state.session_info->session_key.length) {
+ *session_key = p->auth_state.session_info->session_key;
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_NO_USER_SESSION_KEY;
+}
+
+NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
+ DATA_BLOB *session_key)
+{
+ /* this took quite a few CPU cycles to find ... */
+ session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
+ session_key->length = 16;
+ return NT_STATUS_OK;
+}
+
+/*
+ fetch the user session key - may be default (above) or the SMB session key
+
+ The key is always truncated to 16 bytes
+*/
+_PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS status = p->auth_state.session_key(p, session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ session_key->length = MIN(session_key->length, 16);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ destroy a link to an endpoint
+*/
+static int dcesrv_endpoint_destructor(struct dcesrv_connection *p)
+{
+ while (p->contexts) {
+ struct dcesrv_connection_context *c = p->contexts;
+
+ DLIST_REMOVE(p->contexts, c);
+
+ if (c->iface) {
+ c->iface->unbind(c, c->iface);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ connect to a dcerpc endpoint
+*/
+_PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcesrv_endpoint *ep,
+ struct auth_session_info *session_info,
+ struct event_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **_p)
+{
+ struct dcesrv_connection *p;
+
+ if (!session_info) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ p = talloc(mem_ctx, struct dcesrv_connection);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+
+ if (!talloc_reference(p, session_info)) {
+ talloc_free(p);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p->dce_ctx = dce_ctx;
+ p->endpoint = ep;
+ p->contexts = NULL;
+ p->call_list = NULL;
+ p->incoming_fragmented_call_list = NULL;
+ p->pending_call_list = NULL;
+ p->cli_max_recv_frag = 0;
+ p->partial_input = data_blob(NULL, 0);
+ p->auth_state.auth_info = NULL;
+ p->auth_state.gensec_security = NULL;
+ p->auth_state.session_info = session_info;
+ p->auth_state.session_key = dcesrv_generic_session_key;
+ p->event_ctx = event_ctx;
+ p->msg_ctx = msg_ctx;
+ p->server_id = server_id;
+ p->processing = false;
+ p->state_flags = state_flags;
+ ZERO_STRUCT(p->transport);
+
+ talloc_set_destructor(p, dcesrv_endpoint_destructor);
+
+ *_p = p;
+ return NT_STATUS_OK;
+}
+
+/*
+ search and connect to a dcerpc endpoint
+*/
+_PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcerpc_binding *ep_description,
+ struct auth_session_info *session_info,
+ struct event_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **dce_conn_p)
+{
+ NTSTATUS status;
+ const struct dcesrv_endpoint *ep;
+
+ /* make sure this endpoint exists */
+ ep = find_endpoint(dce_ctx, ep_description);
+ if (!ep) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
+ event_ctx, msg_ctx, server_id,
+ state_flags, dce_conn_p);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
+
+ /* TODO: check security descriptor of the endpoint here
+ * if it's a smb named pipe
+ * if it's failed free dce_conn_p
+ */
+
+ return NT_STATUS_OK;
+}
+
+
+static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
+{
+ pkt->rpc_vers = 5;
+ pkt->rpc_vers_minor = 0;
+ if (bigendian) {
+ pkt->drep[0] = 0;
+ } else {
+ pkt->drep[0] = DCERPC_DREP_LE;
+ }
+ pkt->drep[1] = 0;
+ pkt->drep[2] = 0;
+ pkt->drep[3] = 0;
+}
+
+/*
+ move a call from an existing linked list to the specified list. This
+ prevents bugs where we forget to remove the call from a previous
+ list when moving it.
+ */
+static void dcesrv_call_set_list(struct dcesrv_call_state *call,
+ enum dcesrv_call_list list)
+{
+ switch (call->list) {
+ case DCESRV_LIST_NONE:
+ break;
+ case DCESRV_LIST_CALL_LIST:
+ DLIST_REMOVE(call->conn->call_list, call);
+ break;
+ case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+ DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
+ break;
+ case DCESRV_LIST_PENDING_CALL_LIST:
+ DLIST_REMOVE(call->conn->pending_call_list, call);
+ break;
+ }
+ call->list = list;
+ switch (list) {
+ case DCESRV_LIST_NONE:
+ break;
+ case DCESRV_LIST_CALL_LIST:
+ DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ break;
+ case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+ DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
+ break;
+ case DCESRV_LIST_PENDING_CALL_LIST:
+ DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+ break;
+ }
+}
+
+/*
+ return a dcerpc fault
+*/
+static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
+{
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ uint8_t zeros[4];
+ NTSTATUS status;
+
+ /* setup a bind_ack */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_FAULT;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.fault.alloc_hint = 0;
+ pkt.u.fault.context_id = 0;
+ pkt.u.fault.cancel_count = 0;
+ pkt.u.fault.status = fault_code;
+
+ ZERO_STRUCT(zeros);
+ pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return a dcerpc bind_nak
+*/
+static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
+{
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ NTSTATUS status;
+
+ /* setup a bind_nak */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_BIND_NAK;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.bind_nak.reject_reason = reason;
+ if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
+ pkt.u.bind_nak.versions.v.num_versions = 0;
+ }
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a bind request
+*/
+static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
+{
+ uint32_t if_version, transfer_syntax_version;
+ struct GUID uuid, *transfer_syntax_uuid;
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ NTSTATUS status;
+ uint32_t result=0, reason=0;
+ uint32_t context_id;
+ const struct dcesrv_interface *iface;
+ uint32_t extra_flags = 0;
+
+ /*
+ * Association groups allow policy handles to be shared across
+ * multiple client connections. We don't implement this yet.
+ *
+ * So we just allow 0 if the client wants to create a new
+ * association group.
+ *
+ * And we allow the 0x12345678 value, we give away as
+ * assoc_group_id back to the clients
+ */
+ if (call->pkt.u.bind.assoc_group_id != 0 &&
+ call->pkt.u.bind.assoc_group_id != SAMBA_ACCOC_GROUP) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ if (call->pkt.u.bind.num_contexts < 1 ||
+ call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ context_id = call->pkt.u.bind.ctx_list[0].context_id;
+
+ /* you can't bind twice on one context */
+ if (dcesrv_find_context(call->conn, context_id) != NULL) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
+ uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
+
+ transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
+ transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
+ if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
+ ndr_transfer_syntax.if_version != transfer_syntax_version) {
+ char *uuid_str = GUID_string(call, transfer_syntax_uuid);
+ /* we only do NDR encoded dcerpc */
+ DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
+ talloc_free(uuid_str);
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
+ if (iface == NULL) {
+ char *uuid_str = GUID_string(call, &uuid);
+ DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
+ talloc_free(uuid_str);
+
+ /* we don't know about that interface */
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+
+ if (iface) {
+ /* add this context to the list of available context_ids */
+ struct dcesrv_connection_context *context = talloc(call->conn,
+ struct dcesrv_connection_context);
+ if (context == NULL) {
+ return dcesrv_bind_nak(call, 0);
+ }
+ context->conn = call->conn;
+ context->iface = iface;
+ context->context_id = context_id;
+ context->private = NULL;
+ context->handles = NULL;
+ DLIST_ADD(call->conn->contexts, context);
+ call->context = context;
+ }
+
+ if (call->conn->cli_max_recv_frag == 0) {
+ call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
+ }
+
+ if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
+ lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
+ call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
+ extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ /* handle any authentication that is being requested */
+ if (!dcesrv_auth_bind(call)) {
+ return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
+ }
+
+ /* setup a bind_ack */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_BIND_ACK;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
+ pkt.u.bind_ack.max_xmit_frag = 0x2000;
+ pkt.u.bind_ack.max_recv_frag = 0x2000;
+ /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
+ pkt.u.bind_ack.assoc_group_id = SAMBA_ACCOC_GROUP;
+ if (iface) {
+ /* FIXME: Use pipe name as specified by endpoint instead of interface name */
+ pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
+ } else {
+ pkt.u.bind_ack.secondary_address = "";
+ }
+ pkt.u.bind_ack.num_results = 1;
+ pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
+ if (!pkt.u.bind_ack.ctx_list) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pkt.u.bind_ack.ctx_list[0].result = result;
+ pkt.u.bind_ack.ctx_list[0].reason = reason;
+ pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
+ pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
+
+ status = dcesrv_auth_bind_ack(call, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ if (iface) {
+ status = iface->bind(call, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *uuid_str = GUID_string(call, &uuid);
+ DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
+ uuid_str, if_version, nt_errstr(status)));
+ talloc_free(uuid_str);
+ return dcesrv_bind_nak(call, 0);
+ }
+ }
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a auth3 request
+*/
+static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
+{
+ /* handle the auth3 in the auth code */
+ if (!dcesrv_auth_auth3(call)) {
+ return dcesrv_fault(call, DCERPC_FAULT_OTHER);
+ }
+
+ talloc_free(call);
+
+ /* we don't send a reply to a auth3 request, except by a
+ fault */
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a bind request
+*/
+static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
+{
+ uint32_t if_version, transfer_syntax_version;
+ struct dcesrv_connection_context *context;
+ const struct dcesrv_interface *iface;
+ struct GUID uuid, *transfer_syntax_uuid;
+ NTSTATUS status;
+
+ if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
+ uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
+
+ transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
+ transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
+ if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
+ ndr_transfer_syntax.if_version != transfer_syntax_version) {
+ /* we only do NDR encoded dcerpc */
+ return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
+ }
+
+ iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
+ if (iface == NULL) {
+ char *uuid_str = GUID_string(call, &uuid);
+ DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
+ talloc_free(uuid_str);
+ return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
+ }
+
+ /* add this context to the list of available context_ids */
+ context = talloc(call->conn, struct dcesrv_connection_context);
+ if (context == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ context->conn = call->conn;
+ context->iface = iface;
+ context->context_id = context_id;
+ context->private = NULL;
+ context->handles = NULL;
+ DLIST_ADD(call->conn->contexts, context);
+ call->context = context;
+
+ if (iface) {
+ status = iface->bind(call, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a alter context request
+*/
+static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
+{
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ NTSTATUS status;
+ uint32_t result=0, reason=0;
+ uint32_t context_id;
+
+ /* handle any authentication that is being requested */
+ if (!dcesrv_auth_alter(call)) {
+ /* TODO: work out the right reject code */
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+
+ context_id = call->pkt.u.alter.ctx_list[0].context_id;
+
+ /* see if they are asking for a new interface */
+ if (result == 0 &&
+ dcesrv_find_context(call->conn, context_id) == NULL) {
+ status = dcesrv_alter_new_context(call, context_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+ }
+
+ /* setup a alter_resp */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_ALTER_RESP;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.alter_resp.max_xmit_frag = 0x2000;
+ pkt.u.alter_resp.max_recv_frag = 0x2000;
+ pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
+ pkt.u.alter_resp.num_results = 1;
+ pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
+ if (!pkt.u.alter_resp.ctx_list) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pkt.u.alter_resp.ctx_list[0].result = result;
+ pkt.u.alter_resp.ctx_list[0].reason = reason;
+ pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
+ pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
+ pkt.u.alter_resp.secondary_address = "";
+
+ status = dcesrv_auth_alter_ack(call, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
+ || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+ || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+ }
+ return dcesrv_fault(call, 0);
+ }
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ handle a dcerpc request packet
+*/
+static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
+{
+ struct ndr_pull *pull;
+ NTSTATUS status;
+ struct dcesrv_connection_context *context;
+
+ /* if authenticated, and the mech we use can't do async replies, don't use them... */
+ if (call->conn->auth_state.gensec_security &&
+ !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
+ call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
+ }
+
+ context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
+ if (context == NULL) {
+ return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
+ }
+
+ pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
+ lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(pull);
+
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ call->context = context;
+ call->ndr_pull = pull;
+
+ if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+ }
+
+ if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
+ pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ /* unravel the NDR for the packet */
+ status = context->iface->ndr_pull(call, call, pull, &call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ if (pull->offset != pull->data_size) {
+ DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
+ pull->data_size - pull->offset));
+ dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
+ }
+
+ /* call the dispatch function */
+ status = context->iface->dispatch(call, call, call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
+ context->iface->name,
+ call->pkt.u.request.opnum,
+ dcerpc_errstr(pull, call->fault_code)));
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ /* add the call to the pending list */
+ dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
+
+ if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return NT_STATUS_OK;
+ }
+
+ return dcesrv_reply(call);
+}
+
+_PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
+{
+ struct ndr_push *push;
+ NTSTATUS status;
+ DATA_BLOB stub;
+ uint32_t total_length, chunk_size;
+ struct dcesrv_connection_context *context = call->context;
+ size_t sig_size = 0;
+
+ /* call the reply function */
+ status = context->iface->reply(call, call, call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ /* form the reply NDR */
+ push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(push);
+
+ /* carry over the pointer count to the reply in case we are
+ using full pointer. See NDR specification for full
+ pointers */
+ push->ptr_count = call->ndr_pull->ptr_count;
+
+ if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
+ push->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ status = context->iface->ndr_push(call, call, push, call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ stub = ndr_push_blob(push);
+
+ total_length = stub.length;
+
+ /* we can write a full max_recv_frag size, minus the dcerpc
+ request header size */
+ chunk_size = call->conn->cli_max_recv_frag;
+ chunk_size -= DCERPC_REQUEST_LENGTH;
+ if (call->conn->auth_state.auth_info &&
+ call->conn->auth_state.gensec_security) {
+ sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
+ call->conn->cli_max_recv_frag);
+ if (sig_size) {
+ chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
+ chunk_size -= sig_size;
+ }
+ }
+ chunk_size -= (chunk_size % 16);
+
+ do {
+ uint32_t length;
+ struct data_blob_list_item *rep;
+ struct ncacn_packet pkt;
+
+ rep = talloc(call, struct data_blob_list_item);
+ NT_STATUS_HAVE_NO_MEMORY(rep);
+
+ length = MIN(chunk_size, stub.length);
+
+ /* form the dcerpc response packet */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_RESPONSE;
+ pkt.pfc_flags = 0;
+ if (stub.length == total_length) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
+ }
+ if (length == stub.length) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
+ }
+ pkt.u.response.alloc_hint = stub.length;
+ pkt.u.response.context_id = call->pkt.u.request.context_id;
+ pkt.u.response.cancel_count = 0;
+ pkt.u.response.stub_and_verifier.data = stub.data;
+ pkt.u.response.stub_and_verifier.length = length;
+
+ if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
+ return dcesrv_fault(call, DCERPC_FAULT_OTHER);
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+
+ stub.data += length;
+ stub.length -= length;
+ } while (stub.length != 0);
+
+ /* move the call from the pending to the finished calls list */
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ if (call->conn->call_list && call->conn->call_list->replies) {
+ if (call->conn->transport.report_output_data) {
+ call->conn->transport.report_output_data(call->conn);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+{
+ if (!conn->transport.get_my_addr) {
+ return NULL;
+ }
+
+ return conn->transport.get_my_addr(conn, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+{
+ if (!conn->transport.get_peer_addr) {
+ return NULL;
+ }
+
+ return conn->transport.get_peer_addr(conn, mem_ctx);
+}
+
+/*
+ work out if we have a full packet yet
+*/
+static bool dce_full_packet(const DATA_BLOB *data)
+{
+ if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
+ return false;
+ }
+ if (dcerpc_get_frag_length(data) > data->length) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ we might have consumed only part of our input - advance past that part
+*/
+static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
+{
+ DATA_BLOB blob;
+
+ if (dce_conn->partial_input.length == offset) {
+ data_blob_free(&dce_conn->partial_input);
+ return;
+ }
+
+ blob = dce_conn->partial_input;
+ dce_conn->partial_input = data_blob(blob.data + offset,
+ blob.length - offset);
+ data_blob_free(&blob);
+}
+
+/*
+ remove the call from the right list when freed
+ */
+static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
+{
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+ return 0;
+}
+
+/*
+ process some input to a dcerpc endpoint server.
+*/
+NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
+{
+ struct ndr_pull *ndr;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ struct dcesrv_call_state *call;
+ DATA_BLOB blob;
+
+ call = talloc_zero(dce_conn, struct dcesrv_call_state);
+ if (!call) {
+ talloc_free(dce_conn->partial_input.data);
+ return NT_STATUS_NO_MEMORY;
+ }
+ call->conn = dce_conn;
+ call->event_ctx = dce_conn->event_ctx;
+ call->msg_ctx = dce_conn->msg_ctx;
+ call->state_flags = call->conn->state_flags;
+ call->time = timeval_current();
+ call->list = DCESRV_LIST_NONE;
+
+ talloc_set_destructor(call, dcesrv_call_dequeue);
+
+ blob = dce_conn->partial_input;
+ blob.length = dcerpc_get_frag_length(&blob);
+
+ ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ if (!ndr) {
+ talloc_free(dce_conn->partial_input.data);
+ talloc_free(call);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(dce_conn->partial_input.data);
+ talloc_free(call);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ /* we have to check the signing here, before combining the
+ pdus */
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ !dcesrv_auth_request(call, &blob)) {
+ dce_partial_advance(dce_conn, blob.length);
+ return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ dce_partial_advance(dce_conn, blob.length);
+
+ /* see if this is a continued packet */
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
+ struct dcesrv_call_state *call2 = call;
+ uint32_t alloc_size;
+
+ /* we only allow fragmented requests, no other packet types */
+ if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+
+ /* this is a continuation of an existing call - find the call then
+ tack it on the end */
+ call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
+ if (!call) {
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+
+ if (call->pkt.ptype != call2->pkt.ptype) {
+ /* trying to play silly buggers are we? */
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+
+ alloc_size = call->pkt.u.request.stub_and_verifier.length +
+ call2->pkt.u.request.stub_and_verifier.length;
+ if (call->pkt.u.request.alloc_hint > alloc_size) {
+ alloc_size = call->pkt.u.request.alloc_hint;
+ }
+
+ call->pkt.u.request.stub_and_verifier.data =
+ talloc_realloc(call,
+ call->pkt.u.request.stub_and_verifier.data,
+ uint8_t, alloc_size);
+ if (!call->pkt.u.request.stub_and_verifier.data) {
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+ memcpy(call->pkt.u.request.stub_and_verifier.data +
+ call->pkt.u.request.stub_and_verifier.length,
+ call2->pkt.u.request.stub_and_verifier.data,
+ call2->pkt.u.request.stub_and_verifier.length);
+ call->pkt.u.request.stub_and_verifier.length +=
+ call2->pkt.u.request.stub_and_verifier.length;
+
+ call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
+
+ talloc_free(call2);
+ }
+
+ /* this may not be the last pdu in the chain - if its isn't then
+ just put it on the incoming_fragmented_call_list and wait for the rest */
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+ dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
+ return NT_STATUS_OK;
+ }
+
+ /* This removes any fragments we may have had stashed away */
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+
+ switch (call->pkt.ptype) {
+ case DCERPC_PKT_BIND:
+ status = dcesrv_bind(call);
+ break;
+ case DCERPC_PKT_AUTH3:
+ status = dcesrv_auth3(call);
+ break;
+ case DCERPC_PKT_ALTER:
+ status = dcesrv_alter(call);
+ break;
+ case DCERPC_PKT_REQUEST:
+ status = dcesrv_request(call);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ /* if we are going to be sending a reply then add
+ it to the list of pending calls. We add it to the end to keep the call
+ list in the order we will answer */
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(call);
+ }
+
+ return status;
+}
+
+
+/*
+ provide some input to a dcerpc endpoint server. This passes data
+ from a dcerpc client into the server
+*/
+_PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
+{
+ NTSTATUS status;
+
+ dce_conn->partial_input.data = talloc_realloc(dce_conn,
+ dce_conn->partial_input.data,
+ uint8_t,
+ dce_conn->partial_input.length + data->length);
+ if (!dce_conn->partial_input.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
+ data->data, data->length);
+ dce_conn->partial_input.length += data->length;
+
+ while (dce_full_packet(&dce_conn->partial_input)) {
+ status = dcesrv_input_process(dce_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ retrieve some output from a dcerpc server
+ The caller supplies a function that will be called to do the
+ actual output.
+
+ The first argument to write_fn() will be 'private', the second will
+ be a pointer to a buffer containing the data to be sent and the 3rd
+ will be a pointer to a size_t variable that will be set to the
+ number of bytes that are consumed from the output.
+
+ from the current fragment
+*/
+_PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
+ void *private_data,
+ NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
+{
+ NTSTATUS status;
+ struct dcesrv_call_state *call;
+ struct data_blob_list_item *rep;
+ size_t nwritten;
+
+ call = dce_conn->call_list;
+ if (!call || !call->replies) {
+ if (dce_conn->pending_call_list) {
+ /* TODO: we need to say act async here
+ * as we know we have pending requests
+ * which will be finished at a time
+ */
+ return NT_STATUS_FOOBAR;
+ }
+ return NT_STATUS_FOOBAR;
+ }
+ rep = call->replies;
+
+ status = write_fn(private_data, &rep->blob, &nwritten);
+ NT_STATUS_IS_ERR_RETURN(status);
+
+ rep->blob.length -= nwritten;
+ rep->blob.data += nwritten;
+
+ if (rep->blob.length == 0) {
+ /* we're done with this section of the call */
+ DLIST_REMOVE(call->replies, rep);
+ }
+
+ if (call->replies == NULL) {
+ /* we're done with the whole call */
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+ talloc_free(call);
+ }
+
+ return status;
+}
+
+_PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
+{
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ int i;
+
+ if (!endpoint_servers) {
+ DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ dce_ctx = talloc(mem_ctx, struct dcesrv_context);
+ NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
+ dce_ctx->endpoint_list = NULL;
+ dce_ctx->lp_ctx = lp_ctx;
+
+ for (i=0;endpoint_servers[i];i++) {
+ const struct dcesrv_endpoint_server *ep_server;
+
+ ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
+ if (!ep_server) {
+ DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = ep_server->init_server(dce_ctx, ep_server);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ *_dce_ctx = dce_ctx;
+ return NT_STATUS_OK;
+}
+
+/* the list of currently registered DCERPC endpoint servers.
+ */
+static struct ep_server {
+ struct dcesrv_endpoint_server *ep_server;
+} *ep_servers = NULL;
+static int num_ep_servers;
+
+/*
+ register a DCERPC endpoint server.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+
+ The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+_PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
+{
+ const struct dcesrv_endpoint_server *ep_server = _ep_server;
+
+ if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
+ ep_server->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
+ if (!ep_servers) {
+ smb_panic("out of memory in dcerpc_register");
+ }
+
+ ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
+ ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
+
+ num_ep_servers++;
+
+ DEBUG(3,("DCERPC endpoint server '%s' registered\n",
+ ep_server->name));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_ep_servers;i++) {
+ if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
+ return ep_servers[i].ep_server;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ return the DCERPC module version, and the size of some critical types
+ This can be used by endpoint server modules to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct dcesrv_critical_sizes *dcerpc_module_version(void)
+{
+ static const struct dcesrv_critical_sizes critical_sizes = {
+ DCERPC_MODULE_VERSION,
+ sizeof(struct dcesrv_context),
+ sizeof(struct dcesrv_endpoint),
+ sizeof(struct dcesrv_endpoint_server),
+ sizeof(struct dcesrv_interface),
+ sizeof(struct dcesrv_if_list),
+ sizeof(struct dcesrv_connection),
+ sizeof(struct dcesrv_call_state),
+ sizeof(struct dcesrv_auth),
+ sizeof(struct dcesrv_handle)
+ };
+
+ return &critical_sizes;
+}
+
+/*
+ initialise the dcerpc server context for ncacn_np based services
+*/
+_PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ struct dcesrv_context **_dce_ctx)
+{
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+
+ status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *_dce_ctx = dce_ctx;
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
new file mode 100644
index 0000000000..b5672b41ac
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.h
@@ -0,0 +1,372 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc defines
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SAMBA_DCERPC_SERVER_H
+#define SAMBA_DCERPC_SERVER_H
+
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/ndr/libndr.h"
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct dcesrv_critical_sizes.
+ */
+/* version 1 - initial version - metze */
+#define DCERPC_MODULE_VERSION 1
+
+struct dcesrv_connection;
+struct dcesrv_call_state;
+struct dcesrv_auth;
+struct dcesrv_connection_context;
+
+struct dcesrv_interface {
+ const char *name;
+ struct ndr_syntax_id syntax_id;
+
+ /* this function is called when the client binds to this interface */
+ NTSTATUS (*bind)(struct dcesrv_call_state *, const struct dcesrv_interface *);
+
+ /* this function is called when the client disconnects the endpoint */
+ void (*unbind)(struct dcesrv_connection_context *, const struct dcesrv_interface *);
+
+ /* the ndr_pull function for the chosen interface.
+ */
+ NTSTATUS (*ndr_pull)(struct dcesrv_call_state *, TALLOC_CTX *, struct ndr_pull *, void **);
+
+ /* the dispatch function for the chosen interface.
+ */
+ NTSTATUS (*dispatch)(struct dcesrv_call_state *, TALLOC_CTX *, void *);
+
+ /* the reply function for the chosen interface.
+ */
+ NTSTATUS (*reply)(struct dcesrv_call_state *, TALLOC_CTX *, void *);
+
+ /* the ndr_push function for the chosen interface.
+ */
+ NTSTATUS (*ndr_push)(struct dcesrv_call_state *, TALLOC_CTX *, struct ndr_push *, const void *);
+
+ /* for any private use by the interface code */
+ const void *private;
+};
+
+enum dcesrv_call_list {
+ DCESRV_LIST_NONE,
+ DCESRV_LIST_CALL_LIST,
+ DCESRV_LIST_FRAGMENTED_CALL_LIST,
+ DCESRV_LIST_PENDING_CALL_LIST
+};
+
+/* the state of an ongoing dcerpc call */
+struct dcesrv_call_state {
+ struct dcesrv_call_state *next, *prev;
+ struct dcesrv_connection *conn;
+ struct dcesrv_connection_context *context;
+ struct ncacn_packet pkt;
+
+ /*
+ which list this request is in, if any
+ */
+ enum dcesrv_call_list list;
+
+ /* the backend can mark the call
+ * with DCESRV_CALL_STATE_FLAG_ASYNC
+ * that will cause the frontend to not touch r->out
+ * and skip the reply
+ *
+ * this is only allowed to the backend when DCESRV_CALL_STATE_FLAG_MAY_ASYNC
+ * is alerady set by the frontend
+ *
+ * the backend then needs to call dcesrv_reply() when it's
+ * ready to send the reply
+ */
+#define DCESRV_CALL_STATE_FLAG_ASYNC (1<<0)
+#define DCESRV_CALL_STATE_FLAG_MAY_ASYNC (1<<1)
+#define DCESRV_CALL_STATE_FLAG_HEADER_SIGNING (1<<2)
+ uint32_t state_flags;
+
+ /* the time the request arrived in the server */
+ struct timeval time;
+
+ /* the backend can use this event context for async replies */
+ struct event_context *event_ctx;
+
+ /* the message_context that will be used for async replies */
+ struct messaging_context *msg_ctx;
+
+ /* this is the pointer to the allocated function struct */
+ void *r;
+
+ /*
+ * that's the ndr pull context used in dcesrv_request()
+ * needed by dcesrv_reply() to carry over information
+ * for full pointer support.
+ */
+ struct ndr_pull *ndr_pull;
+
+ DATA_BLOB input;
+
+ struct data_blob_list_item *replies;
+
+ /* this is used by the boilerplate code to generate DCERPC faults */
+ uint32_t fault_code;
+};
+
+#define DCESRV_HANDLE_ANY 255
+
+/* a dcerpc handle in internal format */
+struct dcesrv_handle {
+ struct dcesrv_handle *next, *prev;
+ struct dcesrv_connection_context *context;
+ struct policy_handle wire_handle;
+ void *data;
+};
+
+/* hold the authentication state information */
+struct dcesrv_auth {
+ struct dcerpc_auth *auth_info;
+ struct gensec_security *gensec_security;
+ struct auth_session_info *session_info;
+ NTSTATUS (*session_key)(struct dcesrv_connection *, DATA_BLOB *session_key);
+};
+
+struct dcesrv_connection_context {
+ struct dcesrv_connection_context *next, *prev;
+ uint32_t context_id;
+
+ /* the connection this is on */
+ struct dcesrv_connection *conn;
+
+ /* the ndr function table for the chosen interface */
+ const struct dcesrv_interface *iface;
+
+ /* private data for the interface implementation */
+ void *private;
+
+ /* current rpc handles - this is really the wrong scope for
+ them, but it will do for now */
+ struct dcesrv_handle *handles;
+};
+
+
+/* the state associated with a dcerpc server connection */
+struct dcesrv_connection {
+ /* the top level context for this server */
+ struct dcesrv_context *dce_ctx;
+
+ /* the endpoint that was opened */
+ const struct dcesrv_endpoint *endpoint;
+
+ /* a list of established context_ids */
+ struct dcesrv_connection_context *contexts;
+
+ /* the state of the current incoming call fragments */
+ struct dcesrv_call_state *incoming_fragmented_call_list;
+
+ /* the state of the async pending calls */
+ struct dcesrv_call_state *pending_call_list;
+
+ /* the state of the current outgoing calls */
+ struct dcesrv_call_state *call_list;
+
+ /* the maximum size the client wants to receive */
+ uint32_t cli_max_recv_frag;
+
+ DATA_BLOB partial_input;
+
+ /* the current authentication state */
+ struct dcesrv_auth auth_state;
+
+ /* the event_context that will be used for this connection */
+ struct event_context *event_ctx;
+
+ /* the message_context that will be used for this connection */
+ struct messaging_context *msg_ctx;
+
+ /* the server_id that will be used for this connection */
+ struct server_id server_id;
+
+ /* the transport level session key */
+ DATA_BLOB transport_session_key;
+
+ bool processing;
+
+ /* this is the default state_flags for dcesrv_call_state structs */
+ uint32_t state_flags;
+
+ struct {
+ void *private_data;
+ void (*report_output_data)(struct dcesrv_connection *);
+ struct socket_address *(*get_my_addr)(struct dcesrv_connection *, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*get_peer_addr)(struct dcesrv_connection *, TALLOC_CTX *mem_ctx);
+ } transport;
+};
+
+
+struct dcesrv_endpoint_server {
+ /* this is the name of the endpoint server */
+ const char *name;
+
+ /* this function should register endpoints and some other setup stuff,
+ * it is called when the dcesrv_context gets initialized.
+ */
+ NTSTATUS (*init_server)(struct dcesrv_context *, const struct dcesrv_endpoint_server *);
+
+ /* this function can be used by other endpoint servers to
+ * ask for a dcesrv_interface implementation
+ * - iface must be reference to an already existing struct !
+ */
+ bool (*interface_by_uuid)(struct dcesrv_interface *iface, const struct GUID *, uint32_t);
+
+ /* this function can be used by other endpoint servers to
+ * ask for a dcesrv_interface implementation
+ * - iface must be reference to an already existeng struct !
+ */
+ bool (*interface_by_name)(struct dcesrv_interface *iface, const char *);
+};
+
+
+/* server-wide context information for the dcerpc server */
+struct dcesrv_context {
+ /* the list of endpoints that have registered
+ * by the configured endpoint servers
+ */
+ struct dcesrv_endpoint {
+ struct dcesrv_endpoint *next, *prev;
+ /* the type and location of the endpoint */
+ struct dcerpc_binding *ep_description;
+ /* the security descriptor for smb named pipes */
+ struct security_descriptor *sd;
+ /* the list of interfaces available on this endpoint */
+ struct dcesrv_if_list {
+ struct dcesrv_if_list *next, *prev;
+ struct dcesrv_interface iface;
+ } *interface_list;
+ } *endpoint_list;
+
+ /* loadparm context to use for this connection */
+ struct loadparm_context *lp_ctx;
+};
+
+/* this structure is used by modules to determine the size of some critical types */
+struct dcesrv_critical_sizes {
+ int interface_version;
+ int sizeof_dcesrv_context;
+ int sizeof_dcesrv_endpoint;
+ int sizeof_dcesrv_endpoint_server;
+ int sizeof_dcesrv_interface;
+ int sizeof_dcesrv_if_list;
+ int sizeof_dcesrv_connection;
+ int sizeof_dcesrv_call_state;
+ int sizeof_dcesrv_auth;
+ int sizeof_dcesrv_handle;
+};
+
+struct model_ops;
+
+NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
+ const char *ep_name,
+ const struct dcesrv_interface *iface,
+ const struct security_descriptor *sd);
+NTSTATUS dcerpc_register_ep_server(const void *_ep_server);
+NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char **endpoint_servers, struct dcesrv_context **_dce_ctx);
+NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ struct dcesrv_context **_dce_ctx);
+NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcerpc_binding *ep_description,
+ struct auth_session_info *session_info,
+ struct event_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **dce_conn_p);
+NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
+ void *private_data,
+ NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten));
+NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data);
+NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcesrv_endpoint *ep,
+ struct auth_session_info *session_info,
+ struct event_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **_p);
+
+NTSTATUS dcesrv_reply(struct dcesrv_call_state *call);
+struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_connection_context *context,
+ uint8_t handle_type);
+
+struct dcesrv_handle *dcesrv_handle_fetch(
+ struct dcesrv_connection_context *context,
+ struct policy_handle *p,
+ uint8_t handle_type);
+struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx);
+
+struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx);
+
+NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p, DATA_BLOB *session_key);
+
+/* a useful macro for generating a RPC fault in the backend code */
+#define DCESRV_FAULT(code) do { \
+ dce_call->fault_code = code; \
+ return r->out.result; \
+} while(0)
+
+/* a useful macro for generating a RPC fault in the backend code */
+#define DCESRV_FAULT_VOID(code) do { \
+ dce_call->fault_code = code; \
+ return; \
+} while(0)
+
+/* a useful macro for checking the validity of a dcerpc policy handle
+ and giving the right fault code if invalid */
+#define DCESRV_CHECK_HANDLE(h) do {if (!(h)) DCESRV_FAULT(DCERPC_FAULT_CONTEXT_MISMATCH); } while (0)
+
+/* this checks for a valid policy handle, and gives a fault if an
+ invalid handle or retval if the handle is of the
+ wrong type */
+#define DCESRV_PULL_HANDLE_RETVAL(h, inhandle, t, retval) do { \
+ (h) = dcesrv_handle_fetch(dce_call->context, (inhandle), DCESRV_HANDLE_ANY); \
+ DCESRV_CHECK_HANDLE(h); \
+ if ((t) != DCESRV_HANDLE_ANY && (h)->wire_handle.handle_type != (t)) { \
+ return retval; \
+ } \
+} while (0)
+
+/* this checks for a valid policy handle and gives a dcerpc fault
+ if its the wrong type of handle */
+#define DCESRV_PULL_HANDLE_FAULT(h, inhandle, t) do { \
+ (h) = dcesrv_handle_fetch(dce_call->context, (inhandle), t); \
+ DCESRV_CHECK_HANDLE(h); \
+} while (0)
+
+#define DCESRV_PULL_HANDLE(h, inhandle, t) DCESRV_PULL_HANDLE_RETVAL(h, inhandle, t, NT_STATUS_INVALID_HANDLE)
+#define DCESRV_PULL_HANDLE_WERR(h, inhandle, t) DCESRV_PULL_HANDLE_RETVAL(h, inhandle, t, WERR_BADFID)
+
+
+
+#endif /* SAMBA_DCERPC_SERVER_H */
diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c
new file mode 100644
index 0000000000..16bf4eb7ed
--- /dev/null
+++ b/source4/rpc_server/dcesrv_auth.c
@@ -0,0 +1,529 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc authentication code
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+/*
+ parse any auth information from a dcerpc bind request
+ return false if we can't handle the auth request for some
+ reason (in which case we send a bind_nak)
+*/
+bool dcesrv_auth_bind(struct dcesrv_call_state *call)
+{
+ struct cli_credentials *server_credentials;
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ struct dcesrv_auth *auth = &dce_conn->auth_state;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (pkt->u.bind.auth_info.length == 0) {
+ dce_conn->auth_state.auth_info = NULL;
+ return true;
+ }
+
+ dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
+ if (!dce_conn->auth_state.auth_info) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
+ call, NULL,
+ dce_conn->auth_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ status = gensec_server_start(dce_conn, call->event_ctx, call->conn->dce_ctx->lp_ctx, call->msg_ctx, &auth->gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC for DCERPC server: %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ server_credentials
+ = cli_credentials_init(call);
+ if (!server_credentials) {
+ DEBUG(1, ("Failed to init server credentials\n"));
+ return false;
+ }
+
+ cli_credentials_set_conf(server_credentials, call->conn->dce_ctx->lp_ctx);
+ status = cli_credentials_set_machine_account(server_credentials, call->conn->dce_ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
+ talloc_free(server_credentials);
+ server_credentials = NULL;
+ }
+
+ gensec_set_credentials(auth->gensec_security, server_credentials);
+
+ status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type,
+ auth->auth_info->auth_level);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n",
+ (int)auth->auth_info->auth_type,
+ (int)auth->auth_info->auth_level,
+ nt_errstr(status)));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ add any auth information needed in a bind ack, and process the authentication
+ information found in the bind.
+*/
+NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+
+ if (!call->conn->auth_state.gensec_security) {
+ return NT_STATUS_OK;
+ }
+
+ status = gensec_update(dce_conn->auth_state.gensec_security,
+ call,
+ dce_conn->auth_state.auth_info->credentials,
+ &dce_conn->auth_state.auth_info->credentials);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = gensec_session_info(dce_conn->auth_state.gensec_security,
+ &dce_conn->auth_state.session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ if (dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
+ gensec_want_feature(dce_conn->auth_state.gensec_security,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
+
+ /* Now that we are authenticated, go back to the generic session key... */
+ dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+ return NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ dce_conn->auth_state.auth_info->auth_pad_length = 0;
+ dce_conn->auth_state.auth_info->auth_reserved = 0;
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status)));
+ return status;
+ }
+}
+
+
+/*
+ process the final stage of a auth request
+*/
+bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
+{
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ /* We can't work without an existing gensec state, and an new blob to feed it */
+ if (!dce_conn->auth_state.auth_info ||
+ !dce_conn->auth_state.gensec_security ||
+ pkt->u.auth3.auth_info.length == 0) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&pkt->u.auth3.auth_info,
+ call, NULL,
+ dce_conn->auth_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ /* Pass the extra data we got from the client down to gensec for processing */
+ status = gensec_update(dce_conn->auth_state.gensec_security,
+ call,
+ dce_conn->auth_state.auth_info->credentials,
+ &dce_conn->auth_state.auth_info->credentials);
+ if (NT_STATUS_IS_OK(status)) {
+ status = gensec_session_info(dce_conn->auth_state.gensec_security,
+ &dce_conn->auth_state.session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+ return false;
+ }
+ /* Now that we are authenticated, go back to the generic session key... */
+ dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+ return true;
+ } else {
+ DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n",
+ nt_errstr(status)));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ parse any auth information from a dcerpc alter request
+ return false if we can't handle the auth request for some
+ reason (in which case we send a bind_nak (is this true for here?))
+*/
+bool dcesrv_auth_alter(struct dcesrv_call_state *call)
+{
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ enum ndr_err_code ndr_err;
+
+ /* on a pure interface change there is no auth blob */
+ if (pkt->u.alter.auth_info.length == 0) {
+ return true;
+ }
+
+ /* We can't work without an existing gensec state */
+ if (!dce_conn->auth_state.gensec_security) {
+ return false;
+ }
+
+ dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
+ if (!dce_conn->auth_state.auth_info) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&pkt->u.alter.auth_info,
+ call, NULL,
+ dce_conn->auth_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ add any auth information needed in a alter ack, and process the authentication
+ information found in the alter.
+*/
+NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+
+ /* on a pure interface change there is no auth_info structure
+ setup */
+ if (!call->conn->auth_state.auth_info ||
+ dce_conn->auth_state.auth_info->credentials.length == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (!call->conn->auth_state.gensec_security) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = gensec_update(dce_conn->auth_state.gensec_security,
+ call,
+ dce_conn->auth_state.auth_info->credentials,
+ &dce_conn->auth_state.auth_info->credentials);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = gensec_session_info(dce_conn->auth_state.gensec_security,
+ &dce_conn->auth_state.session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ /* Now that we are authenticated, got back to the generic session key... */
+ dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+ return NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ dce_conn->auth_state.auth_info->auth_pad_length = 0;
+ dce_conn->auth_state.auth_info->auth_reserved = 0;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2, ("Failed to finish dcesrv auth alter_ack: %s\n", nt_errstr(status)));
+ return status;
+}
+
+/*
+ check credentials on a request
+*/
+bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
+{
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ DATA_BLOB auth_blob;
+ struct dcerpc_auth auth;
+ struct ndr_pull *ndr;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (!dce_conn->auth_state.auth_info ||
+ !dce_conn->auth_state.gensec_security) {
+ return true;
+ }
+
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ if (pkt->auth_length != 0) {
+ break;
+ }
+ return true;
+ case DCERPC_AUTH_LEVEL_NONE:
+ if (pkt->auth_length != 0) {
+ return false;
+ }
+ return true;
+
+ default:
+ return false;
+ }
+
+ auth_blob.length = 8 + pkt->auth_length;
+
+ /* check for a valid length */
+ if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
+ return false;
+ }
+
+ auth_blob.data =
+ pkt->u.request.stub_and_verifier.data +
+ pkt->u.request.stub_and_verifier.length - auth_blob.length;
+ pkt->u.request.stub_and_verifier.length -= auth_blob.length;
+
+ /* pull the auth structure */
+ ndr = ndr_pull_init_blob(&auth_blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ if (!ndr) {
+ return false;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(ndr);
+ return false;
+ }
+
+ /* check signature or unseal the packet */
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
+ call,
+ full_packet->data + DCERPC_REQUEST_LENGTH,
+ pkt->u.request.stub_and_verifier.length,
+ full_packet->data,
+ full_packet->length-auth.credentials.length,
+ &auth.credentials);
+ memcpy(pkt->u.request.stub_and_verifier.data,
+ full_packet->data + DCERPC_REQUEST_LENGTH,
+ pkt->u.request.stub_and_verifier.length);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_check_packet(dce_conn->auth_state.gensec_security,
+ call,
+ pkt->u.request.stub_and_verifier.data,
+ pkt->u.request.stub_and_verifier.length,
+ full_packet->data,
+ full_packet->length-auth.credentials.length,
+ &auth.credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /* for now we ignore possible signatures here */
+ status = NT_STATUS_OK;
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ /* remove the indicated amount of padding */
+ if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
+ talloc_free(ndr);
+ return false;
+ }
+ pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
+ talloc_free(ndr);
+
+ return NT_STATUS_IS_OK(status);
+}
+
+
+/*
+ push a signed or sealed dcerpc request packet into a blob
+*/
+bool dcesrv_auth_response(struct dcesrv_call_state *call,
+ DATA_BLOB *blob, size_t sig_size,
+ struct ncacn_packet *pkt)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct ndr_push *ndr;
+ uint32_t payload_length;
+ DATA_BLOB creds2;
+
+ /* non-signed packets are simple */
+ if (sig_size == 0) {
+ status = ncacn_push_auth(blob, call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx), pkt, NULL);
+ return NT_STATUS_IS_OK(status);
+ }
+
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /*
+ * TODO: let the gensec mech decide if it wants to generate a signature
+ * that might be needed for schannel...
+ */
+ status = ncacn_push_auth(blob, call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx), pkt, NULL);
+ return NT_STATUS_IS_OK(status);
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ status = ncacn_push_auth(blob, call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx), pkt, NULL);
+ return NT_STATUS_IS_OK(status);
+
+ default:
+ return false;
+ }
+
+ ndr = ndr_push_init_ctx(call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx));
+ if (!ndr) {
+ return false;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ /* pad to 16 byte multiple, match win2k3 */
+ dce_conn->auth_state.auth_info->auth_pad_length =
+ (16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15;
+ ndr_err = ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ payload_length = pkt->u.response.stub_and_verifier.length +
+ dce_conn->auth_state.auth_info->auth_pad_length;
+
+ /* we start without signature, it will appended later */
+ dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0);
+
+ /* add the auth verifier */
+ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
+ dce_conn->auth_state.auth_info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ /* extract the whole packet as a blob */
+ *blob = ndr_push_blob(ndr);
+
+ /*
+ * Setup the frag and auth length in the packet buffer.
+ * This is needed if the GENSEC mech does AEAD signing
+ * of the packet headers. The signature itself will be
+ * appended later.
+ */
+ dcerpc_set_frag_length(blob, blob->length + sig_size);
+ dcerpc_set_auth_length(blob, sig_size);
+
+ /* sign or seal the packet */
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_seal_packet(dce_conn->auth_state.gensec_security,
+ call,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ payload_length,
+ blob->data,
+ blob->length,
+ &creds2);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_sign_packet(dce_conn->auth_state.gensec_security,
+ call,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ payload_length,
+ blob->data,
+ blob->length,
+ &creds2);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (creds2.length != sig_size) {
+ DEBUG(0,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
+ creds2.length, (uint32_t)sig_size,
+ dce_conn->auth_state.auth_info->auth_pad_length,
+ pkt->u.response.stub_and_verifier.length));
+ data_blob_free(&creds2);
+ status = NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (!data_blob_append(call, blob, creds2.data, creds2.length)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ data_blob_free(&creds2);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/rpc_server/dcesrv_mgmt.c b/source4/rpc_server/dcesrv_mgmt.c
new file mode 100644
index 0000000000..3a8f9956c2
--- /dev/null
+++ b/source4/rpc_server/dcesrv_mgmt.c
@@ -0,0 +1,102 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the mgmt pipe
+
+ Copyright (C) Jelmer Vernooij 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_mgmt.h"
+#include "rpc_server/common/common.h"
+
+/*
+ mgmt_inq_if_ids
+*/
+static WERROR dcesrv_mgmt_inq_if_ids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_inq_if_ids *r)
+{
+ const struct dcesrv_endpoint *ep = dce_call->conn->endpoint;
+ struct dcesrv_if_list *l;
+ struct rpc_if_id_vector_t *vector;
+
+ vector = *r->out.if_id_vector = talloc(mem_ctx, struct rpc_if_id_vector_t);
+ vector->count = 0;
+ vector->if_id = NULL;
+ for (l = ep->interface_list; l; l = l->next) {
+ vector->count++;
+ vector->if_id = talloc_realloc(mem_ctx, vector->if_id, struct ndr_syntax_id_p, vector->count);
+ vector->if_id[vector->count-1].id = &l->iface.syntax_id;
+ }
+ return WERR_OK;
+}
+
+
+/*
+ mgmt_inq_stats
+*/
+static WERROR dcesrv_mgmt_inq_stats(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_inq_stats *r)
+{
+ if (r->in.max_count != MGMT_STATS_ARRAY_MAX_SIZE)
+ return WERR_NOT_SUPPORTED;
+
+ r->out.statistics->count = r->in.max_count;
+ r->out.statistics->statistics = talloc_array(mem_ctx, uint32_t, r->in.max_count);
+ /* FIXME */
+ r->out.statistics->statistics[MGMT_STATS_CALLS_IN] = 0;
+ r->out.statistics->statistics[MGMT_STATS_CALLS_OUT] = 0;
+ r->out.statistics->statistics[MGMT_STATS_PKTS_IN] = 0;
+ r->out.statistics->statistics[MGMT_STATS_PKTS_OUT] = 0;
+
+ return WERR_OK;
+}
+
+
+/*
+ mgmt_is_server_listening
+*/
+static uint32_t dcesrv_mgmt_is_server_listening(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_is_server_listening *r)
+{
+ *r->out.status = 0;
+ return 1;
+}
+
+
+/*
+ mgmt_stop_server_listening
+*/
+static WERROR dcesrv_mgmt_stop_server_listening(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_stop_server_listening *r)
+{
+ return WERR_ACCESS_DENIED;
+}
+
+
+/*
+ mgmt_inq_princ_name
+*/
+static WERROR dcesrv_mgmt_inq_princ_name(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_inq_princ_name *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_mgmt_s.c"
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
new file mode 100644
index 0000000000..bbb78cb778
--- /dev/null
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
@@ -0,0 +1,818 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "param/param.h"
+
+/*
+ drsuapi_DsBind
+*/
+static WERROR dcesrv_drsuapi_DsBind(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsBind *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *handle;
+ struct drsuapi_DsBindInfoCtr *bind_info;
+ struct GUID site_guid;
+ struct ldb_result *site_res;
+ struct ldb_dn *server_site_dn;
+ static const char *site_attrs[] = { "objectGUID", NULL };
+ struct ldb_result *ntds_res;
+ struct ldb_dn *ntds_dn;
+ static const char *ntds_attrs[] = { "ms-DS-ReplicationEpoch", NULL };
+ uint32_t pid;
+ uint32_t repl_epoch;
+ int ret;
+
+ r->out.bind_info = NULL;
+ ZERO_STRUCTP(r->out.bind_handle);
+
+ b_state = talloc_zero(mem_ctx, struct drsuapi_bind_state);
+ W_ERROR_HAVE_NO_MEMORY(b_state);
+
+ /*
+ * connect to the samdb
+ */
+ b_state->sam_ctx = samdb_connect(b_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (!b_state->sam_ctx) {
+ return WERR_FOOBAR;
+ }
+
+ /*
+ * find out the guid of our own site
+ */
+ server_site_dn = samdb_server_site_dn(b_state->sam_ctx, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(server_site_dn);
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &site_res,
+ server_site_dn, LDB_SCOPE_BASE, site_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (site_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ site_guid = samdb_result_guid(site_res->msgs[0], "objectGUID");
+
+ /*
+ * lookup the local servers Replication Epoch
+ */
+ ntds_dn = samdb_ntds_settings_dn(b_state->sam_ctx);
+ W_ERROR_HAVE_NO_MEMORY(ntds_dn);
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &ntds_res,
+ ntds_dn, LDB_SCOPE_BASE, ntds_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (ntds_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ repl_epoch = samdb_result_uint(ntds_res->msgs[0], "ms-DS-ReplicationEpoch", 0);
+
+ /*
+ * The "process identifier" of the client.
+ * According to the WSPP docs, sectin 5.35, this is
+ * for informational and debugging purposes only.
+ * The assignment is implementation specific.
+ */
+ pid = 0;
+
+ /*
+ * store the clients bind_guid
+ */
+ if (r->in.bind_guid) {
+ b_state->remote_bind_guid = *r->in.bind_guid;
+ }
+
+ /*
+ * store the clients bind_info
+ */
+ if (r->in.bind_info) {
+ switch (r->in.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &r->in.bind_info->info.info24;
+ b_state->remote_info28.supported_extensions = info24->supported_extensions;
+ b_state->remote_info28.site_guid = info24->site_guid;
+ b_state->remote_info28.pid = info24->pid;
+ b_state->remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 28:
+ b_state->remote_info28 = r->in.bind_info->info.info28;
+ break;
+ }
+ }
+
+ /*
+ * fill in our local bind info 28
+ */
+ b_state->local_info28.supported_extensions = 0;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+#if 0 /* we don't support MSZIP compression (only decompression) */
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+#endif
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ if (0 /*domain.behavior_version == 2*/) {
+ /* TODO: find out how this is really triggered! */
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ }
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_00100000;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+#if 0 /* we don't support XPRESS compression yet */
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+#endif
+ b_state->local_info28.site_guid = site_guid;
+ b_state->local_info28.pid = pid;
+ b_state->local_info28.repl_epoch = repl_epoch;
+
+ /*
+ * allocate the return bind_info
+ */
+ bind_info = talloc(mem_ctx, struct drsuapi_DsBindInfoCtr);
+ W_ERROR_HAVE_NO_MEMORY(bind_info);
+
+ bind_info->length = 28;
+ bind_info->info.info28 = b_state->local_info28;
+
+ /*
+ * allocate a bind handle
+ */
+ handle = dcesrv_handle_new(dce_call->context, DRSUAPI_BIND_HANDLE);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+ handle->data = talloc_steal(handle, b_state);
+
+ /*
+ * prepare reply
+ */
+ r->out.bind_info = bind_info;
+ *r->out.bind_handle = handle->wire_handle;
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsUnbind
+*/
+static WERROR dcesrv_drsuapi_DsUnbind(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsUnbind *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.bind_handle = *r->in.bind_handle;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.bind_handle);
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaSync
+*/
+static WERROR dcesrv_drsuapi_DsReplicaSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaSync *r)
+{
+ /* TODO: implement this call correct!
+ * for now we just say yes,
+ * because we have no output parameter
+ */
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsGetNCChanges
+*/
+static WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChanges *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsReplicaUpdateRefs
+*/
+static WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaUpdateRefs *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REPLICA_ADD
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_ADD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_ADD *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REPLICA_DEL
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_DEL(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_DEL *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REPLICA_MODIFY
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_MODIFY(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_MODIFY *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_VERIFY_NAMES
+*/
+static WERROR dcesrv_DRSUAPI_VERIFY_NAMES(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_VERIFY_NAMES *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsGetMemberships
+*/
+static WERROR dcesrv_drsuapi_DsGetMemberships(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetMemberships *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_INTER_DOMAIN_MOVE
+*/
+static WERROR dcesrv_DRSUAPI_INTER_DOMAIN_MOVE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_INTER_DOMAIN_MOVE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsGetNT4ChangeLog
+*/
+static WERROR dcesrv_drsuapi_DsGetNT4ChangeLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNT4ChangeLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsCrackNames
+*/
+WERROR dcesrv_drsuapi_DsCrackNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsCrackNames *r)
+{
+ WERROR status;
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+
+ r->out.level = r->in.level;
+ ZERO_STRUCT(r->out.ctr);
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ switch (r->in.level) {
+ case 1: {
+ struct drsuapi_DsNameCtr1 *ctr1;
+ struct drsuapi_DsNameInfo1 *names;
+ int count;
+ int i;
+
+ ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ count = r->in.req.req1.count;
+ names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
+ W_ERROR_HAVE_NO_MEMORY(names);
+
+ for (i=0; i < count; i++) {
+ status = DsCrackNameOneName(b_state->sam_ctx, mem_ctx,
+ r->in.req.req1.format_flags,
+ r->in.req.req1.format_offered,
+ r->in.req.req1.format_desired,
+ r->in.req.req1.names[i].str,
+ &names[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ ctr1->count = count;
+ ctr1->array = names;
+ r->out.ctr.ctr1 = ctr1;
+
+ return WERR_OK;
+ }
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/*
+ drsuapi_DsWriteAccountSpn
+*/
+static WERROR dcesrv_drsuapi_DsWriteAccountSpn(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsWriteAccountSpn *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+
+ r->out.level = r->in.level;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ switch (r->in.level) {
+ case 1: {
+ struct drsuapi_DsWriteAccountSpnRequest1 *req;
+ struct ldb_message *msg;
+ int count, i, ret;
+ req = &r->in.req.req1;
+ count = req->count;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return WERR_NOMEM;
+ }
+
+ msg->dn = ldb_dn_new(msg, b_state->sam_ctx, req->object_dn);
+ if ( ! ldb_dn_validate(msg->dn)) {
+ r->out.res.res1.status = WERR_OK;
+ return WERR_OK;
+ }
+
+ /* construct mods */
+ for (i = 0; i < count; i++) {
+ samdb_msg_add_string(b_state->sam_ctx,
+ msg, msg, "servicePrincipalName",
+ req->spn_names[i].str);
+ }
+ for (i=0;i<msg->num_elements;i++) {
+ switch (req->operation) {
+ case DRSUAPI_DS_SPN_OPERATION_ADD:
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ break;
+ case DRSUAPI_DS_SPN_OPERATION_REPLACE:
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ break;
+ case DRSUAPI_DS_SPN_OPERATION_DELETE:
+ msg->elements[i].flags = LDB_FLAG_MOD_DELETE;
+ break;
+ }
+ }
+
+ /* Apply to database */
+
+ ret = ldb_modify(b_state->sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to modify SPNs on %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(b_state->sam_ctx)));
+ r->out.res.res1.status = WERR_ACCESS_DENIED;
+ } else {
+ r->out.res.res1.status = WERR_OK;
+ }
+
+ return WERR_OK;
+ }
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ drsuapi_DsRemoveDSServer
+*/
+static WERROR dcesrv_drsuapi_DsRemoveDSServer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsRemoveDSServer *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REMOVE_DS_DOMAIN
+*/
+static WERROR dcesrv_DRSUAPI_REMOVE_DS_DOMAIN(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REMOVE_DS_DOMAIN *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* Obtain the site name from a server DN */
+const char *result_site_name(struct ldb_dn *site_dn)
+{
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ const struct ldb_val *val = ldb_dn_get_component_val(site_dn, 2);
+ const char *name = ldb_dn_get_component_name(site_dn, 2);
+
+ if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
+ /* Ensure this matches the format. This gives us a
+ * bit more confidence that a 'cn' value will be a
+ * ascii string */
+ return NULL;
+ }
+ if (val) {
+ return (char *)val->data;
+ }
+ return NULL;
+}
+
+/*
+ drsuapi_DsGetDomainControllerInfo
+*/
+static WERROR dcesrv_drsuapi_DsGetDomainControllerInfo_1(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetDomainControllerInfo *r)
+{
+ struct ldb_dn *sites_dn;
+ struct ldb_result *res;
+
+ const char *attrs_account_1[] = { "cn", "dnsHostName", NULL };
+ const char *attrs_account_2[] = { "cn", "dnsHostName", "objectGUID", NULL };
+
+ const char *attrs_none[] = { NULL };
+
+ const char *attrs_site[] = { "objectGUID", NULL };
+
+ const char *attrs_ntds[] = { "options", "objectGUID", NULL };
+
+ const char *attrs_1[] = { "serverReference", "cn", "dnsHostName", NULL };
+ const char *attrs_2[] = { "serverReference", "cn", "dnsHostName", "objectGUID", NULL };
+ const char **attrs;
+
+ struct drsuapi_DsGetDCInfoCtr1 *ctr1;
+ struct drsuapi_DsGetDCInfoCtr2 *ctr2;
+
+ int ret, i;
+
+ r->out.level_out = r->in.req.req1.level;
+
+ sites_dn = samdb_sites_dn(b_state->sam_ctx, mem_ctx);
+ if (!sites_dn) {
+ return WERR_DS_OBJ_NOT_FOUND;
+ }
+
+ switch (r->out.level_out) {
+ case -1:
+ /* this level is not like the others */
+ return WERR_UNKNOWN_LEVEL;
+ case 1:
+ attrs = attrs_1;
+ break;
+ case 2:
+ attrs = attrs_2;
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res, sites_dn, LDB_SCOPE_SUBTREE, attrs,
+ "objectClass=server");
+
+ if (ret) {
+ DEBUG(1, ("searching for servers in sites DN %s failed: %s\n",
+ ldb_dn_get_linearized(sites_dn), ldb_errstring(b_state->sam_ctx)));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ switch (r->out.level_out) {
+ case 1:
+ ctr1 = &r->out.ctr.ctr1;
+ ctr1->count = res->count;
+ ctr1->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo1,
+ res->count);
+ for (i=0; i < res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOMEM;
+ }
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_1, "objectClass=computer");
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr1->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr1->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr1->array[i].computer_dn
+ = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret) {
+ return WERR_GENERAL_FAILURE;
+ }
+ if (res_domain->count == 1) {
+ ctr1->array[i].is_pdc = true;
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr1->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr1->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+
+
+ ctr1->array[i].is_enabled = true;
+
+ }
+ break;
+ case 2:
+ ctr2 = &r->out.ctr.ctr2;
+ ctr2->count = res->count;
+ ctr2->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo2,
+ res->count);
+ for (i=0; i < res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_ntds;
+ struct ldb_dn *site_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_site;
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOMEM;
+ }
+
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ if (!site_dn || !ldb_dn_remove_child_components(site_dn, 2)) {
+ return WERR_NOMEM;
+ }
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_ntds, ntds_dn,
+ LDB_SCOPE_BASE, attrs_ntds, "objectClass=nTDSDSA");
+ if (ret == LDB_SUCCESS && res_ntds->count == 1) {
+ ctr2->array[i].is_gc
+ = (ldb_msg_find_attr_as_int(res_ntds->msgs[0], "options", 0) == 1);
+ ctr2->array[i].ntds_guid
+ = samdb_result_guid(res_ntds->msgs[0], "objectGUID");
+ ctr2->array[i].ntds_dn = ldb_dn_get_linearized(res_ntds->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for NTDS DN %s failed: %s\n",
+ ldb_dn_get_linearized(ntds_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_site, site_dn,
+ LDB_SCOPE_BASE, attrs_site, "objectClass=site");
+ if (ret == LDB_SUCCESS && res_site->count == 1) {
+ ctr2->array[i].site_guid
+ = samdb_result_guid(res_site->msgs[0], "objectGUID");
+ ctr2->array[i].site_dn = ldb_dn_get_linearized(res_site->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for site DN %s failed: %s\n",
+ ldb_dn_get_linearized(site_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_2, "objectClass=computer");
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr2->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr2->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr2->array[i].computer_dn = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+ ctr2->array[i].computer_guid
+ = samdb_result_guid(res_account->msgs[0], "objectGUID");
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search_exp_fmt(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret == LDB_SUCCESS && res_domain->count == 1) {
+ ctr2->array[i].is_pdc = true;
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for domain DN %s failed: %s\n",
+ ldb_dn_get_linearized(domain_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer account DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr2->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr2->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+ ctr2->array[i].server_guid
+ = samdb_result_guid(res->msgs[i], "objectGUID");
+
+ ctr2->array[i].is_enabled = true;
+
+ }
+ break;
+ }
+ return WERR_OK;
+}
+
+/*
+ drsuapi_DsGetDomainControllerInfo
+*/
+static WERROR dcesrv_drsuapi_DsGetDomainControllerInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetDomainControllerInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct drsuapi_bind_state *b_state;
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ return dcesrv_drsuapi_DsGetDomainControllerInfo_1(b_state, mem_ctx, r);
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ drsuapi_DsAddEntry
+*/
+static WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsAddEntry *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_EXECUTE_KCC
+*/
+static WERROR dcesrv_DRSUAPI_EXECUTE_KCC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_EXECUTE_KCC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsReplicaGetInfo
+*/
+static WERROR dcesrv_drsuapi_DsReplicaGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_ADD_SID_HISTORY
+*/
+static WERROR dcesrv_DRSUAPI_ADD_SID_HISTORY(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_ADD_SID_HISTORY *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ drsuapi_DsGetMemberships2
+*/
+static WERROR dcesrv_drsuapi_DsGetMemberships2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetMemberships2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ DRSUAPI_REPLICA_VERIFY_OBJECTS
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_VERIFY_OBJECTS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_VERIFY_OBJECTS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_GET_OBJECT_EXISTENCE
+*/
+static WERROR dcesrv_DRSUAPI_GET_OBJECT_EXISTENCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_GET_OBJECT_EXISTENCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_QuerySitesByCost
+*/
+static WERROR dcesrv_drsuapi_QuerySitesByCost(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_QuerySitesByCost *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_drsuapi_s.c"
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.h b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
new file mode 100644
index 0000000000..7412865449
--- /dev/null
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+
+ Copyright (C) Stefan Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum drsuapi_handle {
+ DRSUAPI_BIND_HANDLE,
+};
+
+/*
+ state asscoiated with a drsuapi_DsBind*() operation
+*/
+struct drsuapi_bind_state {
+ struct ldb_context *sam_ctx;
+ struct GUID remote_bind_guid;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct drsuapi_DsBindInfo28 local_info28;
+};
diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c
new file mode 100644
index 0000000000..f188ef0a0d
--- /dev/null
+++ b/source4/rpc_server/echo/rpc_echo.c
@@ -0,0 +1,204 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the echo pipe
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "lib/events/events.h"
+
+
+static NTSTATUS dcesrv_echo_AddOne(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_AddOne *r)
+{
+ *r->out.out_data = r->in.in_data + 1;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_EchoData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_EchoData *r)
+{
+ if (!r->in.len) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.out_data = talloc_memdup(mem_ctx, r->in.in_data, r->in.len);
+ if (!r->out.out_data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_SinkData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_SinkData *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_SourceData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_SourceData *r)
+{
+ int i;
+
+ r->out.data = talloc_array(mem_ctx, uint8_t, r->in.len);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.len;i++) {
+ r->out.data[i] = i;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestCall(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestCall *r)
+{
+ *r->out.s2 = talloc_strdup(mem_ctx, r->in.s1);
+ if (r->in.s1 && !*r->out.s2) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestCall2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestCall2 *r)
+{
+ r->out.info = talloc(mem_ctx, union echo_Info);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ r->out.info->info1.v = 10;
+ break;
+ case 2:
+ r->out.info->info2.v = 20;
+ break;
+ case 3:
+ r->out.info->info3.v = 30;
+ break;
+ case 4:
+ r->out.info->info4.v = 40;
+ break;
+ case 5:
+ r->out.info->info5.v1 = 50;
+ r->out.info->info5.v2 = 60;
+ break;
+ case 6:
+ r->out.info->info6.v1 = 70;
+ r->out.info->info6.info1.v= 80;
+ break;
+ case 7:
+ r->out.info->info7.v1 = 80;
+ r->out.info->info7.info4.v = 90;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestEnum *r)
+{
+ r->out.foo2->e1 = ECHO_ENUM2;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestSurrounding(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSurrounding *r)
+{
+ if (!r->in.data) {
+ r->out.data = NULL;
+ return NT_STATUS_OK;
+ }
+
+ r->out.data = talloc(mem_ctx, struct echo_Surrounding);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.data->x = 2 * r->in.data->x;
+ r->out.data->surrounding = talloc_zero_array(mem_ctx, uint16_t, r->out.data->x);
+ if (!r->out.data->surrounding) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static uint16_t dcesrv_echo_TestDoublePointer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestDoublePointer *r)
+{
+ if (!*r->in.data)
+ return 0;
+ if (!**r->in.data)
+ return 0;
+ return ***r->in.data;
+}
+
+struct echo_TestSleep_private {
+ struct dcesrv_call_state *dce_call;
+ struct echo_TestSleep *r;
+};
+
+static void echo_TestSleep_handler(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private)
+{
+ struct echo_TestSleep_private *p = talloc_get_type(private,
+ struct echo_TestSleep_private);
+ struct echo_TestSleep *r = p->r;
+ NTSTATUS status;
+
+ r->out.result = r->in.seconds;
+
+ status = dcesrv_reply(p->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("echo_TestSleep_handler: dcesrv_reply() failed - %s\n",
+ nt_errstr(status)));
+ }
+}
+
+static long dcesrv_echo_TestSleep(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSleep *r)
+{
+ struct echo_TestSleep_private *p;
+
+ if (!(dce_call->state_flags & DCESRV_CALL_STATE_FLAG_MAY_ASYNC)) {
+ /* we're not allowed to reply async */
+ sleep(r->in.seconds);
+ return r->in.seconds;
+ }
+
+ /* we're allowed to reply async */
+ p = talloc(mem_ctx, struct echo_TestSleep_private);
+ if (!p) {
+ return 0;
+ }
+
+ p->dce_call = dce_call;
+ p->r = r;
+
+ event_add_timed(dce_call->event_ctx, p,
+ timeval_add(&dce_call->time, r->in.seconds, 0),
+ echo_TestSleep_handler, p);
+
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ return 0;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_echo_s.c"
diff --git a/source4/rpc_server/epmapper/rpc_epmapper.c b/source4/rpc_server/epmapper/rpc_epmapper.c
new file mode 100644
index 0000000000..5491bd242a
--- /dev/null
+++ b/source4/rpc_server/epmapper/rpc_epmapper.c
@@ -0,0 +1,264 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the epmapper pipe
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+
+typedef uint32_t error_status_t;
+
+/* handle types for this module */
+enum handle_types {HTYPE_LOOKUP};
+
+/* a endpoint combined with an interface description */
+struct dcesrv_ep_iface {
+ const char *name;
+ struct epm_tower ep;
+};
+
+/*
+ build a list of all interfaces handled by all endpoint servers
+*/
+static uint32_t build_ep_list(TALLOC_CTX *mem_ctx,
+ struct dcesrv_endpoint *endpoint_list,
+ struct dcesrv_ep_iface **eps)
+{
+ struct dcesrv_endpoint *d;
+ uint32_t total = 0;
+ NTSTATUS status;
+
+ *eps = NULL;
+
+ for (d=endpoint_list; d; d=d->next) {
+ struct dcesrv_if_list *iface;
+ struct dcerpc_binding *description;
+
+ for (iface=d->interface_list;iface;iface=iface->next) {
+ (*eps) = talloc_realloc(mem_ctx,
+ *eps,
+ struct dcesrv_ep_iface,
+ total + 1);
+ if (!*eps) {
+ return 0;
+ }
+ (*eps)[total].name = iface->iface.name;
+
+ description = d->ep_description;
+ description->object = iface->iface.syntax_id;
+
+ status = dcerpc_binding_build_tower(mem_ctx, description, &(*eps)[total].ep);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to build tower for %s\n", iface->iface.name));
+ continue;
+ }
+ total++;
+ }
+ }
+
+ return total;
+}
+
+
+static error_status_t dcesrv_epm_Insert(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct epm_Insert *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_Delete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Delete *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ implement epm_Lookup. This call is used to enumerate the interfaces
+ available on a rpc server
+*/
+static error_status_t dcesrv_epm_Lookup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ struct dcesrv_handle *h;
+ struct rpc_eps {
+ uint32_t count;
+ struct dcesrv_ep_iface *e;
+ } *eps;
+ uint32_t num_ents;
+ int i;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.entry_handle, HTYPE_LOOKUP);
+
+ eps = h->data;
+
+ if (!eps) {
+ /* this is the first call - fill the list. Subsequent calls
+ will feed from this list, stored in the handle */
+ eps = talloc(h, struct rpc_eps);
+ if (!eps) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ h->data = eps;
+
+ eps->count = build_ep_list(h, dce_call->conn->dce_ctx->endpoint_list, &eps->e);
+ }
+
+ /* return the next N elements */
+ num_ents = r->in.max_ents;
+ if (num_ents > eps->count) {
+ num_ents = eps->count;
+ }
+
+ *r->out.entry_handle = h->wire_handle;
+ r->out.num_ents = talloc(mem_ctx, uint32_t);
+ *r->out.num_ents = num_ents;
+
+ if (num_ents == 0) {
+ r->out.entries = NULL;
+ ZERO_STRUCTP(r->out.entry_handle);
+ talloc_free(h);
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.entries = talloc_array(mem_ctx, struct epm_entry_t, num_ents);
+ if (!r->out.entries) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<num_ents;i++) {
+ ZERO_STRUCT(r->out.entries[i].object);
+ r->out.entries[i].annotation = eps->e[i].name;
+ r->out.entries[i].tower = talloc(mem_ctx, struct epm_twr_t);
+ if (!r->out.entries[i].tower) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ r->out.entries[i].tower->tower = eps->e[i].ep;
+ }
+
+ eps->count -= num_ents;
+ eps->e += num_ents;
+
+ return EPMAPPER_STATUS_OK;
+}
+
+
+/*
+ implement epm_Map. This is used to find the specific endpoint to talk to given
+ a generic protocol tower
+*/
+static error_status_t dcesrv_epm_Map(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Map *r)
+{
+ uint32_t count;
+ int i;
+ struct dcesrv_ep_iface *eps;
+ struct epm_floor *floors;
+ enum dcerpc_transport_t transport;
+ struct ndr_syntax_id ndr_syntax;
+
+ count = build_ep_list(mem_ctx, dce_call->conn->dce_ctx->endpoint_list, &eps);
+
+ ZERO_STRUCT(*r->out.entry_handle);
+ r->out.num_towers = talloc(mem_ctx, uint32_t);
+ *r->out.num_towers = 1;
+ r->out.towers = talloc(mem_ctx, struct epm_twr_p_t);
+ if (!r->out.towers) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ r->out.towers->twr = talloc(mem_ctx, struct epm_twr_t);
+ if (!r->out.towers->twr) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ if (!r->in.map_tower || r->in.max_towers == 0 ||
+ r->in.map_tower->tower.num_floors < 3) {
+ goto failed;
+ }
+
+ floors = r->in.map_tower->tower.floors;
+
+ dcerpc_floor_get_lhs_data(&r->in.map_tower->tower.floors[1], &ndr_syntax);
+
+ if (floors[1].lhs.protocol != EPM_PROTOCOL_UUID ||
+ !GUID_equal(&ndr_syntax.uuid, &ndr_transfer_syntax.uuid) ||
+ ndr_syntax.if_version != ndr_transfer_syntax.if_version) {
+ goto failed;
+ }
+
+ transport = dcerpc_transport_by_tower(&r->in.map_tower->tower);
+
+ if (transport == -1) {
+ DEBUG(2, ("Client requested unknown transport with levels: "));
+ for (i = 2; i < r->in.map_tower->tower.num_floors; i++) {
+ DEBUG(2, ("%d, ", r->in.map_tower->tower.floors[i].lhs.protocol));
+ }
+ DEBUG(2, ("\n"));
+ goto failed;
+ }
+
+ for (i=0;i<count;i++) {
+ if (
+ data_blob_cmp(&r->in.map_tower->tower.floors[0].lhs.lhs_data,
+ &eps[i].ep.floors[0].lhs.lhs_data) != 0
+ || transport != dcerpc_transport_by_tower(&eps[i].ep)) {
+ continue;
+ }
+
+ r->out.towers->twr->tower = eps[i].ep;
+ r->out.towers->twr->tower_length = 0;
+ return EPMAPPER_STATUS_OK;
+ }
+
+
+failed:
+ *r->out.num_towers = 0;
+ r->out.towers->twr = NULL;
+
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+}
+
+static error_status_t dcesrv_epm_LookupHandleFree(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_LookupHandleFree *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_InqObject(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_InqObject *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_MgmtDelete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_MgmtDelete *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_MapAuth(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_MapAuth *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_epmapper_s.c"
diff --git a/source4/rpc_server/handles.c b/source4/rpc_server/handles.c
new file mode 100644
index 0000000000..47174b6eeb
--- /dev/null
+++ b/source4/rpc_server/handles.c
@@ -0,0 +1,91 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc handle code
+
+ Copyright (C) Andrew Tridgell 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+
+/*
+ destroy a rpc handle
+*/
+static int dcesrv_handle_destructor(struct dcesrv_handle *h)
+{
+ DLIST_REMOVE(h->context->handles, h);
+ talloc_free(h);
+ return 0;
+}
+
+
+/*
+ allocate a new rpc handle
+*/
+_PUBLIC_ struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_connection_context *context,
+ uint8_t handle_type)
+{
+ struct dcesrv_handle *h;
+
+ h = talloc(context, struct dcesrv_handle);
+ if (!h) {
+ return NULL;
+ }
+ h->data = NULL;
+ h->context = context;
+
+ h->wire_handle.handle_type = handle_type;
+ h->wire_handle.uuid = GUID_random();
+
+ DLIST_ADD(context->handles, h);
+
+ talloc_set_destructor(h, dcesrv_handle_destructor);
+
+ return h;
+}
+
+/**
+ find an internal handle given a wire handle. If the wire handle is NULL then
+ allocate a new handle
+*/
+_PUBLIC_ struct dcesrv_handle *dcesrv_handle_fetch(
+ struct dcesrv_connection_context *context,
+ struct policy_handle *p,
+ uint8_t handle_type)
+{
+ struct dcesrv_handle *h;
+
+ if (policy_handle_empty(p)) {
+ return dcesrv_handle_new(context, handle_type);
+ }
+
+ for (h=context->handles; h; h=h->next) {
+ if (h->wire_handle.handle_type == p->handle_type &&
+ GUID_equal(&p->uuid, &h->wire_handle.uuid)) {
+ if (handle_type != DCESRV_HANDLE_ANY &&
+ p->handle_type != handle_type) {
+ DEBUG(0,("client gave us the wrong handle type (%d should be %d)\n",
+ p->handle_type, handle_type));
+ return NULL;
+ }
+ return h;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
new file mode 100644
index 0000000000..a1ca3b4a46
--- /dev/null
+++ b/source4/rpc_server/lsa/dcesrv_lsa.c
@@ -0,0 +1,3176 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "rpc_server/lsa/lsa.h"
+#include "util/util_ldb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "lib/crypto/crypto.h"
+
+/*
+ this type allows us to distinguish handle types
+*/
+
+/*
+ state associated with a lsa_OpenAccount() operation
+*/
+struct lsa_account_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct dom_sid *account_sid;
+};
+
+
+/*
+ state associated with a lsa_OpenSecret() operation
+*/
+struct lsa_secret_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct ldb_dn *secret_dn;
+ struct ldb_context *sam_ldb;
+ bool global;
+};
+
+/*
+ state associated with a lsa_OpenTrustedDomain() operation
+*/
+struct lsa_trusted_domain_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct ldb_dn *trusted_domain_dn;
+ struct ldb_dn *trusted_domain_user_dn;
+};
+
+static NTSTATUS dcesrv_lsa_EnumAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountRights *r);
+
+static NTSTATUS dcesrv_lsa_AddRemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state *state,
+ int ldb_flag,
+ struct dom_sid *sid,
+ const struct lsa_RightSet *rights);
+
+/*
+ lsa_Close
+*/
+static NTSTATUS dcesrv_lsa_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_Close *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_Delete
+*/
+static NTSTATUS dcesrv_lsa_Delete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_Delete *r)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+
+/*
+ lsa_DeleteObject
+*/
+static NTSTATUS dcesrv_lsa_DeleteObject(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_DeleteObject *r)
+{
+ struct dcesrv_handle *h;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ if (h->wire_handle.handle_type == LSA_HANDLE_SECRET) {
+ struct lsa_secret_state *secret_state = h->data;
+
+ /* Ensure user is permitted to delete this... */
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed delete things */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_delete(secret_state->sam_ldb,
+ secret_state->secret_dn);
+ talloc_free(h);
+ if (ret != 0) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_TRUSTED_DOMAIN) {
+ struct lsa_trusted_domain_state *trusted_domain_state = h->data;
+ ret = ldb_transaction_start(trusted_domain_state->policy->sam_ldb);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_delete(trusted_domain_state->policy->sam_ldb,
+ trusted_domain_state->trusted_domain_dn);
+ if (ret != 0) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (trusted_domain_state->trusted_domain_user_dn) {
+ ret = ldb_delete(trusted_domain_state->policy->sam_ldb,
+ trusted_domain_state->trusted_domain_user_dn);
+ if (ret != 0) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ ret = ldb_transaction_commit(trusted_domain_state->policy->sam_ldb);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ talloc_free(h);
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_ACCOUNT) {
+ struct lsa_RightSet *rights;
+ struct lsa_account_state *astate;
+ struct lsa_EnumAccountRights r2;
+ NTSTATUS status;
+
+ rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ r2.in.handle = &astate->policy->handle->wire_handle;
+ r2.in.sid = astate->account_sid;
+ r2.out.rights = rights;
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ r2.out.rights);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+ }
+
+ return NT_STATUS_INVALID_HANDLE;
+}
+
+
+/*
+ lsa_EnumPrivs
+*/
+static NTSTATUS dcesrv_lsa_EnumPrivs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumPrivs *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int i;
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ i = *r->in.resume_handle;
+ if (i == 0) i = 1;
+
+ while ((privname = sec_privilege_name(i)) &&
+ r->out.privs->count < r->in.max_count) {
+ struct lsa_PrivEntry *e;
+
+ r->out.privs->privs = talloc_realloc(r->out.privs,
+ r->out.privs->privs,
+ struct lsa_PrivEntry,
+ r->out.privs->count+1);
+ if (r->out.privs->privs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ e = &r->out.privs->privs[r->out.privs->count];
+ e->luid.low = i;
+ e->luid.high = 0;
+ e->name.string = privname;
+ r->out.privs->count++;
+ i++;
+ }
+
+ *r->out.resume_handle = i;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QuerySecObj
+*/
+static NTSTATUS dcesrv_lsa_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QuerySecurity *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_SetSecObj
+*/
+static NTSTATUS dcesrv_lsa_SetSecObj(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSecObj *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_ChangePassword
+*/
+static NTSTATUS dcesrv_lsa_ChangePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_ChangePassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ dssetup_DsRoleGetPrimaryDomainInformation
+
+ This is not an LSA call, but is the only call left on the DSSETUP
+ pipe (after the pipe was truncated), and needs lsa_get_policy_state
+*/
+static WERROR dcesrv_dssetup_DsRoleGetPrimaryDomainInformation(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetPrimaryDomainInformation *r)
+{
+ union dssetup_DsRoleInfo *info;
+
+ info = talloc(mem_ctx, union dssetup_DsRoleInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case DS_ROLE_BASIC_INFORMATION:
+ {
+ enum dssetup_DsRole role = DS_ROLE_STANDALONE_SERVER;
+ uint32_t flags = 0;
+ const char *domain = NULL;
+ const char *dns_domain = NULL;
+ const char *forest = NULL;
+ struct GUID domain_guid;
+ struct lsa_policy_state *state;
+
+ NTSTATUS status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ ZERO_STRUCT(domain_guid);
+
+ switch (lp_server_role(dce_call->conn->dce_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ role = DS_ROLE_STANDALONE_SERVER;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ role = DS_ROLE_MEMBER_SERVER;
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ if (samdb_is_pdc(state->sam_ldb)) {
+ role = DS_ROLE_PRIMARY_DC;
+ } else {
+ role = DS_ROLE_BACKUP_DC;
+ }
+ break;
+ }
+
+ switch (lp_server_role(dce_call->conn->dce_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ domain = talloc_strdup(mem_ctx, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(domain);
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ domain = talloc_strdup(mem_ctx, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(domain);
+ /* TODO: what is with dns_domain and forest and guid? */
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ flags = DS_ROLE_PRIMARY_DS_RUNNING;
+
+ if (state->mixed_domain == 1) {
+ flags |= DS_ROLE_PRIMARY_DS_MIXED_MODE;
+ }
+
+ domain = state->domain_name;
+ dns_domain = state->domain_dns;
+ forest = state->forest_dns;
+
+ domain_guid = state->domain_guid;
+ flags |= DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ break;
+ }
+
+ info->basic.role = role;
+ info->basic.flags = flags;
+ info->basic.domain = domain;
+ info->basic.dns_domain = dns_domain;
+ info->basic.forest = forest;
+ info->basic.domain_guid = domain_guid;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ case DS_ROLE_UPGRADE_STATUS:
+ {
+ info->upgrade.upgrading = DS_ROLE_NOT_UPGRADING;
+ info->upgrade.previous_role = DS_ROLE_PREVIOUS_UNKNOWN;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ case DS_ROLE_OP_STATUS:
+ {
+ info->opstatus.status = DS_ROLE_OP_IDLE;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_PARAM;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+
+/*
+ fill in the AccountDomain info
+*/
+static NTSTATUS dcesrv_lsa_info_AccountDomain(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct lsa_DomainInfo *info)
+{
+ info->name.string = state->domain_name;
+ info->sid = state->domain_sid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ fill in the DNS domain info
+*/
+static NTSTATUS dcesrv_lsa_info_DNS(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct lsa_DnsDomainInfo *info)
+{
+ info->name.string = state->domain_name;
+ info->sid = state->domain_sid;
+ info->dns_domain.string = state->domain_dns;
+ info->dns_forest.string = state->forest_dns;
+ info->domain_guid = state->domain_guid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_QueryInfoPolicy2
+*/
+static NTSTATUS dcesrv_lsa_QueryInfoPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryInfoPolicy2 *r)
+{
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h;
+
+ r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ r->out.info = talloc(mem_ctx, union lsa_PolicyInformation);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case LSA_POLICY_INFO_DOMAIN:
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &r->out.info->account_domain);
+
+ case LSA_POLICY_INFO_DNS:
+ return dcesrv_lsa_info_DNS(state, mem_ctx, &r->out.info->dns);
+ case LSA_POLICY_INFO_DB:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+/*
+ lsa_QueryInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_QueryInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryInfoPolicy *r)
+{
+ struct lsa_QueryInfoPolicy2 r2;
+ NTSTATUS status;
+
+ r2.in.handle = r->in.handle;
+ r2.in.level = r->in.level;
+
+ status = dcesrv_lsa_QueryInfoPolicy2(dce_call, mem_ctx, &r2);
+
+ r->out.info = r2.out.info;
+
+ return status;
+}
+
+/*
+ lsa_SetInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_SetInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetInfoPolicy *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_ClearAuditLog
+*/
+static NTSTATUS dcesrv_lsa_ClearAuditLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_ClearAuditLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CreateAccount
+
+ This call does not seem to have any long-term effects, hence no database operations
+*/
+static NTSTATUS dcesrv_lsa_CreateAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateAccount *r)
+{
+ struct lsa_account_state *astate;
+
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h, *ah;
+
+ ZERO_STRUCTP(r->out.acct_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ astate = talloc(dce_call->conn, struct lsa_account_state);
+ if (astate == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->account_sid = dom_sid_dup(astate, r->in.sid);
+ if (astate->account_sid == NULL) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->policy = talloc_reference(astate, state);
+ astate->access_mask = r->in.access_mask;
+
+ ah = dcesrv_handle_new(dce_call->context, LSA_HANDLE_ACCOUNT);
+ if (!ah) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ah->data = talloc_steal(ah, astate);
+
+ *r->out.acct_handle = ah->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumAccounts
+*/
+static NTSTATUS dcesrv_lsa_EnumAccounts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccounts *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", NULL};
+ uint32_t count;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ /* NOTE: This call must only return accounts that have at least
+ one privilege set
+ */
+ ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "(&(objectSid=*)(privilege=*))");
+ if (ret < 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (*r->in.resume_handle >= ret) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ count = ret - *r->in.resume_handle;
+ if (count > r->in.num_entries) {
+ count = r->in.num_entries;
+ }
+
+ if (count == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_SidPtr, count);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<count;i++) {
+ r->out.sids->sids[i].sid =
+ samdb_result_dom_sid(r->out.sids->sids,
+ res[i + *r->in.resume_handle],
+ "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(r->out.sids->sids[i].sid);
+ }
+
+ r->out.sids->num_sids = count;
+ *r->out.resume_handle = count + *r->in.resume_handle;
+
+ return NT_STATUS_OK;
+
+}
+
+
+/*
+ lsa_CreateTrustedDomainEx2
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx2 *r,
+ int op)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs, *msg, *msg_user;
+ const char *attrs[] = {
+ NULL
+ };
+ const char *netbios_name;
+ const char *dns_name;
+ const char *name;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB trustAuthIncoming, trustAuthOutgoing, auth_blob;
+ struct trustAuthInAndOutBlob auth_struct;
+ int ret;
+ NTSTATUS nt_status;
+ enum ndr_err_code ndr_err;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.policy_handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+
+ policy_state = policy_handle->data;
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ netbios_name = r->in.info->netbios_name.string;
+ if (!netbios_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dns_name = r->in.info->domain_name.string;
+
+ trusted_domain_state = talloc(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ if (strcasecmp(netbios_name, "BUILTIN") == 0
+ || (dns_name && strcasecmp(dns_name, "BUILTIN") == 0)
+ || (dom_sid_in_domain(policy_state->builtin_sid, r->in.info->sid))) {
+ return NT_STATUS_INVALID_PARAMETER;;
+ }
+
+ if (strcasecmp(netbios_name, policy_state->domain_name) == 0
+ || strcasecmp(netbios_name, policy_state->domain_dns) == 0
+ || (dns_name && strcasecmp(dns_name, policy_state->domain_dns) == 0)
+ || (dns_name && strcasecmp(dns_name, policy_state->domain_name) == 0)
+ || (dom_sid_equal(policy_state->domain_sid, r->in.info->sid))) {
+ return NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED;
+ }
+
+ /* While this is a REF pointer, some of the functions that wrap this don't provide this */
+ if (op == NDR_LSA_CREATETRUSTEDDOMAIN) {
+ /* No secrets are created at this time, for this function */
+ auth_struct.outgoing.count = 0;
+ auth_struct.incoming.count = 0;
+ } else {
+ auth_blob = data_blob_const(r->in.auth_info->auth_blob.data, r->in.auth_info->auth_blob.size);
+ arcfour_crypt_blob(auth_blob.data, auth_blob.length, &session_key);
+ ndr_err = ndr_pull_struct_blob(&auth_blob, mem_ctx,
+ lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ &auth_struct,
+ (ndr_pull_flags_fn_t)ndr_pull_trustAuthInAndOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (auth_struct.incoming.count) {
+ ndr_err = ndr_push_struct_blob(&trustAuthIncoming, mem_ctx,
+ lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ &auth_struct.incoming,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ trustAuthIncoming = data_blob(NULL, 0);
+ }
+
+ if (auth_struct.outgoing.count) {
+ ndr_err = ndr_push_struct_blob(&trustAuthOutgoing, mem_ctx,
+ lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ &auth_struct.outgoing,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ trustAuthOutgoing = data_blob(NULL, 0);
+ }
+
+ ret = ldb_transaction_start(policy_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (dns_name) {
+ char *dns_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ char *netbios_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ /* search for the trusted_domain record */
+ ret = gendb_search(policy_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(|(flatname=%s)(cn=%s)(trustPartner=%s)(flatname=%s)(cn=%s)(trustPartner=%s))(objectclass=trustedDomain))",
+ dns_encoded, dns_encoded, dns_encoded, netbios_encoded, netbios_encoded, netbios_encoded);
+ if (ret > 0) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ } else {
+ char *netbios_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ /* search for the trusted_domain record */
+ ret = gendb_search(policy_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(|(flatname=%s)(cn=%s)(trustPartner=%s))(objectclass=trustedDomain))",
+ netbios_encoded, netbios_encoded, netbios_encoded);
+ if (ret > 0) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ if (ret < 0 ) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ name = dns_name ? dns_name : netbios_name;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, policy_state->system_dn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "cn=%s", name)) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "flatname", netbios_name);
+
+ if (r->in.info->sid) {
+ const char *sid_string = dom_sid_string(mem_ctx, r->in.info->sid);
+ if (!sid_string) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "securityIdentifier", sid_string);
+ }
+
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "objectClass", "trustedDomain");
+
+ samdb_msg_add_int(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustType", r->in.info->trust_type);
+
+ samdb_msg_add_int(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustAttributes", r->in.info->trust_attributes);
+
+ samdb_msg_add_int(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustDirection", r->in.info->trust_direction);
+
+ if (dns_name) {
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustPartner", dns_name);
+ }
+
+ if (trustAuthIncoming.data) {
+ ret = ldb_msg_add_value(msg, "trustAuthIncoming", &trustAuthIncoming, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (trustAuthOutgoing.data) {
+ ret = ldb_msg_add_value(msg, "trustAuthOutgoing", &trustAuthOutgoing, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msg->dn);
+
+ /* create the trusted_domain */
+ ret = ldb_add(trusted_domain_state->policy->sam_ldb, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (r->in.info->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
+ msg_user = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Inbound trusts must also create a cn=users object to match */
+
+ trusted_domain_state->trusted_domain_user_dn = msg_user->dn
+ = ldb_dn_copy(trusted_domain_state, policy_state->domain_dn);
+ if ( ! ldb_dn_add_child_fmt(msg_user->dn, "cn=users")) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( ! ldb_dn_add_child_fmt(msg_user->dn, "cn=%s", netbios_name)) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb_msg_add_string(msg_user, "objectClass", "user");
+
+ ldb_msg_add_steal_string(msg_user, "samAccountName",
+ talloc_asprintf(mem_ctx, "%s$", netbios_name));
+
+ if (samdb_msg_add_uint(trusted_domain_state->policy->sam_ldb, mem_ctx, msg_user,
+ "userAccountControl",
+ UF_INTERDOMAIN_TRUST_ACCOUNT) != 0) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (auth_struct.incoming.count) {
+ int i;
+ for (i=0; i < auth_struct.incoming.count; i++ ) {
+ if (auth_struct.incoming.current->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
+ samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, msg_user, "unicodePwd",
+ &auth_struct.incoming.current->array[i].AuthInfo.nt4owf.password);
+ } else if (auth_struct.incoming.current->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+ struct samr_Password hash;
+/*
+ . We cannot do this, as windows chooses to send in random passwords here, that won't convert to UTF8
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, msg_user, "userPassword",
+ auth_struct.incoming.current->array[i].AuthInfo.clear.password);
+*/
+ mdfour(hash.hash, auth_struct.incoming.current->array[i].AuthInfo.clear.password,
+ auth_struct.incoming.current->array[i].AuthInfo.clear.size);
+ samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, msg_user, "unicodePwd",
+ &hash);
+ }
+ }
+ }
+
+ /* create the cn=users trusted_domain account */
+ ret = ldb_add(trusted_domain_state->policy->sam_ldb, msg_user);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg_user->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg_user->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg_user->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ ret = ldb_transaction_commit(policy_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CreateTrustedDomainEx2
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomainEx2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx2 *r)
+{
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, r, NDR_LSA_CREATETRUSTEDDOMAINEX2);
+}
+/*
+ lsa_CreateTrustedDomainEx
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomainEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx *r)
+{
+ struct lsa_CreateTrustedDomainEx2 r2;
+
+ r2.in.policy_handle = r->in.policy_handle;
+ r2.in.info = r->in.info;
+ r2.in.auth_info = r->in.auth_info;
+ r2.out.trustdom_handle = r->out.trustdom_handle;
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, &r2, NDR_LSA_CREATETRUSTEDDOMAINEX);
+}
+
+/*
+ lsa_CreateTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomain *r)
+{
+ struct lsa_CreateTrustedDomainEx2 r2;
+
+ r2.in.policy_handle = r->in.policy_handle;
+ r2.in.info = talloc(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
+ if (!r2.in.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r2.in.info->domain_name.string = NULL;
+ r2.in.info->netbios_name = r->in.info->name;
+ r2.in.info->sid = r->in.info->sid;
+ r2.in.info->trust_direction = LSA_TRUST_DIRECTION_OUTBOUND;
+ r2.in.info->trust_type = LSA_TRUST_TYPE_DOWNLEVEL;
+ r2.in.info->trust_attributes = 0;
+
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.trustdom_handle = r->out.trustdom_handle;
+
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, &r2, NDR_LSA_CREATETRUSTEDDOMAIN);
+
+}
+
+/*
+ lsa_OpenTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_OpenTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenTrustedDomain *r)
+{
+ struct dcesrv_handle *policy_handle;
+
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ "trustDirection",
+ "flatname",
+ NULL
+ };
+
+ const char *sid_string;
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+ policy_state = policy_handle->data;
+
+ trusted_domain_state = talloc(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ sid_string = dom_sid_string(mem_ctx, r->in.sid);
+ if (!sid_string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the trusted_domain record */
+ ret = gendb_search(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(securityIdentifier=%s)(objectclass=trustedDomain))",
+ sid_string);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msgs[0]->dn);
+
+ trusted_domain_state->trusted_domain_user_dn = NULL;
+
+ if (ldb_msg_find_attr_as_int(msgs[0], "trustDirection", 0) & LSA_TRUST_DIRECTION_INBOUND) {
+ const char *flatname = ldb_binary_encode_string(mem_ctx, ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL));
+ /* search for the trusted_domain record */
+ ret = gendb_search(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, policy_state->domain_dn, &msgs, attrs,
+ "(&(samaccountname=%s$)(objectclass=user)(userAccountControl:1.2.840.113556.1.4.803:=%d))",
+ flatname, UF_INTERDOMAIN_TRUST_ACCOUNT);
+ if (ret == 1) {
+ trusted_domain_state->trusted_domain_user_dn = talloc_steal(trusted_domain_state, msgs[0]->dn);
+ }
+ }
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenTrustedDomainByName
+*/
+static NTSTATUS dcesrv_lsa_OpenTrustedDomainByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_OpenTrustedDomainByName *r)
+{
+ struct dcesrv_handle *policy_handle;
+
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ NULL
+ };
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ trusted_domain_state = talloc(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ /* search for the trusted_domain record */
+ ret = gendb_search(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(flatname=%s)(objectclass=trustedDomain))",
+ ldb_binary_encode_string(mem_ctx, r->in.name.string));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msgs[0]->dn);
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ lsa_SetTrustedDomainInfo
+*/
+static NTSTATUS dcesrv_lsa_SetTrustedDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetTrustedDomainInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ lsa_SetInfomrationTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_SetInformationTrustedDomain(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetInformationTrustedDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_DeleteTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_DeleteTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_DeleteTrustedDomain *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomain open;
+ struct lsa_DeleteObject delete;
+ struct dcesrv_handle *h;
+
+ open.in.handle = r->in.handle;
+ open.in.sid = r->in.dom_sid;
+ open.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ open.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!open.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomain(dce_call, mem_ctx, &open);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DCESRV_PULL_HANDLE(h, open.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ delete.in.handle = open.out.trustdom_handle;
+ delete.out.handle = open.out.trustdom_handle;
+ status = dcesrv_lsa_DeleteObject(dce_call, mem_ctx, &delete);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fill_trust_domain_ex(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct lsa_TrustDomainInfoInfoEx *info_ex)
+{
+ info_ex->domain_name.string
+ = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
+ info_ex->netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ info_ex->sid
+ = samdb_result_dom_sid(mem_ctx, msg, "securityIdentifier");
+ info_ex->trust_direction
+ = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
+ info_ex->trust_type
+ = ldb_msg_find_attr_as_int(msg, "trustType", 0);
+ info_ex->trust_attributes
+ = ldb_msg_find_attr_as_int(msg, "trustAttributes", 0);
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_QueryTrustedDomainInfo
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct ldb_message *msg;
+ int ret;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "flatname",
+ "trustPartner",
+ "securityIdentifier",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ "msDs-supportedEncryptionTypes",
+ NULL
+ };
+
+ DCESRV_PULL_HANDLE(h, r->in.trustdom_handle, LSA_HANDLE_TRUSTED_DOMAIN);
+
+ trusted_domain_state = h->data;
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(trusted_domain_state->policy->sam_ldb, mem_ctx,
+ trusted_domain_state->trusted_domain_dn, &res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ r->out.info = talloc(mem_ctx, union lsa_TrustedDomainInfo);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ switch (r->in.level) {
+ case LSA_TRUSTED_DOMAIN_INFO_NAME:
+ r->out.info->name.netbios_name.string
+ = samdb_result_string(msg, "flatname", NULL);
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ r->out.info->posix_offset.posix_offset
+ = samdb_result_uint(msg, "posixOffset", 0);
+ break;
+#if 0 /* Win2k3 doesn't implement this */
+ case LSA_TRUSTED_DOMAIN_INFO_BASIC:
+ r->out.info->info_basic.netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ r->out.info->info_basic.sid
+ = samdb_result_dom_sid(mem_ctx, msg, "securityIdentifier");
+ break;
+#endif
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ return fill_trust_domain_ex(mem_ctx, msg, &r->out.info->info_ex);
+
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ ZERO_STRUCT(r->out.info->full_info);
+ return fill_trust_domain_ex(mem_ctx, msg, &r->out.info->full_info.info_ex);
+
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL:
+ ZERO_STRUCT(r->out.info->full_info2_internal);
+ r->out.info->full_info2_internal.posix_offset.posix_offset
+ = samdb_result_uint(msg, "posixOffset", 0);
+ return fill_trust_domain_ex(mem_ctx, msg, &r->out.info->full_info2_internal.info.info_ex);
+
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRTYPION_TYPES:
+ r->out.info->enc_types.enc_types
+ = samdb_result_uint(msg, "msDs-supportedEncryptionTypes", KERB_ENCTYPE_RC4_HMAC_MD5);
+ break;
+
+ case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS:
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL:
+ /* oops, we don't want to return the info after all */
+ talloc_free(r->out.info);
+ r->out.info = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ /* oops, we don't want to return the info after all */
+ talloc_free(r->out.info);
+ r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QueryTrustedDomainInfoBySid
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfoBySid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfoBySid *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomain open;
+ struct lsa_QueryTrustedDomainInfo query;
+ struct dcesrv_handle *h;
+ open.in.handle = r->in.handle;
+ open.in.sid = r->in.dom_sid;
+ open.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ open.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!open.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomain(dce_call, mem_ctx, &open);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, open.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ query.in.trustdom_handle = open.out.trustdom_handle;
+ query.in.level = r->in.level;
+ status = dcesrv_lsa_QueryTrustedDomainInfo(dce_call, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info = query.out.info;
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_SetTrustedDomainInfoByName
+*/
+static NTSTATUS dcesrv_lsa_SetTrustedDomainInfoByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetTrustedDomainInfoByName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_QueryTrustedDomainInfoByName
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfoByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfoByName *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomainByName open;
+ struct lsa_QueryTrustedDomainInfo query;
+ struct dcesrv_handle *h;
+ open.in.handle = r->in.handle;
+ open.in.name = r->in.trusted_domain;
+ open.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ open.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!open.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomainByName(dce_call, mem_ctx, &open);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, open.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ query.in.trustdom_handle = open.out.trustdom_handle;
+ query.in.level = r->in.level;
+ status = dcesrv_lsa_QueryTrustedDomainInfo(dce_call, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info = query.out.info;
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CloseTrustedDomainEx
+*/
+static NTSTATUS dcesrv_lsa_CloseTrustedDomainEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CloseTrustedDomainEx *r)
+{
+ /* The result of a bad hair day from an IDL programmer? Not
+ * implmented in Win2k3. You should always just lsa_Close
+ * anyway. */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ comparison function for sorting lsa_DomainInformation array
+*/
+static int compare_DomainInfo(struct lsa_DomainInfo *e1, struct lsa_DomainInfo *e2)
+{
+ return strcasecmp_m(e1->name.string, e2->name.string);
+}
+
+/*
+ lsa_EnumTrustDom
+*/
+static NTSTATUS dcesrv_lsa_EnumTrustDom(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumTrustDom *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_DomainInfo *entries;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **domains;
+ const char *attrs[] = {
+ "flatname",
+ "securityIdentifier",
+ NULL
+ };
+
+
+ int count, i;
+
+ *r->out.resume_handle = 0;
+
+ r->out.domains->domains = NULL;
+ r->out.domains->count = 0;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ /* search for all users in this domain. This could possibly be cached and
+ resumed based on resume_key */
+ count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
+ "objectclass=trustedDomain");
+ if (count == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to lsa_TrustInformation format */
+ entries = talloc_array(mem_ctx, struct lsa_DomainInfo, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<count;i++) {
+ entries[i].sid = samdb_result_dom_sid(mem_ctx, domains[i], "securityIdentifier");
+ entries[i].name.string = samdb_result_string(domains[i], "flatname", NULL);
+ }
+
+ /* sort the results by name */
+ qsort(entries, count, sizeof(*entries),
+ (comparison_fn_t)compare_DomainInfo);
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 60 */
+ r->out.domains->count = count - *r->in.resume_handle;
+ r->out.domains->count = MIN(r->out.domains->count,
+ 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+ r->out.domains->count = r->out.domains->count;
+
+ if (r->out.domains->count < count - *r->in.resume_handle) {
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ comparison function for sorting lsa_DomainInformation array
+*/
+static int compare_TrustDomainInfoInfoEx(struct lsa_TrustDomainInfoInfoEx *e1, struct lsa_TrustDomainInfoInfoEx *e2)
+{
+ return strcasecmp_m(e1->netbios_name.string, e2->netbios_name.string);
+}
+
+/*
+ lsa_EnumTrustedDomainsEx
+*/
+static NTSTATUS dcesrv_lsa_EnumTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumTrustedDomainsEx *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_TrustDomainInfoInfoEx *entries;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **domains;
+ const char *attrs[] = {
+ "flatname",
+ "trustPartner",
+ "securityIdentifier",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ NULL
+ };
+ NTSTATUS nt_status;
+
+ int count, i;
+
+ *r->out.resume_handle = 0;
+
+ r->out.domains->domains = NULL;
+ r->out.domains->count = 0;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ /* search for all users in this domain. This could possibly be cached and
+ resumed based on resume_key */
+ count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
+ "objectclass=trustedDomain");
+ if (count == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to lsa_DomainInformation format */
+ entries = talloc_array(mem_ctx, struct lsa_TrustDomainInfoInfoEx, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<count;i++) {
+ nt_status = fill_trust_domain_ex(mem_ctx, domains[i], &entries[i]);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ /* sort the results by name */
+ qsort(entries, count, sizeof(*entries),
+ (comparison_fn_t)compare_TrustDomainInfoInfoEx);
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 60 */
+ r->out.domains->count = count - *r->in.resume_handle;
+ r->out.domains->count = MIN(r->out.domains->count,
+ 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+ r->out.domains->count = r->out.domains->count;
+
+ if (r->out.domains->count < count - *r->in.resume_handle) {
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenAccount
+*/
+static NTSTATUS dcesrv_lsa_OpenAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenAccount *r)
+{
+ struct dcesrv_handle *h, *ah;
+ struct lsa_policy_state *state;
+ struct lsa_account_state *astate;
+
+ ZERO_STRUCTP(r->out.acct_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ astate = talloc(dce_call->conn, struct lsa_account_state);
+ if (astate == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->account_sid = dom_sid_dup(astate, r->in.sid);
+ if (astate->account_sid == NULL) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->policy = talloc_reference(astate, state);
+ astate->access_mask = r->in.access_mask;
+
+ ah = dcesrv_handle_new(dce_call->context, LSA_HANDLE_ACCOUNT);
+ if (!ah) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ah->data = talloc_steal(ah, astate);
+
+ *r->out.acct_handle = ah->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumPrivsAccount
+*/
+static NTSTATUS dcesrv_lsa_EnumPrivsAccount(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumPrivsAccount *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ struct ldb_message_element *el;
+ const char *sidstr;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ r->out.privs = talloc(mem_ctx, struct lsa_PrivilegeSet);
+ r->out.privs->count = 0;
+ r->out.privs->unknown = 0;
+ r->out.privs->set = NULL;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, astate->account_sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(astate->policy->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "objectSid=%s", sidstr);
+ if (ret != 1) {
+ return NT_STATUS_OK;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.privs->set = talloc_array(r->out.privs,
+ struct lsa_LUIDAttribute, el->num_values);
+ if (r->out.privs->set == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ int id = sec_privilege_id((const char *)el->values[i].data);
+ if (id == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ r->out.privs->set[i].attribute = 0;
+ r->out.privs->set[i].luid.low = id;
+ r->out.privs->set[i].luid.high = 0;
+ }
+
+ r->out.privs->count = el->num_values;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_EnumAccountRights
+*/
+static NTSTATUS dcesrv_lsa_EnumAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ const char *sidstr;
+ struct ldb_message_element *el;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "(&(objectSid=%s)(privilege=*))", sidstr);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret == -1) {
+ DEBUG(3, ("searching for account rights for SID: %s failed: %s",
+ dom_sid_string(mem_ctx, r->in.sid),
+ ldb_errstring(state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ r->out.rights->count = el->num_values;
+ r->out.rights->names = talloc_array(r->out.rights,
+ struct lsa_StringLarge, r->out.rights->count);
+ if (r->out.rights->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ r->out.rights->names[i].string = (const char *)el->values[i].data;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ helper for lsa_AddAccountRights and lsa_RemoveAccountRights
+*/
+static NTSTATUS dcesrv_lsa_AddRemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state *state,
+ int ldb_flag,
+ struct dom_sid *sid,
+ const struct lsa_RightSet *rights)
+{
+ const char *sidstr;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ int i, ret;
+ struct lsa_EnumAccountRights r2;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = samdb_search_dn(state->sam_ldb, mem_ctx,
+ NULL, "objectSid=%s", sidstr);
+ if (msg->dn == NULL) {
+ NTSTATUS status;
+ if (ldb_flag == LDB_FLAG_MOD_DELETE) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ status = samdb_create_foreign_security_principal(state->sam_ldb, mem_ctx,
+ sid, &msg->dn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ldb_msg_add_empty(msg, "privilege", ldb_flag, NULL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldb_flag == LDB_FLAG_MOD_ADD) {
+ NTSTATUS status;
+
+ r2.in.handle = &state->handle->wire_handle;
+ r2.in.sid = sid;
+ r2.out.rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(r2.out.rights);
+ }
+ }
+
+ for (i=0;i<rights->count;i++) {
+ if (sec_privilege_id(rights->names[i].string) == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ if (ldb_flag == LDB_FLAG_MOD_ADD) {
+ int j;
+ for (j=0;j<r2.out.rights->count;j++) {
+ if (strcasecmp_m(r2.out.rights->names[j].string,
+ rights->names[i].string) == 0) {
+ break;
+ }
+ }
+ if (j != r2.out.rights->count) continue;
+ }
+
+ ret = ldb_msg_add_string(msg, "privilege", rights->names[i].string);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ el = ldb_msg_find_element(msg, "privilege");
+ if (!el) {
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_modify(state->sam_ldb, msg);
+ if (ret != 0) {
+ if (ldb_flag == LDB_FLAG_MOD_DELETE && ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ DEBUG(3, ("Could not %s attributes from %s: %s",
+ ldb_flag == LDB_FLAG_MOD_DELETE ? "delete" : "add",
+ ldb_dn_get_linearized(msg->dn), ldb_errstring(state->sam_ldb)));
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_AddPrivilegesToAccount
+*/
+static NTSTATUS dcesrv_lsa_AddPrivilegesToAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_AddPrivilegesToAccount *r)
+{
+ struct lsa_RightSet rights;
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ rights.count = r->in.privs->count;
+ rights.names = talloc_array(mem_ctx, struct lsa_StringLarge, rights.count);
+ if (rights.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<rights.count;i++) {
+ int id = r->in.privs->set[i].luid.low;
+ if (r->in.privs->set[i].luid.high) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ rights.names[i].string = sec_privilege_name(id);
+ if (rights.names[i].string == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_ADD, astate->account_sid,
+ &rights);
+}
+
+
+/*
+ lsa_RemovePrivilegesFromAccount
+*/
+static NTSTATUS dcesrv_lsa_RemovePrivilegesFromAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_RemovePrivilegesFromAccount *r)
+{
+ struct lsa_RightSet *rights;
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ if (r->in.remove_all == 1 &&
+ r->in.privs == NULL) {
+ struct lsa_EnumAccountRights r2;
+ NTSTATUS status;
+
+ r2.in.handle = &astate->policy->handle->wire_handle;
+ r2.in.sid = astate->account_sid;
+ r2.out.rights = rights;
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ r2.out.rights);
+ }
+
+ if (r->in.remove_all != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rights->count = r->in.privs->count;
+ rights->names = talloc_array(mem_ctx, struct lsa_StringLarge, rights->count);
+ if (rights->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<rights->count;i++) {
+ int id = r->in.privs->set[i].luid.low;
+ if (r->in.privs->set[i].luid.high) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ rights->names[i].string = sec_privilege_name(id);
+ if (rights->names[i].string == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ rights);
+}
+
+
+/*
+ lsa_GetQuotasForAccount
+*/
+static NTSTATUS dcesrv_lsa_GetQuotasForAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetQuotasForAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_SetQuotasForAccount
+*/
+static NTSTATUS dcesrv_lsa_SetQuotasForAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetQuotasForAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_GetSystemAccessAccount
+*/
+static NTSTATUS dcesrv_lsa_GetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetSystemAccessAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_SetSystemAccessAccount
+*/
+static NTSTATUS dcesrv_lsa_SetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSystemAccessAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CreateSecret
+*/
+static NTSTATUS dcesrv_lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateSecret *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_secret_state *secret_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs, *msg;
+ const char *errstr;
+ const char *attrs[] = {
+ NULL
+ };
+
+ const char *name;
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.sec_handle);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed create secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ secret_state = talloc(mem_ctx, struct lsa_secret_state);
+ if (!secret_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ secret_state->policy = policy_state;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (strncmp("G$", r->in.name.string, 2) == 0) {
+ const char *name2;
+ name = &r->in.name.string[2];
+ /* We need to connect to the database as system, as this is one of the rare RPC calls that must read the secrets (and this is denied otherwise) */
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(secret_state, dce_call->conn->dce_ctx->lp_ctx)));
+ secret_state->global = true;
+
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ name2 = talloc_asprintf(mem_ctx, "%s Secret", ldb_binary_encode_string(mem_ctx, name));
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ name2);
+ if (ret > 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("Failure searching for CN=%s: %s\n",
+ name2, ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, policy_state->system_dn);
+ if (!name2 || ! ldb_dn_add_child_fmt(msg->dn, "cn=%s", name2)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(secret_state->sam_ldb, mem_ctx, msg, "cn", name2);
+
+ } else {
+ secret_state->global = false;
+
+ name = r->in.name.string;
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ secrets_db_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb, mem_ctx,
+ ldb_dn_new(mem_ctx, secret_state->sam_ldb, "cn=LSA Secrets"),
+ &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret > 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("Failure searching for CN=%s: %s\n",
+ name, ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg->dn = ldb_dn_new_fmt(mem_ctx, secret_state->sam_ldb, "cn=%s,cn=LSA Secrets", name);
+ samdb_msg_add_string(secret_state->sam_ldb, mem_ctx, msg, "cn", name);
+ }
+
+ /* pull in all the template attributes. Note this is always from the global samdb */
+ ret = samdb_copy_template(secret_state->policy->sam_ldb, msg,
+ "secret", &errstr);
+ if (ret != 0) {
+ DEBUG(0,("Failed to load TemplateSecret from samdb: %s\n",
+ errstr));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ samdb_msg_add_string(secret_state->sam_ldb, mem_ctx, msg, "objectClass", "secret");
+
+ secret_state->secret_dn = talloc_reference(secret_state, msg->dn);
+
+ /* create the secret */
+ ret = ldb_add(secret_state->sam_ldb, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to create secret record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_SECRET);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, secret_state);
+
+ secret_state->access_mask = r->in.access_mask;
+ secret_state->policy = talloc_reference(secret_state, policy_state);
+
+ *r->out.sec_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenSecret
+*/
+static NTSTATUS dcesrv_lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenSecret *r)
+{
+ struct dcesrv_handle *policy_handle;
+
+ struct lsa_policy_state *policy_state;
+ struct lsa_secret_state *secret_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ NULL
+ };
+
+ const char *name;
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.sec_handle);
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed to access secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ secret_state = talloc(mem_ctx, struct lsa_secret_state);
+ if (!secret_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ secret_state->policy = policy_state;
+
+ if (strncmp("G$", r->in.name.string, 2) == 0) {
+ name = &r->in.name.string[2];
+ /* We need to connect to the database as system, as this is one of the rare RPC calls that must read the secrets (and this is denied otherwise) */
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(secret_state, dce_call->conn->dce_ctx->lp_ctx)));
+ secret_state->global = true;
+
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(cn=%s Secret)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ } else {
+ secret_state->global = false;
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ secrets_db_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx));
+
+ name = r->in.name.string;
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb, mem_ctx,
+ ldb_dn_new(mem_ctx, secret_state->sam_ldb, "cn=LSA Secrets"),
+ &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching CN=%s\n",
+ ret, ldb_binary_encode_string(mem_ctx, name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ secret_state->secret_dn = talloc_reference(secret_state, msgs[0]->dn);
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_SECRET);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, secret_state);
+
+ secret_state->access_mask = r->in.access_mask;
+ secret_state->policy = talloc_reference(secret_state, policy_state);
+
+ *r->out.sec_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_SetSecret
+*/
+static NTSTATUS dcesrv_lsa_SetSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSecret *r)
+{
+
+ struct dcesrv_handle *h;
+ struct lsa_secret_state *secret_state;
+ struct ldb_message *msg;
+ DATA_BLOB session_key;
+ DATA_BLOB crypt_secret, secret;
+ struct ldb_val val;
+ int ret;
+ NTSTATUS status = NT_STATUS_OK;
+
+ struct timeval now = timeval_current();
+ NTTIME nt_now = timeval_to_nttime(&now);
+
+ DCESRV_PULL_HANDLE(h, r->in.sec_handle, LSA_HANDLE_SECRET);
+
+ secret_state = h->data;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, secret_state->secret_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.old_val) {
+ /* Decrypt */
+ crypt_secret.data = r->in.old_val->data;
+ crypt_secret.length = r->in.old_val->size;
+
+ status = sess_decrypt_blob(mem_ctx, &crypt_secret, &session_key, &secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ val.data = secret.data;
+ val.length = secret.length;
+
+ /* set value */
+ if (samdb_msg_add_value(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue", &val) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set old value mtime */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ /* If the old value is not set, then migrate the
+ * current value to the old value */
+ const struct ldb_val *old_val;
+ NTTIME last_set_time;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "currentValue",
+ "lastSetTime",
+ NULL
+ };
+
+ /* search for the secret record */
+ ret = gendb_search_dn(secret_state->sam_ldb,mem_ctx,
+ secret_state->secret_dn, &res, attrs);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching dn=%s\n", ret,
+ ldb_dn_get_linearized(secret_state->secret_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ old_val = ldb_msg_find_ldb_val(res[0], "currentValue");
+ last_set_time = ldb_msg_find_attr_as_uint64(res[0], "lastSetTime", 0);
+
+ if (old_val) {
+ /* set old value */
+ if (samdb_msg_add_value(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue",
+ old_val) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ if (samdb_msg_add_delete(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue")) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ }
+
+ /* set old value mtime */
+ if (ldb_msg_find_ldb_val(res[0], "lastSetTime")) {
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", last_set_time) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ if (r->in.new_val) {
+ /* Decrypt */
+ crypt_secret.data = r->in.new_val->data;
+ crypt_secret.length = r->in.new_val->size;
+
+ status = sess_decrypt_blob(mem_ctx, &crypt_secret, &session_key, &secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ val.data = secret.data;
+ val.length = secret.length;
+
+ /* set value */
+ if (samdb_msg_add_value(secret_state->sam_ldb,
+ mem_ctx, msg, "currentValue", &val) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set new value mtime */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "lastSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ /* NULL out the NEW value */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "lastSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (samdb_msg_add_delete(secret_state->sam_ldb,
+ mem_ctx, msg, "currentValue")) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(secret_state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QuerySecret
+*/
+static NTSTATUS dcesrv_lsa_QuerySecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QuerySecret *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_secret_state *secret_state;
+ struct ldb_message *msg;
+ DATA_BLOB session_key;
+ DATA_BLOB crypt_secret, secret;
+ int ret;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "currentValue",
+ "priorValue",
+ "lastSetTime",
+ "priorSetTime",
+ NULL
+ };
+
+ NTSTATUS nt_status;
+
+ DCESRV_PULL_HANDLE(h, r->in.sec_handle, LSA_HANDLE_SECRET);
+
+ /* Ensure user is permitted to read this... */
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed to read secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ secret_state = h->data;
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(secret_state->sam_ldb, mem_ctx,
+ secret_state->secret_dn, &res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (r->in.old_val) {
+ const struct ldb_val *prior_val;
+ r->out.old_val = talloc_zero(mem_ctx, struct lsa_DATA_BUF_PTR);
+ if (!r->out.old_val) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prior_val = ldb_msg_find_ldb_val(res[0], "priorValue");
+
+ if (prior_val && prior_val->length) {
+ secret.data = prior_val->data;
+ secret.length = prior_val->length;
+
+ /* Encrypt */
+ crypt_secret = sess_encrypt_blob(mem_ctx, &secret, &session_key);
+ if (!crypt_secret.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.old_val->buf = talloc(mem_ctx, struct lsa_DATA_BUF);
+ if (!r->out.old_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.old_val->buf->size = crypt_secret.length;
+ r->out.old_val->buf->length = crypt_secret.length;
+ r->out.old_val->buf->data = crypt_secret.data;
+ }
+ }
+
+ if (r->in.old_mtime) {
+ r->out.old_mtime = talloc(mem_ctx, NTTIME);
+ if (!r->out.old_mtime) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.old_mtime = ldb_msg_find_attr_as_uint64(res[0], "priorSetTime", 0);
+ }
+
+ if (r->in.new_val) {
+ const struct ldb_val *new_val;
+ r->out.new_val = talloc_zero(mem_ctx, struct lsa_DATA_BUF_PTR);
+ if (!r->out.new_val) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_val = ldb_msg_find_ldb_val(res[0], "currentValue");
+
+ if (new_val && new_val->length) {
+ secret.data = new_val->data;
+ secret.length = new_val->length;
+
+ /* Encrypt */
+ crypt_secret = sess_encrypt_blob(mem_ctx, &secret, &session_key);
+ if (!crypt_secret.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.new_val->buf = talloc(mem_ctx, struct lsa_DATA_BUF);
+ if (!r->out.new_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.new_val->buf->length = crypt_secret.length;
+ r->out.new_val->buf->size = crypt_secret.length;
+ r->out.new_val->buf->data = crypt_secret.data;
+ }
+ }
+
+ if (r->in.new_mtime) {
+ r->out.new_mtime = talloc(mem_ctx, NTTIME);
+ if (!r->out.new_mtime) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.new_mtime = ldb_msg_find_attr_as_uint64(res[0], "lastSetTime", 0);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivValue
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivValue *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int id;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ id = sec_privilege_id(r->in.name->string);
+ if (id == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ r->out.luid->low = id;
+ r->out.luid->high = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivName
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivName *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ if (r->in.luid->high != 0) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privname = sec_privilege_name(r->in.luid->low);
+ if (privname == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ r->out.name = talloc(mem_ctx, struct lsa_StringLarge);
+ if (r->out.name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.name->string = privname;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivDisplayName
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivDisplayName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivDisplayName *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int id;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ id = sec_privilege_id(r->in.name->string);
+ if (id == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ r->out.disp_name = talloc(mem_ctx, struct lsa_StringLarge);
+ if (r->out.disp_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.disp_name->string = sec_privilege_display_name(id, r->in.language_id);
+ if (r->out.disp_name->string == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumAccountsWithUserRight
+*/
+static NTSTATUS dcesrv_lsa_EnumAccountsWithUserRight(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountsWithUserRight *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", NULL};
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ if (r->in.name == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privname = r->in.name->string;
+ if (sec_privilege_id(privname) == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "privilege=%s", privname);
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_SidPtr, ret);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<ret;i++) {
+ r->out.sids->sids[i].sid = samdb_result_dom_sid(r->out.sids->sids,
+ res[i], "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(r->out.sids->sids[i].sid);
+ }
+ r->out.sids->num_sids = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_AddAccountRights
+*/
+static NTSTATUS dcesrv_lsa_AddAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_AddAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, state,
+ LDB_FLAG_MOD_ADD,
+ r->in.sid, r->in.rights);
+}
+
+
+/*
+ lsa_RemoveAccountRights
+*/
+static NTSTATUS dcesrv_lsa_RemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RemoveAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, state,
+ LDB_FLAG_MOD_DELETE,
+ r->in.sid, r->in.rights);
+}
+
+
+/*
+ lsa_StorePrivateData
+*/
+static NTSTATUS dcesrv_lsa_StorePrivateData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_StorePrivateData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_RetrievePrivateData
+*/
+static NTSTATUS dcesrv_lsa_RetrievePrivateData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_RetrievePrivateData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_GetUserName
+*/
+static NTSTATUS dcesrv_lsa_GetUserName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetUserName *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ const char *account_name;
+ const char *authority_name;
+ struct lsa_String *_account_name;
+ struct lsa_StringPointer *_authority_name = NULL;
+
+ /* this is what w2k3 does */
+ r->out.account_name = r->in.account_name;
+ r->out.authority_name = r->in.authority_name;
+
+ if (r->in.account_name && r->in.account_name->string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.authority_name &&
+ r->in.authority_name->string &&
+ r->in.authority_name->string->string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ account_name = talloc_reference(mem_ctx, dce_call->conn->auth_state.session_info->server_info->account_name);
+ authority_name = talloc_reference(mem_ctx, dce_call->conn->auth_state.session_info->server_info->domain_name);
+
+ _account_name = talloc(mem_ctx, struct lsa_String);
+ NT_STATUS_HAVE_NO_MEMORY(_account_name);
+ _account_name->string = account_name;
+
+ if (r->in.authority_name) {
+ _authority_name = talloc(mem_ctx, struct lsa_StringPointer);
+ NT_STATUS_HAVE_NO_MEMORY(_authority_name);
+ _authority_name->string = talloc(mem_ctx, struct lsa_String);
+ NT_STATUS_HAVE_NO_MEMORY(_authority_name->string);
+ _authority_name->string->string = authority_name;
+ }
+
+ r->out.account_name = _account_name;
+ r->out.authority_name = _authority_name;
+
+ return status;
+}
+
+/*
+ lsa_SetInfoPolicy2
+*/
+static NTSTATUS dcesrv_lsa_SetInfoPolicy2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetInfoPolicy2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_QueryDomainInformationPolicy
+*/
+static NTSTATUS dcesrv_lsa_QueryDomainInformationPolicy(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_QueryDomainInformationPolicy *r)
+{
+ r->out.info = talloc(mem_ctx, union lsa_DomainInformationPolicy);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case LSA_DOMAIN_INFO_POLICY_EFS:
+ talloc_free(r->out.info);
+ r->out.info = NULL;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ case LSA_DOMAIN_INFO_POLICY_KERBEROS:
+ {
+ struct lsa_DomainInfoKerberos *k = &r->out.info->kerberos_info;
+ struct smb_krb5_context *smb_krb5_context;
+ int ret = smb_krb5_init_context(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &smb_krb5_context);
+ if (ret != 0) {
+ talloc_free(r->out.info);
+ r->out.info = NULL;
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ k->enforce_restrictions = 0; /* FIXME, details missing from MS-LSAD 2.2.53 */
+ k->service_tkt_lifetime = 0; /* Need to find somewhere to store this, and query in KDC too */
+ k->user_tkt_lifetime = 0; /* Need to find somewhere to store this, and query in KDC too */
+ k->user_tkt_renewaltime = 0; /* Need to find somewhere to store this, and query in KDC too */
+ k->clock_skew = krb5_get_max_time_skew(smb_krb5_context->krb5_context);
+ talloc_free(smb_krb5_context);
+ return NT_STATUS_OK;
+ }
+ default:
+ talloc_free(r->out.info);
+ r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+/*
+ lsa_SetDomInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_SetDomainInformationPolicy(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetDomainInformationPolicy *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_TestCall
+*/
+static NTSTATUS dcesrv_lsa_TestCall(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_TestCall *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_CREDRWRITE
+*/
+static NTSTATUS dcesrv_lsa_CREDRWRITE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRWRITE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRREAD
+*/
+static NTSTATUS dcesrv_lsa_CREDRREAD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRREAD *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRENUMERATE
+*/
+static NTSTATUS dcesrv_lsa_CREDRENUMERATE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRENUMERATE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRWRITEDOMAINCREDENTIALS
+*/
+static NTSTATUS dcesrv_lsa_CREDRWRITEDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRWRITEDOMAINCREDENTIALS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRREADDOMAINCREDENTIALS
+*/
+static NTSTATUS dcesrv_lsa_CREDRREADDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRREADDOMAINCREDENTIALS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRDELETE
+*/
+static NTSTATUS dcesrv_lsa_CREDRDELETE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRDELETE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRGETTARGETINFO
+*/
+static NTSTATUS dcesrv_lsa_CREDRGETTARGETINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRGETTARGETINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRPROFILELOADED
+*/
+static NTSTATUS dcesrv_lsa_CREDRPROFILELOADED(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRPROFILELOADED *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRGETSESSIONTYPES
+*/
+static NTSTATUS dcesrv_lsa_CREDRGETSESSIONTYPES(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRGETSESSIONTYPES *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARREGISTERAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARREGISTERAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARGENAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARGENAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARGENAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARUNREGISTERAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARUNREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARUNREGISTERAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_lsaRQueryForestTrustInformation
+*/
+static NTSTATUS dcesrv_lsa_lsaRQueryForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_lsaRQueryForestTrustInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARSETFORESTTRUSTINFORMATION
+*/
+static NTSTATUS dcesrv_lsa_LSARSETFORESTTRUSTINFORMATION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARSETFORESTTRUSTINFORMATION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRRENAME
+*/
+static NTSTATUS dcesrv_lsa_CREDRRENAME(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRRENAME *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ lsa_LSAROPENPOLICYSCE
+*/
+static NTSTATUS dcesrv_lsa_LSAROPENPOLICYSCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSAROPENPOLICYSCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTREGISTERSECURITYEVENTSOURCE
+*/
+static NTSTATUS dcesrv_lsa_LSARADTREGISTERSECURITYEVENTSOURCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTREGISTERSECURITYEVENTSOURCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE
+*/
+static NTSTATUS dcesrv_lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTREPORTSECURITYEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARADTREPORTSECURITYEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTREPORTSECURITYEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_lsa_s.c"
+
+
+
+/*****************************************
+NOTE! The remaining calls below were
+removed in w2k3, so the DCESRV_FAULT()
+replies are the correct implementation. Do
+not try and fill these in with anything else
+******************************************/
+
+/*
+ dssetup_DsRoleDnsNameToFlatName
+*/
+static WERROR dcesrv_dssetup_DsRoleDnsNameToFlatName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDnsNameToFlatName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDcAsDc
+*/
+static WERROR dcesrv_dssetup_DsRoleDcAsDc(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDcAsDc *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDcAsReplica
+*/
+static WERROR dcesrv_dssetup_DsRoleDcAsReplica(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDcAsReplica *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDemoteDc
+*/
+static WERROR dcesrv_dssetup_DsRoleDemoteDc(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDemoteDc *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleGetDcOperationProgress
+*/
+static WERROR dcesrv_dssetup_DsRoleGetDcOperationProgress(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetDcOperationProgress *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleGetDcOperationResults
+*/
+static WERROR dcesrv_dssetup_DsRoleGetDcOperationResults(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetDcOperationResults *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleCancel
+*/
+static WERROR dcesrv_dssetup_DsRoleCancel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleCancel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleServerSaveStateForUpgrade
+*/
+static WERROR dcesrv_dssetup_DsRoleServerSaveStateForUpgrade(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleServerSaveStateForUpgrade *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleUpgradeDownlevelServer
+*/
+static WERROR dcesrv_dssetup_DsRoleUpgradeDownlevelServer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleUpgradeDownlevelServer *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleAbortDownlevelServerUpgrade
+*/
+static WERROR dcesrv_dssetup_DsRoleAbortDownlevelServerUpgrade(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleAbortDownlevelServerUpgrade *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_dssetup_s.c"
+
+NTSTATUS dcerpc_server_lsa_init(void)
+{
+ NTSTATUS ret;
+
+ ret = dcerpc_server_dssetup_init();
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ ret = dcerpc_server_lsarpc_init();
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ return ret;
+}
diff --git a/source4/rpc_server/lsa/lsa.h b/source4/rpc_server/lsa/lsa.h
new file mode 100644
index 0000000000..b7c41486a2
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa.h
@@ -0,0 +1,68 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/secrets.h"
+#include "util/util_ldb.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "param/param.h"
+
+/*
+ state associated with a lsa_OpenPolicy() operation
+*/
+struct lsa_policy_state {
+ struct dcesrv_handle *handle;
+ struct ldb_context *sam_ldb;
+ uint32_t access_mask;
+ struct ldb_dn *domain_dn;
+ struct ldb_dn *forest_dn;
+ struct ldb_dn *builtin_dn;
+ struct ldb_dn *system_dn;
+ const char *domain_name;
+ const char *domain_dns;
+ const char *forest_dns;
+ struct dom_sid *domain_sid;
+ struct GUID domain_guid;
+ struct dom_sid *builtin_sid;
+ struct dom_sid *nt_authority_sid;
+ struct dom_sid *creator_owner_domain_sid;
+ struct dom_sid *world_domain_sid;
+ int mixed_domain;
+};
+
+enum lsa_handle {
+ LSA_HANDLE_POLICY,
+ LSA_HANDLE_ACCOUNT,
+ LSA_HANDLE_SECRET,
+ LSA_HANDLE_TRUSTED_DOMAIN
+};
+
+#include "rpc_server/lsa/proto.h"
+
diff --git a/source4/rpc_server/lsa/lsa_init.c b/source4/rpc_server/lsa/lsa_init.c
new file mode 100644
index 0000000000..0dc21fd9c5
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa_init.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "rpc_server/lsa/lsa.h"
+
+NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state **_state)
+{
+ struct lsa_policy_state *state;
+ struct ldb_dn *partitions_basedn;
+ struct ldb_result *dom_res;
+ const char *dom_attrs[] = {
+ "objectSid",
+ "objectGUID",
+ "nTMixedDomain",
+ "fSMORoleOwner",
+ NULL
+ };
+ struct ldb_result *ref_res;
+ struct ldb_result *forest_ref_res;
+ const char *ref_attrs[] = {
+ "nETBIOSName",
+ "dnsRoot",
+ NULL
+ };
+ int ret;
+
+ state = talloc(mem_ctx, struct lsa_policy_state);
+ if (!state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the sam database is accessible */
+ state->sam_ldb = samdb_connect(state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (state->sam_ldb == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
+
+ /* work out the domain_dn - useful for so many calls its worth
+ fetching here */
+ state->domain_dn = samdb_base_dn(state->sam_ldb);
+ if (!state->domain_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* work out the forest root_dn - useful for so many calls its worth
+ fetching here */
+ state->forest_dn = samdb_root_dn(state->sam_ldb);
+ if (!state->forest_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(state->sam_ldb, state->domain_dn, LDB_SCOPE_BASE, NULL, dom_attrs, &dom_res);
+
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ talloc_steal(mem_ctx, dom_res);
+ if (dom_res->count != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_sid = samdb_result_dom_sid(state, dom_res->msgs[0], "objectSid");
+ if (!state->domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_guid = samdb_result_guid(dom_res->msgs[0], "objectGUID");
+ if (!state->domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->mixed_domain = ldb_msg_find_attr_as_uint(dom_res->msgs[0], "nTMixedDomain", 0);
+
+ talloc_free(dom_res);
+
+ ret = ldb_search_exp_fmt(state->sam_ldb, state, &ref_res,
+ partitions_basedn, LDB_SCOPE_SUBTREE, ref_attrs,
+ "(&(objectclass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(state->domain_dn));
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ref_res);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ if (ref_res->count != 1) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_name = ldb_msg_find_attr_as_string(ref_res->msgs[0], "nETBIOSName", NULL);
+ if (!state->domain_name) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ talloc_steal(state, state->domain_name);
+
+ state->domain_dns = ldb_msg_find_attr_as_string(ref_res->msgs[0], "dnsRoot", NULL);
+ if (!state->domain_dns) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ talloc_steal(state, state->domain_dns);
+
+ talloc_free(ref_res);
+
+ ret = ldb_search_exp_fmt(state->sam_ldb, state, &forest_ref_res,
+ partitions_basedn, LDB_SCOPE_SUBTREE, ref_attrs,
+ "(&(objectclass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(state->forest_dn));
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(forest_ref_res);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ if (forest_ref_res->count != 1) {
+ talloc_free(forest_ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->forest_dns = ldb_msg_find_attr_as_string(forest_ref_res->msgs[0], "dnsRoot", NULL);
+ if (!state->forest_dns) {
+ talloc_free(forest_ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ talloc_steal(state, state->forest_dns);
+
+ talloc_free(forest_ref_res);
+
+ /* work out the builtin_dn - useful for so many calls its worth
+ fetching here */
+ state->builtin_dn = samdb_search_dn(state->sam_ldb, state, state->domain_dn, "(objectClass=builtinDomain)");
+ if (!state->builtin_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* work out the system_dn - useful for so many calls its worth
+ fetching here */
+ state->system_dn = samdb_search_dn(state->sam_ldb, state,
+ state->domain_dn, "(&(objectClass=container)(cn=System))");
+ if (!state->system_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->builtin_sid = dom_sid_parse_talloc(state, SID_BUILTIN);
+ if (!state->builtin_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->nt_authority_sid = dom_sid_parse_talloc(state, SID_NT_AUTHORITY);
+ if (!state->nt_authority_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->creator_owner_domain_sid = dom_sid_parse_talloc(state, SID_CREATOR_OWNER_DOMAIN);
+ if (!state->creator_owner_domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->world_domain_sid = dom_sid_parse_talloc(state, SID_WORLD_DOMAIN);
+ if (!state->world_domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ *_state = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenPolicy2
+*/
+NTSTATUS dcesrv_lsa_OpenPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenPolicy2 *r)
+{
+ NTSTATUS status;
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *handle;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_POLICY);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, state);
+
+ state->access_mask = r->in.access_mask;
+ state->handle = handle;
+ *r->out.handle = handle->wire_handle;
+
+ /* note that we have completely ignored the attr element of
+ the OpenPolicy. As far as I can tell, this is what w2k3
+ does */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenPolicy
+ a wrapper around lsa_OpenPolicy2
+*/
+NTSTATUS dcesrv_lsa_OpenPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenPolicy *r)
+{
+ struct lsa_OpenPolicy2 r2;
+
+ r2.in.system_name = NULL;
+ r2.in.attr = r->in.attr;
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.handle = r->out.handle;
+
+ return dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &r2);
+}
+
+
diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
new file mode 100644
index 0000000000..30bceb8139
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -0,0 +1,932 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "rpc_server/lsa/lsa.h"
+
+static const struct {
+ const char *domain;
+ const char *name;
+ const char *sid;
+ int rtype;
+} well_known[] = {
+ {
+ .name = "EVERYONE",
+ .sid = SID_WORLD,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .name = "CREATOR OWNER",
+ .sid = SID_CREATOR_OWNER,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .name = "CREATOR GROUP",
+ .sid = SID_CREATOR_GROUP,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Dialup",
+ .sid = SID_NT_DIALUP,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Network",
+ .sid = SID_NT_NETWORK,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Batch",
+ .sid = SID_NT_BATCH,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Interactive",
+ .sid = SID_NT_INTERACTIVE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Service",
+ .sid = SID_NT_SERVICE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "ANONYMOUS LOGON",
+ .sid = SID_NT_ANONYMOUS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Proxy",
+ .sid = SID_NT_PROXY,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "ServerLogon",
+ .sid = SID_NT_ENTERPRISE_DCS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Self",
+ .sid = SID_NT_SELF,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Authenticated Users",
+ .sid = SID_NT_AUTHENTICATED_USERS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Restricted",
+ .sid = SID_NT_RESTRICTED,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Termainal Server User",
+ .sid = SID_NT_TERMINAL_SERVER_USERS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Remote Interactive Logon",
+ .sid = SID_NT_REMOTE_INTERACTIVE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "This Organization",
+ .sid = SID_NT_THIS_ORGANISATION,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "SYSTEM",
+ .sid = SID_NT_SYSTEM,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Local Service",
+ .sid = SID_NT_LOCAL_SERVICE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Network Service",
+ .sid = SID_NT_NETWORK_SERVICE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .sid = NULL,
+ }
+};
+
+static NTSTATUS lookup_well_known_names(TALLOC_CTX *mem_ctx, const char *domain,
+ const char *name, const char **authority_name,
+ struct dom_sid **sid, uint32_t *rtype)
+{
+ int i;
+ for (i=0; well_known[i].sid; i++) {
+ if (domain) {
+ if (strcasecmp_m(domain, well_known[i].domain) == 0
+ && strcasecmp_m(name, well_known[i].name) == 0) {
+ *authority_name = well_known[i].domain;
+ *sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
+ *rtype = well_known[i].rtype;
+ return NT_STATUS_OK;
+ }
+ } else {
+ if (strcasecmp_m(name, well_known[i].name) == 0) {
+ *authority_name = well_known[i].domain;
+ *sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
+ *rtype = well_known[i].rtype;
+ return NT_STATUS_OK;
+ }
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+static NTSTATUS lookup_well_known_sids(TALLOC_CTX *mem_ctx,
+ const char *sid_str, const char **authority_name,
+ const char **name, uint32_t *rtype)
+{
+ int i;
+ for (i=0; well_known[i].sid; i++) {
+ if (strcasecmp_m(sid_str, well_known[i].sid) == 0) {
+ *authority_name = well_known[i].domain;
+ *name = well_known[i].name;
+ *rtype = well_known[i].rtype;
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+/*
+ lookup a SID for 1 name
+*/
+static NTSTATUS dcesrv_lsa_lookup_name(struct event_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ const char *name, const char **authority_name,
+ struct dom_sid **sid, enum lsa_SidType *rtype)
+{
+ int ret, atype, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", "sAMAccountType", NULL};
+ const char *p;
+ const char *domain;
+ const char *username;
+ struct ldb_dn *domain_dn;
+ struct dom_sid *domain_sid;
+ NTSTATUS status;
+
+ p = strchr_m(name, '\\');
+ if (p != NULL) {
+ domain = talloc_strndup(mem_ctx, name, p-name);
+ if (!domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ username = p + 1;
+ } else if (strchr_m(name, '@')) {
+ status = crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, name, &domain, &username);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n", name, nt_errstr(status)));
+ return status;
+ }
+ } else {
+ domain = NULL;
+ username = name;
+ }
+
+ if (!domain) {
+ /* Look up table of well known names */
+ status = lookup_well_known_names(mem_ctx, NULL, username, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ if (strcasecmp_m(username, NAME_NT_AUTHORITY) == 0) {
+ *authority_name = NAME_NT_AUTHORITY;
+ *sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+ if (strcasecmp_m(username, NAME_BUILTIN) == 0) {
+ *authority_name = NAME_BUILTIN;
+ *sid = dom_sid_parse_talloc(mem_ctx, SID_BUILTIN);
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+ if (strcasecmp_m(username, state->domain_dns) == 0) {
+ *authority_name = state->domain_name;
+ *sid = state->domain_sid;
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+ if (strcasecmp_m(username, state->domain_name) == 0) {
+ *authority_name = state->domain_name;
+ *sid = state->domain_sid;
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ /* Perhaps this is a well known user? */
+ name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_NT_AUTHORITY, username);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Perhaps this is a BUILTIN user? */
+ name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_BUILTIN, username);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* OK, I give up - perhaps we need to assume the user is in our domain? */
+ name = talloc_asprintf(mem_ctx, "%s\\%s", state->domain_name, username);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return STATUS_SOME_UNMAPPED;
+ } else if (strcasecmp_m(domain, NAME_NT_AUTHORITY) == 0) {
+ if (!*username) {
+ *authority_name = NAME_NT_AUTHORITY;
+ *sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ /* Look up table of well known names */
+ return lookup_well_known_names(mem_ctx, domain, username, authority_name,
+ sid, rtype);
+ } else if (strcasecmp_m(domain, NAME_BUILTIN) == 0) {
+ *authority_name = NAME_BUILTIN;
+ domain_dn = state->builtin_dn;
+ } else if (strcasecmp_m(domain, state->domain_dns) == 0) {
+ *authority_name = state->domain_name;
+ domain_dn = state->domain_dn;
+ } else if (strcasecmp_m(domain, state->domain_name) == 0) {
+ *authority_name = state->domain_name;
+ domain_dn = state->domain_dn;
+ } else {
+ /* Not local, need to ask winbind in future */
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ ret = gendb_search_dn(state->sam_ldb, mem_ctx, domain_dn, &res, attrs);
+ if (ret == 1) {
+ domain_sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ if (domain_sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!*username) {
+ *sid = domain_sid;
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
+ "(&(sAMAccountName=%s)(objectSid=*))",
+ ldb_binary_encode_string(mem_ctx, username));
+ if (ret == -1) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ for (i=0; i < ret; i++) {
+ *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ if (*sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ /* Check that this is in the domain */
+ if (!dom_sid_in_domain(domain_sid, *sid)) {
+ continue;
+ }
+
+ atype = samdb_result_uint(res[i], "sAMAccountType", 0);
+
+ *rtype = samdb_atype_map(atype);
+ if (*rtype == SID_NAME_UNKNOWN) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ /* need to check for an allocated sid */
+
+ return NT_STATUS_INVALID_SID;
+}
+
+
+/*
+ add to the lsa_RefDomainList for LookupSids and LookupNames
+*/
+static NTSTATUS dcesrv_lsa_authority_list(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ enum lsa_SidType rtype,
+ const char *authority_name,
+ struct dom_sid *sid,
+ struct lsa_RefDomainList *domains,
+ uint32_t *sid_index)
+{
+ struct dom_sid *authority_sid;
+ int i;
+
+ if (rtype != SID_NAME_DOMAIN) {
+ authority_sid = dom_sid_dup(mem_ctx, sid);
+ if (authority_sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ authority_sid->num_auths--;
+ } else {
+ authority_sid = sid;
+ }
+
+ /* see if we've already done this authority name */
+ for (i=0;i<domains->count;i++) {
+ if (strcasecmp_m(authority_name, domains->domains[i].name.string) == 0) {
+ *sid_index = i;
+ return NT_STATUS_OK;
+ }
+ }
+
+ domains->domains = talloc_realloc(domains,
+ domains->domains,
+ struct lsa_DomainInfo,
+ domains->count+1);
+ if (domains->domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ domains->domains[i].name.string = authority_name;
+ domains->domains[i].sid = authority_sid;
+ domains->count++;
+ domains->max_size = LSA_REF_DOMAIN_LIST_MULTIPLIER * domains->count;
+ *sid_index = i;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lookup a name for 1 SID
+*/
+static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct dom_sid *sid, const char *sid_str,
+ const char **authority_name,
+ const char **name, enum lsa_SidType *rtype)
+{
+ NTSTATUS status;
+ int ret;
+ uint32_t atype;
+ struct ldb_message **res;
+ struct ldb_dn *domain_dn;
+ const char * const attrs[] = { "sAMAccountName", "sAMAccountType", "cn", NULL};
+
+ status = lookup_well_known_sids(mem_ctx, sid_str, authority_name, name, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (dom_sid_in_domain(state->domain_sid, sid)) {
+ *authority_name = state->domain_name;
+ domain_dn = state->domain_dn;
+ } else if (dom_sid_in_domain(state->builtin_sid, sid)) {
+ *authority_name = NAME_BUILTIN;
+ domain_dn = state->builtin_dn;
+ } else {
+ /* Not well known, our domain or built in */
+
+ /* In future, we must look at SID histories, and at trusted domains via winbind */
+
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
+ "objectSid=%s", ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (ret == 1) {
+ *name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
+ if (!*name) {
+ *name = ldb_msg_find_attr_as_string(res[0], "cn", NULL);
+ if (!*name) {
+ *name = talloc_strdup(mem_ctx, sid_str);
+ NT_STATUS_HAVE_NO_MEMORY(*name);
+ }
+ }
+
+ atype = samdb_result_uint(res[0], "sAMAccountType", 0);
+
+ *rtype = samdb_atype_map(atype);
+
+ return NT_STATUS_OK;
+ }
+
+ /* need to re-add a check for an allocated sid */
+
+ return NT_STATUS_NOT_FOUND;
+}
+
+
+/*
+ lsa_LookupSids2
+*/
+NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids2 *r)
+{
+ struct lsa_policy_state *state;
+ int i;
+ NTSTATUS status = NT_STATUS_OK;
+
+ r->out.domains = NULL;
+
+ status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
+ if (r->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.names = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
+ if (r->out.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.count = 0;
+
+ r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName2,
+ r->in.sids->num_sids);
+ if (r->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.sids->num_sids;i++) {
+ struct dom_sid *sid = r->in.sids->sids[i].sid;
+ char *sid_str = dom_sid_string(mem_ctx, sid);
+ const char *name, *authority_name;
+ enum lsa_SidType rtype;
+ uint32_t sid_index;
+ NTSTATUS status2;
+
+ r->out.names->count++;
+
+ r->out.names->names[i].sid_type = SID_NAME_UNKNOWN;
+ r->out.names->names[i].name.string = sid_str;
+ r->out.names->names[i].sid_index = 0xFFFFFFFF;
+ r->out.names->names[i].unknown = 0;
+
+ if (sid_str == NULL) {
+ r->out.names->names[i].name.string = "(SIDERROR)";
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ status2 = dcesrv_lsa_lookup_sid(state, mem_ctx, sid, sid_str,
+ &authority_name, &name, &rtype);
+ if (!NT_STATUS_IS_OK(status2)) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ /* set up the authority table */
+ status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype,
+ authority_name, sid,
+ r->out.domains, &sid_index);
+ if (!NT_STATUS_IS_OK(status2)) {
+ return status2;
+ }
+
+ r->out.names->names[i].sid_type = rtype;
+ r->out.names->names[i].name.string = name;
+ r->out.names->names[i].sid_index = sid_index;
+ r->out.names->names[i].unknown = 0;
+
+ (*r->out.count)++;
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.sids->num_sids) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupSids3
+
+ Identical to LookupSids2, but doesn't take a policy handle
+
+*/
+NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids3 *r)
+{
+ struct lsa_LookupSids2 r2;
+ struct lsa_OpenPolicy2 pol;
+ NTSTATUS status;
+ struct dcesrv_handle *h;
+
+ /* No policy handle on the wire, so make one up here */
+ r2.in.handle = talloc(mem_ctx, struct policy_handle);
+ if (!r2.in.handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pol.out.handle = r2.in.handle;
+ pol.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ pol.in.attr = NULL;
+ pol.in.system_name = NULL;
+ status = dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, r2.in.handle, LSA_HANDLE_POLICY);
+ talloc_steal(mem_ctx, h);
+
+ r2.in.sids = r->in.sids;
+ r2.in.names = r->in.names;
+ r2.in.level = r->in.level;
+ r2.in.count = r->in.count;
+ r2.in.unknown1 = r->in.unknown1;
+ r2.in.unknown2 = r->in.unknown2;
+ r2.out.count = r->out.count;
+ r2.out.names = r->out.names;
+
+ status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
+ if (dce_call->fault_code != 0) {
+ return status;
+ }
+
+ r->out.domains = r2.out.domains;
+ r->out.names = r2.out.names;
+ r->out.count = r2.out.count;
+
+ return status;
+}
+
+
+/*
+ lsa_LookupSids
+*/
+NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids *r)
+{
+ struct lsa_LookupSids2 r2;
+ NTSTATUS status;
+ int i;
+
+ r2.in.handle = r->in.handle;
+ r2.in.sids = r->in.sids;
+ r2.in.names = NULL;
+ r2.in.level = r->in.level;
+ r2.in.count = r->in.count;
+ r2.in.unknown1 = 0;
+ r2.in.unknown2 = 0;
+ r2.out.count = r->out.count;
+ r2.out.names = NULL;
+
+ status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
+ if (dce_call->fault_code != 0) {
+ return status;
+ }
+
+ r->out.domains = r2.out.domains;
+ if (!r2.out.names) {
+ r->out.names = NULL;
+ return status;
+ }
+
+ r->out.names = talloc(mem_ctx, struct lsa_TransNameArray);
+ if (r->out.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.names->count = r2.out.names->count;
+ r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName,
+ r->out.names->count);
+ if (r->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<r->out.names->count;i++) {
+ r->out.names->names[i].sid_type = r2.out.names->names[i].sid_type;
+ r->out.names->names[i].name.string = r2.out.names->names[i].name.string;
+ r->out.names->names[i].sid_index = r2.out.names->names[i].sid_index;
+ }
+
+ return status;
+}
+
+
+/*
+ lsa_LookupNames3
+*/
+NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames3 *r)
+{
+ struct lsa_policy_state *policy_state;
+ struct dcesrv_handle *policy_handle;
+ int i;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ r->out.domains = NULL;
+
+ r->out.domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
+ if (r->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
+ if (r->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.count = 0;
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid3,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.num_names;i++) {
+ const char *name = r->in.names[i].string;
+ const char *authority_name;
+ struct dom_sid *sid;
+ uint32_t sid_index;
+ enum lsa_SidType rtype;
+ NTSTATUS status2;
+
+ r->out.sids->count++;
+
+ r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN;
+ r->out.sids->sids[i].sid = NULL;
+ r->out.sids->sids[i].sid_index = 0xFFFFFFFF;
+ r->out.sids->sids[i].unknown = 0;
+
+ status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, policy_state, mem_ctx, name, &authority_name, &sid, &rtype);
+ if (!NT_STATUS_IS_OK(status2) || sid->num_auths == 0) {
+ continue;
+ }
+
+ status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype, authority_name,
+ sid, r->out.domains, &sid_index);
+ if (!NT_STATUS_IS_OK(status2)) {
+ return status2;
+ }
+
+ r->out.sids->sids[i].sid_type = rtype;
+ r->out.sids->sids[i].sid = sid;
+ r->out.sids->sids[i].sid_index = sid_index;
+ r->out.sids->sids[i].unknown = 0;
+
+ (*r->out.count)++;
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.num_names) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_LookupNames4
+
+ Identical to LookupNames3, but doesn't take a policy handle
+
+*/
+NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames4 *r)
+{
+ struct lsa_LookupNames3 r2;
+ struct lsa_OpenPolicy2 pol;
+ NTSTATUS status;
+ struct dcesrv_handle *h;
+
+ /* No policy handle on the wire, so make one up here */
+ r2.in.handle = talloc(mem_ctx, struct policy_handle);
+ if (!r2.in.handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pol.out.handle = r2.in.handle;
+ pol.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ pol.in.attr = NULL;
+ pol.in.system_name = NULL;
+ status = dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, r2.in.handle, LSA_HANDLE_POLICY);
+ talloc_steal(mem_ctx, h);
+
+ r2.in.num_names = r->in.num_names;
+ r2.in.names = r->in.names;
+ r2.in.sids = r->in.sids;
+ r2.in.count = r->in.count;
+ r2.in.unknown1 = r->in.unknown1;
+ r2.in.unknown2 = r->in.unknown2;
+ r2.out.domains = r->out.domains;
+ r2.out.sids = r->out.sids;
+ r2.out.count = r->out.count;
+
+ status = dcesrv_lsa_LookupNames3(dce_call, mem_ctx, &r2);
+ if (dce_call->fault_code != 0) {
+ return status;
+ }
+
+ r->out.domains = r2.out.domains;
+ r->out.sids = r2.out.sids;
+ r->out.count = r2.out.count;
+ return status;
+}
+
+/*
+ lsa_LookupNames2
+*/
+NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames2 *r)
+{
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h;
+ int i;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+
+ r->out.domains = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ r->out.domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
+ if (r->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
+ if (r->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.count = 0;
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid2,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.num_names;i++) {
+ const char *name = r->in.names[i].string;
+ const char *authority_name;
+ struct dom_sid *sid;
+ uint32_t rtype, sid_index;
+ NTSTATUS status2;
+
+ r->out.sids->count++;
+
+ r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN;
+ r->out.sids->sids[i].rid = 0xFFFFFFFF;
+ r->out.sids->sids[i].sid_index = 0xFFFFFFFF;
+ r->out.sids->sids[i].unknown = 0;
+
+ status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, state, mem_ctx, name,
+ &authority_name, &sid, &rtype);
+ if (!NT_STATUS_IS_OK(status2)) {
+ continue;
+ }
+
+ status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype, authority_name,
+ sid, r->out.domains, &sid_index);
+ if (!NT_STATUS_IS_OK(status2)) {
+ return status2;
+ }
+
+ r->out.sids->sids[i].sid_type = rtype;
+ r->out.sids->sids[i].rid = sid->sub_auths[sid->num_auths-1];
+ r->out.sids->sids[i].sid_index = sid_index;
+ r->out.sids->sids[i].unknown = 0;
+
+ (*r->out.count)++;
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.num_names) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_LookupNames
+*/
+NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames *r)
+{
+ struct lsa_LookupNames2 r2;
+ NTSTATUS status;
+ int i;
+
+ r2.in.handle = r->in.handle;
+ r2.in.num_names = r->in.num_names;
+ r2.in.names = r->in.names;
+ r2.in.sids = NULL;
+ r2.in.level = r->in.level;
+ r2.in.count = r->in.count;
+ r2.in.unknown1 = 0;
+ r2.in.unknown2 = 0;
+ r2.out.count = r->out.count;
+
+ status = dcesrv_lsa_LookupNames2(dce_call, mem_ctx, &r2);
+ if (dce_call->fault_code != 0) {
+ return status;
+ }
+
+ r->out.domains = r2.out.domains;
+ r->out.sids = talloc(mem_ctx, struct lsa_TransSidArray);
+ if (r->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.sids->count = r2.out.sids->count;
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid,
+ r->out.sids->count);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<r->out.sids->count;i++) {
+ r->out.sids->sids[i].sid_type = r2.out.sids->sids[i].sid_type;
+ r->out.sids->sids[i].rid = r2.out.sids->sids[i].rid;
+ r->out.sids->sids[i].sid_index = r2.out.sids->sids[i].sid_index;
+ }
+
+ return status;
+}
+
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
new file mode 100644
index 0000000000..6f4287f9d8
--- /dev/null
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -0,0 +1,1320 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the netlogon pipe
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2008
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "rpc_server/samr/proto.h"
+#include "util/util_ldb.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/gensec/schannel_state.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+
+struct server_pipe_state {
+ struct netr_Credential client_challenge;
+ struct netr_Credential server_challenge;
+};
+
+
+static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerReqChallenge *r)
+{
+ struct server_pipe_state *pipe_state = dce_call->context->private;
+
+ ZERO_STRUCTP(r->out.credentials);
+
+ /* destroyed on pipe shutdown */
+
+ if (pipe_state) {
+ talloc_free(pipe_state);
+ dce_call->context->private = NULL;
+ }
+
+ pipe_state = talloc(dce_call->context, struct server_pipe_state);
+ NT_STATUS_HAVE_NO_MEMORY(pipe_state);
+
+ pipe_state->client_challenge = *r->in.credentials;
+
+ generate_random_buffer(pipe_state->server_challenge.data,
+ sizeof(pipe_state->server_challenge.data));
+
+ *r->out.credentials = pipe_state->server_challenge;
+
+ dce_call->context->private = pipe_state;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate3 *r)
+{
+ struct server_pipe_state *pipe_state = dce_call->context->private;
+ struct creds_CredentialState *creds;
+ void *sam_ctx;
+ struct samr_Password *mach_pwd;
+ uint32_t user_account_control;
+ int num_records;
+ struct ldb_message **msgs;
+ NTSTATUS nt_status;
+ const char *attrs[] = {"unicodePwd", "userAccountControl",
+ "objectSid", NULL};
+
+ ZERO_STRUCTP(r->out.credentials);
+ *r->out.rid = 0;
+ *r->out.negotiate_flags = *r->in.negotiate_flags;
+
+ if (!pipe_state) {
+ DEBUG(1, ("No challenge requested by client, cannot authenticate\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ /* pull the user attributes */
+ num_records = gendb_search(sam_ctx, mem_ctx, NULL, &msgs, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account_name);
+
+ if (num_records == 0) {
+ DEBUG(3,("Couldn't find user [%s] in samdb.\n",
+ r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (num_records > 1) {
+ DEBUG(0,("Found %d records matching user [%s]\n", num_records, r->in.account_name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+
+ user_account_control = ldb_msg_find_attr_as_uint(msgs[0], "userAccountControl", 0);
+
+ if (user_account_control & UF_ACCOUNTDISABLE) {
+ DEBUG(1, ("Account [%s] is disabled\n", r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (r->in.secure_channel_type == SEC_CHAN_WKSTA) {
+ if (!(user_account_control & UF_WORKSTATION_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a workstation secure channel, but is not a workstation (member server) acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_DOMAIN) {
+ if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a trusted domain secure channel, but is not a trusted domain: acb flags: 0x%x\n", user_account_control));
+
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_BDC) {
+ if (!(user_account_control & UF_SERVER_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a server secure channel, but is not a server (domain controller): acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
+ r->in.secure_channel_type));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *r->out.rid = samdb_result_rid_from_sid(mem_ctx, msgs[0],
+ "objectSid", 0);
+
+ mach_pwd = samdb_result_hash(mem_ctx, msgs[0], "unicodePwd");
+ if (mach_pwd == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ creds = talloc(mem_ctx, struct creds_CredentialState);
+ NT_STATUS_HAVE_NO_MEMORY(creds);
+
+ creds_server_init(creds, &pipe_state->client_challenge,
+ &pipe_state->server_challenge, mach_pwd,
+ r->out.credentials,
+ *r->in.negotiate_flags);
+
+ if (!creds_server_check(creds, r->in.credentials)) {
+ talloc_free(creds);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ creds->account_name = talloc_steal(creds, r->in.account_name);
+
+ creds->computer_name = talloc_steal(creds, r->in.computer_name);
+ creds->domain = talloc_strdup(creds, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+
+ creds->secure_channel_type = r->in.secure_channel_type;
+
+ creds->sid = samdb_result_dom_sid(creds, msgs[0], "objectSid");
+
+
+ /* remember this session key state */
+ nt_status = schannel_store_session_key(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, creds);
+
+ return nt_status;
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate *r)
+{
+ struct netr_ServerAuthenticate3 r3;
+ uint32_t rid = 0;
+ /* TODO:
+ * negotiate_flags is used as an [in] parameter
+ * so it need to be initialised.
+ *
+ * (I think ... = 0; seems wrong here --metze)
+ */
+ uint32_t negotiate_flags = 0;
+
+ r3.in.server_name = r->in.server_name;
+ r3.in.account_name = r->in.account_name;
+ r3.in.secure_channel_type = r->in.secure_channel_type;
+ r3.in.computer_name = r->in.computer_name;
+ r3.in.credentials = r->in.credentials;
+ r3.out.credentials = r->out.credentials;
+ r3.in.negotiate_flags = &negotiate_flags;
+ r3.out.negotiate_flags = &negotiate_flags;
+ r3.out.rid = &rid;
+
+ return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate2 *r)
+{
+ struct netr_ServerAuthenticate3 r3;
+ uint32_t rid = 0;
+
+ r3.in.server_name = r->in.server_name;
+ r3.in.account_name = r->in.account_name;
+ r3.in.secure_channel_type = r->in.secure_channel_type;
+ r3.in.computer_name = r->in.computer_name;
+ r3.in.credentials = r->in.credentials;
+ r3.out.credentials = r->out.credentials;
+ r3.in.negotiate_flags = r->in.negotiate_flags;
+ r3.out.negotiate_flags = r->out.negotiate_flags;
+ r3.out.rid = &rid;
+
+ return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
+}
+
+/*
+ Validate an incoming authenticator against the credentials for the remote machine.
+
+ The credentials are (re)read and from the schannel database, and
+ written back after the caclulations are performed.
+
+ The creds_out parameter (if not NULL) returns the credentials, if
+ the caller needs some of that information.
+
+*/
+static NTSTATUS dcesrv_netr_creds_server_step_check(struct event_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ TALLOC_CTX *mem_ctx,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator,
+ struct creds_CredentialState **creds_out)
+{
+ struct creds_CredentialState *creds;
+ NTSTATUS nt_status;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = schannel_db_connect(mem_ctx, event_ctx, lp_ctx);
+ if (!ldb) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != 0) {
+ talloc_free(ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* Because this is a shared structure (even across
+ * disconnects) we must update the database every time we
+ * update the structure */
+
+ nt_status = schannel_fetch_session_key_ldb(ldb, ldb, computer_name,
+ lp_workgroup(lp_ctx),
+ &creds);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = creds_server_step_check(creds,
+ received_authenticator,
+ return_authenticator);
+ }
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = schannel_store_session_key_ldb(ldb, ldb, creds);
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ ldb_transaction_commit(ldb);
+ if (creds_out) {
+ *creds_out = creds;
+ talloc_steal(mem_ctx, creds);
+ }
+ } else {
+ ldb_transaction_cancel(ldb);
+ }
+ talloc_free(ldb);
+ return nt_status;
+}
+
+/*
+ Change the machine account password for the currently connected
+ client. Supplies only the NT#.
+*/
+
+static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordSet *r)
+{
+ struct creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ &r->in.credential, &r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ creds_des_decrypt(creds, &r->in.new_password);
+
+ /* Using the sid for the account as the key, set the password */
+ nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
+ creds->sid,
+ NULL, /* Don't have plaintext */
+ NULL, &r->in.new_password,
+ false, /* This is not considered a password change */
+ NULL, NULL);
+ return nt_status;
+}
+
+/*
+ Change the machine account password for the currently connected
+ client. Supplies new plaintext.
+*/
+static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordSet2 *r)
+{
+ struct creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+ char new_pass[512];
+ uint32_t new_pass_len;
+ bool ret;
+
+ struct samr_CryptPassword password_buf;
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ &r->in.credential, &r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ memcpy(password_buf.data, r->in.new_password.data, 512);
+ SIVAL(password_buf.data,512,r->in.new_password.length);
+ creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ ret = decode_pw_buffer(password_buf.data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_UNICODE);
+ if (!ret) {
+ DEBUG(3,("netr_ServerPasswordSet2: failed to decode password buffer\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Using the sid for the account as the key, set the password */
+ nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
+ creds->sid,
+ new_pass, /* we have plaintext */
+ NULL, NULL,
+ false, /* This is not considered a password change */
+ NULL, NULL);
+ return nt_status;
+}
+
+
+/*
+ netr_LogonUasLogon
+*/
+static WERROR dcesrv_netr_LogonUasLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonUasLogon *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonUasLogoff
+*/
+static WERROR dcesrv_netr_LogonUasLogoff(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonUasLogoff *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonSamLogon_base
+
+ This version of the function allows other wrappers to say 'do not check the credentials'
+
+ We can't do the traditional 'wrapping' format completly, as this function must only run under schannel
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonEx *r, struct creds_CredentialState *creds)
+{
+ struct auth_context *auth_context;
+ struct auth_usersupplied_info *user_info;
+ struct auth_serversupplied_info *server_info;
+ NTSTATUS nt_status;
+ static const char zeros[16];
+ struct netr_SamBaseInfo *sam;
+ struct netr_SamInfo2 *sam2;
+ struct netr_SamInfo3 *sam3;
+ struct netr_SamInfo6 *sam6;
+
+ user_info = talloc(mem_ctx, struct auth_usersupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(user_info);
+
+ user_info->flags = 0;
+ user_info->mapped_state = false;
+ user_info->remote_host = NULL;
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ r->in.logon.password->lmpassword.hash,
+ sizeof(r->in.logon.password->lmpassword.hash));
+ creds_arcfour_crypt(creds,
+ r->in.logon.password->ntpassword.hash,
+ sizeof(r->in.logon.password->ntpassword.hash));
+ } else {
+ creds_des_decrypt(creds, &r->in.logon.password->lmpassword);
+ creds_des_decrypt(creds, &r->in.logon.password->ntpassword);
+ }
+
+ /* TODO: we need to deny anonymous access here */
+ nt_status = auth_context_create(mem_ctx,
+ dce_call->event_ctx, dce_call->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &auth_context);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ user_info->logon_parameters = r->in.logon.password->identity_info.parameter_control;
+ user_info->client.account_name = r->in.logon.password->identity_info.account_name.string;
+ user_info->client.domain_name = r->in.logon.password->identity_info.domain_name.string;
+ user_info->workstation_name = r->in.logon.password->identity_info.workstation.string;
+
+ user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
+ user_info->password_state = AUTH_PASSWORD_HASH;
+
+ user_info->password.hash.lanman = talloc(user_info, struct samr_Password);
+ NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.lanman);
+ *user_info->password.hash.lanman = r->in.logon.password->lmpassword;
+
+ user_info->password.hash.nt = talloc(user_info, struct samr_Password);
+ NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.nt);
+ *user_info->password.hash.nt = r->in.logon.password->ntpassword;
+
+ break;
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+
+ /* TODO: we need to deny anonymous access here */
+ nt_status = auth_context_create(mem_ctx,
+ dce_call->event_ctx, dce_call->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &auth_context);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = auth_context_set_challenge(auth_context, r->in.logon.network->challenge, "netr_LogonSamLogonWithFlags");
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ user_info->logon_parameters = r->in.logon.network->identity_info.parameter_control;
+ user_info->client.account_name = r->in.logon.network->identity_info.account_name.string;
+ user_info->client.domain_name = r->in.logon.network->identity_info.domain_name.string;
+ user_info->workstation_name = r->in.logon.network->identity_info.workstation.string;
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = data_blob_talloc(mem_ctx, r->in.logon.network->lm.data, r->in.logon.network->lm.length);
+ user_info->password.response.nt = data_blob_talloc(mem_ctx, r->in.logon.network->nt.data, r->in.logon.network->nt.length);
+
+ break;
+
+
+ case NetlogonGenericInformation:
+ {
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ r->in.logon.generic->data, r->in.logon.generic->length);
+ } else {
+ /* Using DES to verify kerberos tickets makes no sense */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strcmp(r->in.logon.generic->package_name.string, "Kerberos") == 0) {
+ NTSTATUS status;
+ struct server_id *kdc;
+ struct kdc_check_generic_kerberos check;
+ struct netr_GenericInfo2 *generic = talloc_zero(mem_ctx, struct netr_GenericInfo2);
+ NT_STATUS_HAVE_NO_MEMORY(generic);
+ r->out.authoritative = 1;
+
+ /* TODO: Describe and deal with these flags */
+ r->out.flags = 0;
+
+ r->out.validation.generic = generic;
+
+ kdc = irpc_servers_byname(dce_call->msg_ctx, mem_ctx, "kdc_server");
+ if ((kdc == NULL) || (kdc[0].id == 0)) {
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ check.in.generic_request =
+ data_blob_const(r->in.logon.generic->data,
+ r->in.logon.generic->length);
+
+ status = irpc_call(dce_call->msg_ctx, kdc[0],
+ &ndr_table_irpc, NDR_KDC_CHECK_GENERIC_KERBEROS,
+ &check, mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ generic->length = check.out.generic_reply.length;
+ generic->data = check.out.generic_reply.data;
+ return NT_STATUS_OK;
+ }
+
+ /* Until we get an implemetnation of these other packages */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = auth_check_password(auth_context, mem_ctx, user_info, &server_info);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = auth_convert_server_info_sambaseinfo(mem_ctx, server_info, &sam);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ /* It appears that level 6 is not individually encrypted */
+ if ((r->in.validation_level != 6) &&
+ memcmp(sam->key.key, zeros, sizeof(sam->key.key)) != 0) {
+ /* This key is sent unencrypted without the ARCFOUR flag set */
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ sam->key.key,
+ sizeof(sam->key.key));
+ }
+ }
+
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ /* It appears that level 6 is not individually encrypted */
+ if ((r->in.validation_level != 6) &&
+ memcmp(sam->LMSessKey.key, zeros, sizeof(sam->LMSessKey.key)) != 0) {
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ sam->LMSessKey.key,
+ sizeof(sam->LMSessKey.key));
+ } else {
+ creds_des_encrypt_LMKey(creds,
+ &sam->LMSessKey);
+ }
+ }
+
+ switch (r->in.validation_level) {
+ case 2:
+ sam2 = talloc_zero(mem_ctx, struct netr_SamInfo2);
+ NT_STATUS_HAVE_NO_MEMORY(sam2);
+ sam2->base = *sam;
+ r->out.validation.sam2 = sam2;
+ break;
+
+ case 3:
+ sam3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ NT_STATUS_HAVE_NO_MEMORY(sam3);
+ sam3->base = *sam;
+ r->out.validation.sam3 = sam3;
+ break;
+
+ case 6:
+ sam6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
+ NT_STATUS_HAVE_NO_MEMORY(sam6);
+ sam6->base = *sam;
+ sam6->forest.string = lp_realm(dce_call->conn->dce_ctx->lp_ctx);
+ sam6->principle.string = talloc_asprintf(mem_ctx, "%s@%s",
+ sam->account_name.string, sam6->forest.string);
+ NT_STATUS_HAVE_NO_MEMORY(sam6->principle.string);
+ r->out.validation.sam6 = sam6;
+ break;
+
+ default:
+ break;
+ }
+
+ r->out.authoritative = 1;
+
+ /* TODO: Describe and deal with these flags */
+ r->out.flags = 0;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonEx *r)
+{
+ NTSTATUS nt_status;
+ struct creds_CredentialState *creds;
+ nt_status = schannel_fetch_session_key(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, r->in.computer_name, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx), &creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (!dce_call->conn->auth_state.auth_info ||
+ dce_call->conn->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return dcesrv_netr_LogonSamLogon_base(dce_call, mem_ctx, r, creds);
+}
+
+/*
+ netr_LogonSamLogonWithFlags
+
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogonWithFlags(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonWithFlags *r)
+{
+ NTSTATUS nt_status;
+ struct creds_CredentialState *creds;
+ struct netr_LogonSamLogonEx r2;
+
+ struct netr_Authenticator *return_authenticator;
+
+ return_authenticator = talloc(mem_ctx, struct netr_Authenticator);
+ NT_STATUS_HAVE_NO_MEMORY(return_authenticator);
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ r->in.credential, return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.computer_name = r->in.computer_name;
+ r2.in.logon_level = r->in.logon_level;
+ r2.in.logon = r->in.logon;
+ r2.in.validation_level = r->in.validation_level;
+ r2.in.flags = r->in.flags;
+
+ nt_status = dcesrv_netr_LogonSamLogon_base(dce_call, mem_ctx, &r2, creds);
+
+ r->out.return_authenticator = return_authenticator;
+ r->out.validation = r2.out.validation;
+ r->out.authoritative = r2.out.authoritative;
+ r->out.flags = r2.out.flags;
+
+ return nt_status;
+}
+
+/*
+ netr_LogonSamLogon
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogon *r)
+{
+ struct netr_LogonSamLogonWithFlags r2;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.computer_name = r->in.computer_name;
+ r2.in.credential = r->in.credential;
+ r2.in.return_authenticator = r->in.return_authenticator;
+ r2.in.logon_level = r->in.logon_level;
+ r2.in.logon = r->in.logon;
+ r2.in.validation_level = r->in.validation_level;
+ r2.in.flags = 0;
+
+ status = dcesrv_netr_LogonSamLogonWithFlags(dce_call, mem_ctx, &r2);
+
+ r->out.return_authenticator = r2.out.return_authenticator;
+ r->out.validation = r2.out.validation;
+ r->out.authoritative = r2.out.authoritative;
+
+ return status;
+}
+
+
+/*
+ netr_LogonSamLogoff
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogoff(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogoff *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ netr_DatabaseDeltas
+*/
+static NTSTATUS dcesrv_netr_DatabaseDeltas(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseDeltas *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DatabaseSync
+*/
+static NTSTATUS dcesrv_netr_DatabaseSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseSync *r)
+{
+ /* win2k3 native mode returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_AccountDeltas
+*/
+static NTSTATUS dcesrv_netr_AccountDeltas(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_AccountDeltas *r)
+{
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_AccountSync
+*/
+static NTSTATUS dcesrv_netr_AccountSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_AccountSync *r)
+{
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_GetDcName
+*/
+static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetDcName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonControl
+*/
+static WERROR dcesrv_netr_LogonControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_GetAnyDCName
+*/
+static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetAnyDCName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonControl2
+*/
+static WERROR dcesrv_netr_LogonControl2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DatabaseSync2
+*/
+static NTSTATUS dcesrv_netr_DatabaseSync2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseSync2 *r)
+{
+ /* win2k3 native mode returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_DatabaseRedo
+*/
+static NTSTATUS dcesrv_netr_DatabaseRedo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseRedo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonControl2Ex
+*/
+static WERROR dcesrv_netr_LogonControl2Ex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2Ex *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NetrEnumerateTurstedDomains
+*/
+static WERROR dcesrv_netr_NetrEnumerateTrustedDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrEnumerateTrustedDomains *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONDUMMYROUTINE1
+*/
+static WERROR dcesrv_netr_NETRLOGONDUMMYROUTINE1(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONDUMMYROUTINE1 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONSETSERVICEBITS
+*/
+static WERROR dcesrv_netr_NETRLOGONSETSERVICEBITS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONSETSERVICEBITS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonGetTrustRid
+*/
+static WERROR dcesrv_netr_LogonGetTrustRid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetTrustRid *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONCOMPUTESERVERDIGEST
+*/
+static WERROR dcesrv_netr_NETRLOGONCOMPUTESERVERDIGEST(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONCOMPUTESERVERDIGEST *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONCOMPUTECLIENTDIGEST
+*/
+static WERROR dcesrv_netr_NETRLOGONCOMPUTECLIENTDIGEST(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONCOMPUTECLIENTDIGEST *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ netr_DsRGetSiteName
+*/
+static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetSiteName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ fill in a netr_DomainTrustInfo from a ldb search result
+*/
+static NTSTATUS fill_domain_trust_info(TALLOC_CTX *mem_ctx,
+ struct ldb_message *res,
+ struct ldb_message *ref_res,
+ struct netr_DomainTrustInfo *info,
+ bool is_local)
+{
+ ZERO_STRUCTP(info);
+
+ if (is_local) {
+ info->domainname.string = samdb_result_string(ref_res, "nETBIOSName", NULL);
+ info->fulldomainname.string = samdb_result_string(ref_res, "dnsRoot", NULL);
+ info->forest.string = NULL;
+ info->guid = samdb_result_guid(res, "objectGUID");
+ info->sid = samdb_result_dom_sid(mem_ctx, res, "objectSid");
+ } else {
+ info->domainname.string = samdb_result_string(res, "flatName", NULL);
+ info->fulldomainname.string = samdb_result_string(res, "trustPartner", NULL);
+ info->forest.string = NULL;
+ info->guid = samdb_result_guid(res, "objectGUID");
+ info->sid = samdb_result_dom_sid(mem_ctx, res, "securityIdentifier");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ netr_LogonGetDomainInfo
+ this is called as part of the ADS domain logon procedure.
+
+ It has an important role in convaying details about the client, such
+ as Operating System, Version, Service Pack etc.
+*/
+static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetDomainInfo *r)
+{
+ const char * const attrs[] = { "objectSid",
+ "objectGUID", "flatName", "securityIdentifier",
+ "trustPartner", NULL };
+ const char * const ref_attrs[] = { "nETBIOSName", "dnsRoot", NULL };
+ struct ldb_context *sam_ctx;
+ struct ldb_message **res1, **res2, **ref_res;
+ struct netr_DomainInfo1 *info1;
+ int ret, ret1, ret2, i;
+ NTSTATUS status;
+ struct ldb_dn *partitions_basedn;
+
+ const char *local_domain;
+
+ status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ r->in.credential,
+ r->out.return_authenticator,
+ NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ /* we need to do two searches. The first will pull our primary
+ domain and the second will pull any trusted domains. Our
+ primary domain is also a "trusted" domain, so we need to
+ put the primary domain into the lists of returned trusts as
+ well */
+ ret1 = gendb_search_dn(sam_ctx, mem_ctx, samdb_base_dn(sam_ctx), &res1, attrs);
+ if (ret1 != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* try and find the domain */
+ ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn,
+ &ref_res, ref_attrs,
+ "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(res1[0]->dn));
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ local_domain = samdb_result_string(ref_res[0], "nETBIOSName", NULL);
+
+ ret2 = gendb_search(sam_ctx, mem_ctx, NULL, &res2, attrs, "(objectClass=trustedDomain)");
+ if (ret2 == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ info1 = talloc(mem_ctx, struct netr_DomainInfo1);
+ NT_STATUS_HAVE_NO_MEMORY(info1);
+
+ ZERO_STRUCTP(info1);
+
+ info1->num_trusts = ret2 + 1;
+ info1->trusts = talloc_array(mem_ctx, struct netr_DomainTrustInfo,
+ info1->num_trusts);
+ NT_STATUS_HAVE_NO_MEMORY(info1->trusts);
+
+ status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->domaininfo, true);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ for (i=0;i<ret2;i++) {
+ status = fill_domain_trust_info(mem_ctx, res2[i], NULL, &info1->trusts[i], false);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->trusts[i], true);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.info.info1 = info1;
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ netr_ServerPasswordGet
+*/
+static WERROR dcesrv_netr_ServerPasswordGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONSENDTOSAM
+*/
+static WERROR dcesrv_netr_NETRLOGONSENDTOSAM(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONSENDTOSAM *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRAddressToSitenamesW
+*/
+static WERROR dcesrv_netr_DsRAddressToSitenamesW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRAddressToSitenamesW *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRGetDCNameEx2
+*/
+static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameEx2 *r)
+{
+ const char * const attrs[] = { "dnsDomain", "objectGUID", NULL };
+ void *sam_ctx;
+ struct ldb_message **res;
+ struct ldb_dn *domain_dn;
+ int ret;
+
+ ZERO_STRUCT(r->out);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ domain_dn = samdb_dns_domain_to_dn(sam_ctx, mem_ctx,
+ r->in.domain_name);
+ if (domain_dn == NULL) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ ret = gendb_search_dn(sam_ctx, mem_ctx, domain_dn, &res, attrs);
+ if (ret != 1) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ r->out.info = talloc(mem_ctx, struct netr_DsRGetDCNameInfo);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info);
+
+ /* TODO: - return real IP address
+ * - check all r->in.* parameters (server_unc is ignored by w2k3!)
+ */
+ r->out.info->dc_unc = talloc_asprintf(mem_ctx, "\\\\%s.%s",
+ lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx),
+ lp_realm(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->dc_unc);
+ r->out.info->dc_address = talloc_strdup(mem_ctx, "\\\\0.0.0.0");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->dc_address);
+ r->out.info->dc_address_type = DS_ADDRESS_TYPE_INET;
+ r->out.info->domain_guid = samdb_result_guid(res[0], "objectGUID");
+ r->out.info->domain_name = samdb_result_string(res[0], "dnsDomain", NULL);
+ r->out.info->forest_name = samdb_result_string(res[0], "dnsDomain", NULL);
+ r->out.info->dc_flags = DS_DNS_FOREST |
+ DS_DNS_DOMAIN |
+ DS_DNS_CONTROLLER |
+ DS_SERVER_WRITABLE |
+ DS_SERVER_CLOSEST |
+ DS_SERVER_TIMESERV |
+ DS_SERVER_KDC |
+ DS_SERVER_DS |
+ DS_SERVER_LDAP |
+ DS_SERVER_GC |
+ DS_SERVER_PDC;
+ r->out.info->dc_site_name = talloc_strdup(mem_ctx, "Default-First-Site-Name");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->dc_site_name);
+ r->out.info->client_site_name = talloc_strdup(mem_ctx, "Default-First-Site-Name");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->client_site_name);
+
+ return WERR_OK;
+}
+
+/*
+ netr_DsRGetDCNameEx
+*/
+static WERROR dcesrv_netr_DsRGetDCNameEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameEx *r)
+{
+ struct netr_DsRGetDCNameEx2 r2;
+ WERROR werr;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_unc = r->in.server_unc;
+ r2.in.client_account = NULL;
+ r2.in.mask = 0;
+ r2.in.domain_guid = r->in.domain_guid;
+ r2.in.domain_name = r->in.domain_name;
+ r2.in.site_name = r->in.site_name;
+ r2.in.flags = r->in.flags;
+ r2.out.info = NULL;
+
+ werr = dcesrv_netr_DsRGetDCNameEx2(dce_call, mem_ctx, &r2);
+
+ r->out.info = r2.out.info;
+
+ return werr;
+}
+
+/*
+ netr_DsRGetDCName
+*/
+static WERROR dcesrv_netr_DsRGetDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCName *r)
+{
+ struct netr_DsRGetDCNameEx2 r2;
+ WERROR werr;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_unc = r->in.server_unc;
+ r2.in.client_account = NULL;
+ r2.in.mask = 0;
+ r2.in.domain_name = r->in.domain_name;
+ r2.in.domain_guid = r->in.domain_guid;
+
+ r2.in.site_name = NULL; /* should fill in from site GUID */
+ r2.in.flags = r->in.flags;
+ r2.out.info = NULL;
+
+ werr = dcesrv_netr_DsRGetDCNameEx2(dce_call, mem_ctx, &r2);
+
+ r->out.info = r2.out.info;
+
+ return werr;
+}
+
+/*
+ netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN
+*/
+static WERROR dcesrv_netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NetrEnumerateTrustedDomainsEx
+*/
+static WERROR dcesrv_netr_NetrEnumerateTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrEnumerateTrustedDomainsEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRAddressToSitenamesExW
+*/
+static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRAddressToSitenamesExW *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsrGetDcSiteCoverageW
+*/
+static WERROR dcesrv_netr_DsrGetDcSiteCoverageW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrGetDcSiteCoverageW *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsrEnumerateDomainTrusts
+*/
+static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrEnumerateDomainTrusts *r)
+{
+ struct netr_DomainTrust *trusts;
+ void *sam_ctx;
+ int ret;
+ struct ldb_message **dom_res, **ref_res;
+ const char * const dom_attrs[] = { "objectSid", "objectGUID", NULL };
+ const char * const ref_attrs[] = { "nETBIOSName", "dnsRoot", NULL };
+ struct ldb_dn *partitions_basedn;
+
+ ZERO_STRUCT(r->out);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ ret = gendb_search_dn(sam_ctx, mem_ctx, NULL, &dom_res, dom_attrs);
+ if (ret == -1) {
+ return WERR_GENERAL_FAILURE;
+ }
+ if (ret != 1) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &ref_res, ref_attrs,
+ "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(dom_res[0]->dn));
+ if (ret == -1) {
+ return WERR_GENERAL_FAILURE;
+ }
+ if (ret != 1) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ trusts = talloc_array(mem_ctx, struct netr_DomainTrust, ret);
+ W_ERROR_HAVE_NO_MEMORY(trusts);
+
+ r->out.count = 1;
+ r->out.trusts = trusts;
+
+ /* TODO: add filtering by trust_flags, and correct trust_type
+ and attributes */
+ trusts[0].netbios_name = samdb_result_string(ref_res[0], "nETBIOSName", NULL);
+ trusts[0].dns_name = samdb_result_string(ref_res[0], "dnsRoot", NULL);
+ trusts[0].trust_flags =
+ NETR_TRUST_FLAG_TREEROOT |
+ NETR_TRUST_FLAG_IN_FOREST |
+ NETR_TRUST_FLAG_PRIMARY;
+ trusts[0].parent_index = 0;
+ trusts[0].trust_type = 2;
+ trusts[0].trust_attributes = 0;
+ trusts[0].sid = samdb_result_dom_sid(mem_ctx, dom_res[0], "objectSid");
+ trusts[0].guid = samdb_result_guid(dom_res[0], "objectGUID");
+
+ return WERR_OK;
+}
+
+
+/*
+ netr_DsrDeregisterDNSHostRecords
+*/
+static WERROR dcesrv_netr_DsrDeregisterDNSHostRecords(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrDeregisterDNSHostRecords *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_ServerTrustPasswordsGet
+*/
+static NTSTATUS dcesrv_netr_ServerTrustPasswordsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerTrustPasswordsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRGetForestTrustInformation
+*/
+static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetForestTrustInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_GetForestTrustInformation
+*/
+static WERROR dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetForestTrustInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRSERVERGETTRUSTINFO
+*/
+static WERROR dcesrv_netr_NETRSERVERGETTRUSTINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRSERVERGETTRUSTINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_netlogon_s.c"
diff --git a/source4/rpc_server/remote/README b/source4/rpc_server/remote/README
new file mode 100644
index 0000000000..0d235a9901
--- /dev/null
+++ b/source4/rpc_server/remote/README
@@ -0,0 +1,38 @@
+This is an RPC backend that implements all operations in terms of
+remote RPC operations. This may be useful in certain debugging
+situations, where the traffic is encrypted, or you wish to validate
+that IDL is correct before implementing full test clients, or with
+windows clients.
+
+There are two modes of operation: Password specified and delegated
+credentials.
+
+Password specified:
+-------------------
+
+This uses a static username/password in the config file, example:
+
+[global]
+ dcerpc endpoint servers = remote
+ dcerpc_remote:binding = ncacn_np:win2003
+ dcerpc_remote:username = administrator
+ dcerpc_remote:password = PASSWORD
+ dcerpc_remote:interfaces = samr, lsarpc, netlogon
+
+Delegated credentials:
+----------------------
+
+If your incoming user is authenticated with Kerberos, and the machine
+account for this Samba4 proxy server is 'trusted for delegation', then
+the Samba4 proxy can forward the client's credentials to the target.
+
+You must be joined to the domain (net join <domain> member).
+
+To set 'trusted for delegation' with MMC, see the checkbox in the
+Computer account property page under Users and Computers.
+
+[global]
+ dcerpc endpoint servers = remote
+ dcerpc_remote:binding = ncacn_np:win2003
+ dcerpc_remote:interfaces = samr, lsarpc, netlogon
+
diff --git a/source4/rpc_server/remote/dcesrv_remote.c b/source4/rpc_server/remote/dcesrv_remote.c
new file mode 100644
index 0000000000..cd32160d88
--- /dev/null
+++ b/source4/rpc_server/remote/dcesrv_remote.c
@@ -0,0 +1,326 @@
+/*
+ Unix SMB/CIFS implementation.
+ remote dcerpc operations
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/ndr_table.h"
+#include "param/param.h"
+
+
+struct dcesrv_remote_private {
+ struct dcerpc_pipe *c_pipe;
+};
+
+static NTSTATUS remote_op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface)
+{
+ NTSTATUS status;
+ const struct ndr_interface_table *table;
+ struct dcesrv_remote_private *private;
+ const char *binding = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "binding");
+ const char *user, *pass, *domain;
+ struct cli_credentials *credentials;
+ bool machine_account;
+
+ machine_account = lp_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "use_machine_account", false);
+
+ private = talloc(dce_call->conn, struct dcesrv_remote_private);
+ if (!private) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ private->c_pipe = NULL;
+ dce_call->context->private = private;
+
+ if (!binding) {
+ DEBUG(0,("You must specify a DCE/RPC binding string\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ user = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "user");
+ pass = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "password");
+ domain = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dceprc_remote", "domain");
+
+ table = ndr_table_by_uuid(&iface->syntax_id.uuid); /* FIXME: What about if_version ? */
+ if (!table) {
+ dce_call->fault_code = DCERPC_FAULT_UNK_IF;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if (user && pass) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using specified account\n"));
+ credentials = cli_credentials_init(private);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(credentials, dce_call->conn->dce_ctx->lp_ctx);
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ cli_credentials_set_password(credentials, pass, CRED_SPECIFIED);
+ } else if (machine_account) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using machine account\n"));
+ credentials = cli_credentials_init(private);
+ cli_credentials_set_conf(credentials, dce_call->conn->dce_ctx->lp_ctx);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ status = cli_credentials_set_machine_account(credentials, dce_call->conn->dce_ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (dce_call->conn->auth_state.session_info->credentials) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using delegated credentials\n"));
+ credentials = dce_call->conn->auth_state.session_info->credentials;
+ } else {
+ DEBUG(1,("dcerpc_remote: RPC Proxy: You must supply binding, user and password or have delegated credentials\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_pipe_connect(private,
+ &(private->c_pipe), binding, table,
+ credentials, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx);
+
+ talloc_free(credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void remote_op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)
+{
+ struct dcesrv_remote_private *private = (struct dcesrv_remote_private *)context->private;
+
+ talloc_free(private->c_pipe);
+
+ return;
+}
+
+static NTSTATUS remote_op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r)
+{
+ enum ndr_err_code ndr_err;
+ const struct ndr_interface_table *table = (const struct ndr_interface_table *)dce_call->context->iface->private;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ dce_call->fault_code = 0;
+
+ if (opnum >= table->num_calls) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ *r = talloc_size(mem_ctx, table->calls[opnum].struct_size);
+ if (!*r) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* unravel the NDR for the packet */
+ ndr_err = table->calls[opnum].ndr_pull(pull, NDR_IN, *r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dcerpc_log_packet(table, opnum, NDR_IN,
+ &dce_call->pkt.u.request.stub_and_verifier);
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ struct dcesrv_remote_private *private = dce_call->context->private;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ const struct ndr_interface_table *table = dce_call->context->iface->private;
+ const struct ndr_interface_call *call;
+ const char *name;
+
+ name = table->calls[opnum].name;
+ call = &table->calls[opnum];
+
+ if (private->c_pipe->conn->flags & DCERPC_DEBUG_PRINT_IN) {
+ ndr_print_function_debug(call->ndr_print, name, NDR_IN | NDR_SET_VALUES, r);
+ }
+
+ private->c_pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+
+ /* we didn't use the return code of this function as we only check the last_fault_code */
+ dcerpc_ndr_request(private->c_pipe, NULL, table, opnum, mem_ctx,r);
+
+ dce_call->fault_code = private->c_pipe->last_fault_code;
+ if (dce_call->fault_code != 0) {
+ DEBUG(0,("dcesrv_remote: call[%s] failed with: %s!\n",name, dcerpc_errstr(mem_ctx, dce_call->fault_code)));
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if ((dce_call->fault_code == 0) &&
+ (private->c_pipe->conn->flags & DCERPC_DEBUG_PRINT_OUT)) {
+ ndr_print_function_debug(call->ndr_print, name, NDR_OUT, r);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r)
+{
+ enum ndr_err_code ndr_err;
+ const struct ndr_interface_table *table = dce_call->context->iface->private;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ /* unravel the NDR for the packet */
+ ndr_err = table->calls[opnum].ndr_push(push, NDR_OUT, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_register_one_iface(struct dcesrv_context *dce_ctx, const struct dcesrv_interface *iface)
+{
+ int i;
+ const struct ndr_interface_table *table = iface->private;
+
+ for (i=0;i<table->endpoints->count;i++) {
+ NTSTATUS ret;
+ const char *name = table->endpoints->names[i];
+
+ ret = dcesrv_interface_register(dce_ctx, name, iface, NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("remote_op_init_server: failed to register endpoint '%s'\n",name));
+ return ret;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
+{
+ int i;
+ const char **ifaces = str_list_make(dce_ctx, lp_parm_string(dce_ctx->lp_ctx, NULL, "dcerpc_remote", "interfaces"),NULL);
+
+ if (!ifaces) {
+ DEBUG(3,("remote_op_init_server: no interfaces configured\n"));
+ return NT_STATUS_OK;
+ }
+
+ for (i=0;ifaces[i];i++) {
+ NTSTATUS ret;
+ struct dcesrv_interface iface;
+
+ if (!ep_server->interface_by_name(&iface, ifaces[i])) {
+ DEBUG(0,("remote_op_init_server: failed to find interface = '%s'\n", ifaces[i]));
+ talloc_free(ifaces);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = remote_register_one_iface(dce_ctx, &iface);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("remote_op_init_server: failed to register interface = '%s'\n", ifaces[i]));
+ talloc_free(ifaces);
+ return ret;
+ }
+ }
+
+ talloc_free(ifaces);
+ return NT_STATUS_OK;
+}
+
+static bool remote_fill_interface(struct dcesrv_interface *iface, const struct ndr_interface_table *if_tabl)
+{
+ iface->name = if_tabl->name;
+ iface->syntax_id = if_tabl->syntax_id;
+
+ iface->bind = remote_op_bind;
+ iface->unbind = remote_op_unbind;
+
+ iface->ndr_pull = remote_op_ndr_pull;
+ iface->dispatch = remote_op_dispatch;
+ iface->reply = remote_op_reply;
+ iface->ndr_push = remote_op_ndr_push;
+
+ iface->private = if_tabl;
+
+ return true;
+}
+
+static bool remote_op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version)
+{
+ const struct ndr_interface_list *l;
+
+ for (l=ndr_table_list();l;l=l->next) {
+ if (l->table->syntax_id.if_version == if_version &&
+ GUID_equal(&l->table->syntax_id.uuid, uuid)==0) {
+ return remote_fill_interface(iface, l->table);
+ }
+ }
+
+ return false;
+}
+
+static bool remote_op_interface_by_name(struct dcesrv_interface *iface, const char *name)
+{
+ const struct ndr_interface_table *tbl = ndr_table_by_name(name);
+
+ if (tbl)
+ return remote_fill_interface(iface, tbl);
+
+ return false;
+}
+
+NTSTATUS dcerpc_server_remote_init(void)
+{
+ NTSTATUS ret;
+ struct dcesrv_endpoint_server ep_server;
+
+ ZERO_STRUCT(ep_server);
+
+ /* fill in our name */
+ ep_server.name = "remote";
+
+ /* fill in all the operations */
+ ep_server.init_server = remote_op_init_server;
+
+ ep_server.interface_by_uuid = remote_op_interface_by_uuid;
+ ep_server.interface_by_name = remote_op_interface_by_name;
+
+ /* register ourselves with the DCERPC subsystem. */
+ ret = dcerpc_register_ep_server(&ep_server);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'remote' endpoint server!\n"));
+ return ret;
+ }
+
+ /* We need the full DCE/RPC interface table */
+ ndr_table_init();
+
+ return ret;
+}
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
new file mode 100644
index 0000000000..e54d518f76
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -0,0 +1,4317 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+#include "system/time.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "dsdb/common/flags.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/security.h"
+#include "rpc_server/samr/proto.h"
+#include "util/util_ldb.h"
+#include "param/param.h"
+
+/* these query macros make samr_Query[User|Group]Info a bit easier to read */
+
+#define QUERY_STRING(msg, field, attr) \
+ r->out.info->field.string = samdb_result_string(msg, attr, "");
+#define QUERY_UINT(msg, field, attr) \
+ r->out.info->field = samdb_result_uint(msg, attr, 0);
+#define QUERY_RID(msg, field, attr) \
+ r->out.info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
+#define QUERY_UINT64(msg, field, attr) \
+ r->out.info->field = samdb_result_uint64(msg, attr, 0);
+#define QUERY_APASSC(msg, field, attr) \
+ r->out.info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
+ a_state->domain_state->domain_dn, msg, attr);
+#define QUERY_FPASSC(msg, field, attr) \
+ r->out.info->field = samdb_result_force_password_change(sam_ctx, mem_ctx, \
+ a_state->domain_state->domain_dn, msg);
+#define QUERY_LHOURS(msg, field, attr) \
+ r->out.info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
+#define QUERY_AFLAGS(msg, field, attr) \
+ r->out.info->field = samdb_result_acct_flags(sam_ctx, mem_ctx, msg, a_state->domain_state->domain_dn);
+
+
+/* these are used to make the Set[User|Group]Info code easier to follow */
+
+#define SET_STRING(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
+ if (r->in.info->field.string[0] == '\0') { \
+ if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL)) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ } \
+ if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_UINT(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_INT64(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_UINT64(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define CHECK_FOR_MULTIPLES(value, flag, poss_flags) \
+ do { \
+ if ((value & flag) && ((value & flag) != (value & (poss_flags)))) { \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ } while (0) \
+
+/* Set account flags, discarding flags that cannot be set with SAMR */
+#define SET_AFLAGS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if ((r->in.info->field & (ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST)) == 0) { \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_NORMAL, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_DOMTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_WSTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_SVRTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, (r->in.info->field & ~(ACB_AUTOLOCK|ACB_PW_EXPIRED))) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_LHOURS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+
+/*
+ samr_Connect
+
+ create a connection to the SAM database
+*/
+static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *handle;
+
+ ZERO_STRUCTP(r->out.connect_handle);
+
+ c_state = talloc(dce_call->conn, struct samr_connect_state);
+ if (!c_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the sam database is accessible */
+ c_state->sam_ctx = samdb_connect(c_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (c_state->sam_ctx == NULL) {
+ talloc_free(c_state);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+
+ handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
+ if (!handle) {
+ talloc_free(c_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, c_state);
+
+ c_state->access_mask = r->in.access_mask;
+ *r->out.connect_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Close
+*/
+static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Close *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetSecurity
+*/
+static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetSecurity *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_QuerySecurity
+*/
+static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QuerySecurity *r)
+{
+ struct dcesrv_handle *h;
+ struct sec_desc_buf *sd;
+
+ r->out.sdbuf = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ sd = talloc(mem_ctx, struct sec_desc_buf);
+ if (sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sd->sd = samdb_default_security_descriptor(mem_ctx);
+
+ r->out.sdbuf = sd;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Shutdown
+
+ we refuse this operation completely. If a admin wants to shutdown samr
+ in Samba then they should use the samba admin tools to disable the samr pipe
+*/
+static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Shutdown *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ samr_LookupDomain
+
+ this maps from a domain name to a SID
+*/
+static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupDomain *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *h;
+ struct dom_sid *sid;
+ const char * const dom_attrs[] = { "objectSid", NULL};
+ const char * const ref_attrs[] = { "ncName", NULL};
+ struct ldb_message **dom_msgs;
+ struct ldb_message **ref_msgs;
+ int ret;
+ struct ldb_dn *partitions_basedn;
+
+ r->out.sid = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h->data;
+
+ if (r->in.domain_name->string == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
+
+ if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, NULL, &dom_msgs, dom_attrs,
+ "(objectClass=builtinDomain)");
+ } else {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
+ "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
+ ldb_binary_encode_string(mem_ctx, r->in.domain_name->string));
+ if (ret != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ ret = gendb_search_dn(c_state->sam_ctx, mem_ctx,
+ samdb_result_dn(c_state->sam_ctx, mem_ctx,
+ ref_msgs[0], "ncName", NULL),
+ &dom_msgs, dom_attrs);
+ }
+
+ if (ret != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
+ "objectSid");
+
+ if (sid == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ r->out.sid = sid;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_EnumDomains
+
+ list the domains in the SAM
+*/
+static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomains *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *h;
+ struct samr_SamArray *array;
+ int i, start_i, ret;
+ const char * const dom_attrs[] = { "cn", NULL};
+ const char * const ref_attrs[] = { "nETBIOSName", NULL};
+ struct ldb_result *dom_res;
+ struct ldb_result *ref_res;
+ struct ldb_dn *partitions_basedn;
+
+ *r->out.resume_handle = 0;
+ r->out.sam = NULL;
+ r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h->data;
+
+ partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
+
+ ret = ldb_search_exp_fmt(c_state->sam_ctx, mem_ctx, &dom_res, ldb_get_default_basedn(c_state->sam_ctx),
+ LDB_SCOPE_SUBTREE, dom_attrs, "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("samdb: unable to find domains: %s\n", ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ *r->out.resume_handle = dom_res->count;
+
+ start_i = *r->in.resume_handle;
+
+ if (start_i >= dom_res->count) {
+ /* search past end of list is not an error for this call */
+ return NT_STATUS_OK;
+ }
+
+ array = talloc(mem_ctx, struct samr_SamArray);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array->count = 0;
+ array->entries = NULL;
+
+ array->entries = talloc_array(mem_ctx, struct samr_SamEntry, dom_res->count - start_i);
+ if (array->entries == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<dom_res->count-start_i;i++) {
+ array->entries[i].idx = start_i + i;
+ /* try and find the domain */
+ ret = ldb_search_exp_fmt(c_state->sam_ctx, mem_ctx, &ref_res, partitions_basedn,
+ LDB_SCOPE_SUBTREE, ref_attrs, "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(dom_res->msgs[i]->dn));
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("samdb: unable to find domains: %s\n", ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ref_res->count == 1) {
+ array->entries[i].name.string = samdb_result_string(ref_res->msgs[0], "nETBIOSName", NULL);
+ } else {
+ array->entries[i].name.string = samdb_result_string(dom_res->msgs[i], "cn", NULL);
+ }
+ }
+
+ r->out.sam = array;
+ r->out.num_entries = i;
+ array->count = r->out.num_entries;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_OpenDomain
+*/
+static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenDomain *r)
+{
+ struct dcesrv_handle *h_conn, *h_domain;
+ const char *domain_name;
+ struct samr_connect_state *c_state;
+ struct samr_domain_state *d_state;
+ const char * const dom_attrs[] = { "cn", NULL};
+ const char * const ref_attrs[] = { "nETBIOSName", NULL};
+ struct ldb_message **dom_msgs;
+ struct ldb_message **ref_msgs;
+ int ret;
+ struct ldb_dn *partitions_basedn;
+
+ ZERO_STRUCTP(r->out.domain_handle);
+
+ DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h_conn->data;
+
+ if (r->in.sid == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
+
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, NULL, &dom_msgs, dom_attrs,
+ "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ } else if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == -1) {
+ DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
+ "(&(&(nETBIOSName=*)(objectclass=crossRef))(ncName=%s))",
+ ldb_dn_get_linearized(dom_msgs[0]->dn));
+ if (ret == 0) {
+ domain_name = ldb_msg_find_attr_as_string(dom_msgs[0], "cn", NULL);
+ if (domain_name == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else if (ret == 1) {
+
+ domain_name = ldb_msg_find_attr_as_string(ref_msgs[0], "nETBIOSName", NULL);
+ if (domain_name == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ d_state = talloc(c_state, struct samr_domain_state);
+ if (!d_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ d_state->role = lp_server_role(dce_call->conn->dce_ctx->lp_ctx);
+ d_state->connect_state = talloc_reference(d_state, c_state);
+ d_state->sam_ctx = c_state->sam_ctx;
+ d_state->domain_sid = dom_sid_dup(d_state, r->in.sid);
+ d_state->domain_name = talloc_strdup(d_state, domain_name);
+ d_state->domain_dn = ldb_dn_copy(d_state, dom_msgs[0]->dn);
+ if (!d_state->domain_sid || !d_state->domain_name || !d_state->domain_dn) {
+ talloc_free(d_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ d_state->access_mask = r->in.access_mask;
+
+ if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
+ d_state->builtin = true;
+ } else {
+ d_state->builtin = false;
+ }
+
+ d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+
+ h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
+ if (!h_domain) {
+ talloc_free(d_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ h_domain->data = talloc_steal(h_domain, d_state);
+
+ *r->out.domain_handle = h_domain->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo1
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo1 *info)
+{
+ info->min_password_length =
+ samdb_result_uint(dom_msgs[0], "minPwdLength", 0);
+ info->password_history_length =
+ samdb_result_uint(dom_msgs[0], "pwdHistoryLength", 0);
+ info->password_properties =
+ samdb_result_uint(dom_msgs[0], "pwdProperties", 0);
+ info->max_password_age =
+ samdb_result_int64(dom_msgs[0], "maxPwdAge", 0);
+ info->min_password_age =
+ samdb_result_int64(dom_msgs[0], "minPwdAge", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo2
+*/
+static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomGeneralInformation *info)
+{
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx, dom_msgs[0], "fSMORoleOwner");
+
+ if (!info->primary.string) {
+ info->primary.string = lp_netbios_name(state->lp_ctx);
+ }
+
+ info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
+ 0x8000000000000000LL);
+
+ info->oem_information.string = samdb_result_string(dom_msgs[0], "oEMInformation", NULL);
+ info->domain_name.string = state->domain_name;
+
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ 0);
+ switch (state->role) {
+ case ROLE_DOMAIN_CONTROLLER:
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ if (samdb_is_pdc(state->sam_ctx)) {
+ info->role = SAMR_ROLE_DOMAIN_PDC;
+ } else {
+ info->role = SAMR_ROLE_DOMAIN_BDC;
+ }
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ info->role = SAMR_ROLE_DOMAIN_MEMBER;
+ break;
+ case ROLE_STANDALONE:
+ info->role = SAMR_ROLE_STANDALONE;
+ break;
+ }
+
+ /* No users in BUILTIN, and the LOCAL group types are only in builtin, and the global group type is never in BUILTIN */
+ info->num_users = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
+ "(objectClass=user)");
+ info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
+ "(&(objectClass=group)(sAMAccountType=%u))",
+ ATYPE_GLOBAL_GROUP);
+ info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
+ "(&(objectClass=group)(sAMAccountType=%u))",
+ ATYPE_LOCAL_GROUP);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo3
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo3 *info)
+{
+ info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
+ 0x8000000000000000LL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo4
+*/
+static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomOEMInformation *info)
+{
+ info->oem_information.string = samdb_result_string(dom_msgs[0], "oEMInformation", NULL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo5
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo5 *info)
+{
+ info->domain_name.string = state->domain_name;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo6
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo6 *info)
+{
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx,
+ dom_msgs[0], "fSMORoleOwner");
+
+ if (!info->primary.string) {
+ info->primary.string = lp_netbios_name(state->lp_ctx);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo7
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo7 *info)
+{
+
+ switch (state->role) {
+ case ROLE_DOMAIN_CONTROLLER:
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ if (samdb_is_pdc(state->sam_ctx)) {
+ info->role = SAMR_ROLE_DOMAIN_PDC;
+ } else {
+ info->role = SAMR_ROLE_DOMAIN_BDC;
+ }
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ info->role = SAMR_ROLE_DOMAIN_MEMBER;
+ break;
+ case ROLE_STANDALONE:
+ info->role = SAMR_ROLE_STANDALONE;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo8
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo8 *info)
+{
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ time(NULL));
+
+ info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
+ 0x0LL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo9
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo9 *info)
+{
+ info->unknown = 1;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo11
+*/
+static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomGeneralInformation2 *info)
+{
+ NTSTATUS status;
+ status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
+ -18000000000LL);
+ info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
+ -18000000000LL);
+ info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo12
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo12 *info)
+{
+ info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
+ -18000000000LL);
+ info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
+ -18000000000LL);
+ info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo13
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo13 *info)
+{
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ time(NULL));
+
+ info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
+ 0x0LL);
+
+ info->unknown1 = 0;
+ info->unknown2 = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_QueryDomainInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+
+ struct ldb_message **dom_msgs;
+ const char * const *attrs = NULL;
+
+ r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ r->out.info = talloc(mem_ctx, union samr_DomainInfo);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ {
+ static const char * const attrs2[] = { "minPwdLength", "pwdHistoryLength",
+ "pwdProperties", "maxPwdAge",
+ "minPwdAge", NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 2:
+ {
+ static const char * const attrs2[] = {"forceLogoff",
+ "oEMInformation",
+ "modifiedCount",
+ "fSMORoleOwner",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 3:
+ {
+ static const char * const attrs2[] = {"forceLogoff",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 4:
+ {
+ static const char * const attrs2[] = {"oEMInformation",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 5:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 6:
+ {
+ static const char * const attrs2[] = {"fSMORoleOwner",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 7:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 8:
+ {
+ static const char * const attrs2[] = { "modifiedCount",
+ "creationTime",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 9:
+ attrs = NULL;
+ break;
+ case 11:
+ {
+ static const char * const attrs2[] = { "oEMInformation", "forceLogoff",
+ "modifiedCount",
+ "lockoutDuration",
+ "lockOutObservationWindow",
+ "lockoutThreshold",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 12:
+ {
+ static const char * const attrs2[] = { "lockoutDuration",
+ "lockOutObservationWindow",
+ "lockoutThreshold",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 13:
+ {
+ static const char * const attrs2[] = { "modifiedCount",
+ "creationTime",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ }
+
+ /* some levels don't need a search */
+ if (attrs) {
+ int ret;
+ ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &dom_msgs, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 1:
+ return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info1);
+ case 2:
+ return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
+ &r->out.info->general);
+ case 3:
+ return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info3);
+ case 4:
+ return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
+ &r->out.info->oem);
+ case 5:
+ return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info5);
+ case 6:
+ return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info6);
+ case 7:
+ return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info7);
+ case 8:
+ return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info8);
+ case 9:
+ return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info9);
+ case 11:
+ return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
+ &r->out.info->general2);
+ case 12:
+ return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info12);
+ case 13:
+ return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
+ &r->out.info->info13);
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+
+/*
+ samr_SetDomainInfo
+*/
+static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message *msg;
+ int ret;
+ struct ldb_context *sam_ctx;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+ sam_ctx = d_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ SET_UINT (msg, info1.min_password_length, "minPwdLength");
+ SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
+ SET_UINT (msg, info1.password_properties, "pwdProperties");
+ SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
+ SET_INT64 (msg, info1.min_password_age, "minPwdAge");
+ break;
+ case 3:
+ SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
+ break;
+ case 4:
+ SET_STRING(msg, oem.oem_information, "oEMInformation");
+ break;
+
+ case 6:
+ case 7:
+ case 9:
+ /* No op, we don't know where to set these */
+ return NT_STATUS_OK;
+
+ case 12:
+
+ SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
+ SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
+ SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
+ break;
+
+ default:
+ /* many info classes are not valid for SetDomainInfo */
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_get_linearized(d_state->domain_dn),
+ ldb_errstring(sam_ctx)));
+
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_CreateDomainGroup
+*/
+static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateDomainGroup *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *name;
+ struct ldb_message *msg;
+ struct dom_sid *sid;
+ const char *groupname;
+ struct dcesrv_handle *g_handle;
+ int ret;
+
+ ZERO_STRUCTP(r->out.group_handle);
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ groupname = r->in.name->string;
+
+ if (groupname == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if the group already exists */
+ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "sAMAccountName",
+ "(&(sAMAccountName=%s)(objectclass=group))",
+ ldb_binary_encode_string(mem_ctx, groupname));
+ if (name != NULL) {
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* add core elements to the ldb_message for the user */
+ msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", groupname);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", groupname);
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
+
+ /* create the group */
+ ret = ldb_add(d_state->sam_ctx, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ DEBUG(0,("Failed to create group record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_GROUP_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ DEBUG(0,("Failed to create group record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ DEBUG(0,("Failed to create group record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msg->dn);
+
+ /* retrieve the sid for the group just created */
+ sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
+ msg->dn, "objectSid", NULL);
+ if (sid == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ a_state->account_name = talloc_strdup(a_state, groupname);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.group_handle = g_handle->wire_handle;
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ comparison function for sorting SamEntry array
+*/
+static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
+{
+ return e1->idx - e2->idx;
+}
+
+/*
+ samr_EnumDomainGroups
+*/
+static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainGroups *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int ldb_cnt, count, i, first;
+ struct samr_SamEntry *entries;
+ const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
+
+ *r->out.resume_handle = 0;
+ r->out.sam = NULL;
+ r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* search for all domain groups in this domain. This could possibly be
+ cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid,
+ "(&(grouptype=%d)(objectclass=group))",
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (ldb_cnt == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = 0;
+
+ for (i=0;i<ldb_cnt;i++) {
+ struct dom_sid *group_sid;
+
+ group_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+ if (group_sid == NULL)
+ continue;
+
+ entries[count].idx =
+ group_sid->sub_auths[group_sid->num_auths-1];
+ entries[count].name.string =
+ samdb_result_string(res[i], "sAMAccountName", "");
+ count += 1;
+ }
+
+ /* sort the results by rid */
+ qsort(entries, count, sizeof(struct samr_SamEntry),
+ (comparison_fn_t)compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<count && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 54 */
+ r->out.num_entries = count - first;
+ r->out.num_entries = MIN(r->out.num_entries,
+ 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
+
+ r->out.sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!r->out.sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.sam->entries = entries+first;
+ r->out.sam->count = r->out.num_entries;
+
+ if (r->out.num_entries < count - first) {
+ *r->out.resume_handle = entries[first+r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateUser2
+
+ This call uses transactions to ensure we don't get a new conflicting
+ user while we are processing this, and to ensure the user either
+ completly exists, or does not.
+*/
+static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateUser2 *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *name;
+ struct ldb_message *msg;
+ struct dom_sid *sid;
+ const char *account_name;
+ struct dcesrv_handle *u_handle;
+ int ret;
+ const char *container, *obj_class=NULL;
+ char *cn_name;
+ int cn_name_len;
+
+ const char *attrs[] = {
+ "objectSid",
+ "userAccountControl",
+ NULL
+ };
+
+ uint32_t user_account_control;
+
+ struct ldb_message **msgs;
+
+ ZERO_STRUCTP(r->out.user_handle);
+ *r->out.access_granted = 0;
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ account_name = r->in.account_name->string;
+
+ if (account_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = ldb_transaction_start(d_state->sam_ctx);
+ if (ret != 0) {
+ DEBUG(0,("Failed to start a transaction for user creation: %s\n",
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* check if the user already exists */
+ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "sAMAccountName",
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx, account_name));
+ if (name != NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_USER_EXISTS;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cn_name = talloc_strdup(mem_ctx, account_name);
+ if (!cn_name) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cn_name_len = strlen(cn_name);
+
+ /* This must be one of these values *only* */
+ if (r->in.acct_flags == ACB_NORMAL) {
+ container = "CN=Users";
+ obj_class = "user";
+
+ } else if (r->in.acct_flags == ACB_WSTRUST) {
+ if (cn_name[cn_name_len - 1] != '$') {
+ return NT_STATUS_FOOBAR;
+ }
+ cn_name[cn_name_len - 1] = '\0';
+ container = "CN=Computers";
+ obj_class = "computer";
+ samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "primaryGroupID", DOMAIN_RID_DOMAIN_MEMBERS);
+
+ } else if (r->in.acct_flags == ACB_SVRTRUST) {
+ if (cn_name[cn_name_len - 1] != '$') {
+ return NT_STATUS_FOOBAR;
+ }
+ cn_name[cn_name_len - 1] = '\0';
+ container = "OU=Domain Controllers";
+ obj_class = "computer";
+ samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "primaryGroupID", DOMAIN_RID_DCS);
+
+ } else if (r->in.acct_flags == ACB_DOMTRUST) {
+ container = "CN=Users";
+ obj_class = "user";
+
+ } else {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* add core elements to the ldb_message for the user */
+ msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s,%s", cn_name, container)) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_FOOBAR;
+ }
+
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", account_name);
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", obj_class);
+
+ /* Start a transaction, so we can query and do a subsequent atomic modify */
+
+ /* create the user */
+ ret = ldb_add(d_state->sam_ctx, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_USER_EXISTS;
+ case LDB_ERR_UNWILLING_TO_PERFORM:
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msg->dn);
+
+ /* retrieve the sid and account control bits for the user just created */
+ ret = gendb_search_dn(d_state->sam_ctx, a_state,
+ msg->dn, &msgs, attrs);
+
+ if (ret != 1) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Apparently we failed to create an account record, as %s now doesn't exist\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ sid = samdb_result_dom_sid(mem_ctx, msgs[0], "objectSid");
+ if (sid == NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Apparently we failed to get the objectSid of the just created account record %s\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* Change the account control to be the correct account type.
+ * The default is for a workstation account */
+ user_account_control = samdb_result_uint(msgs[0], "userAccountControl", 0);
+ user_account_control = (user_account_control &
+ ~(UF_NORMAL_ACCOUNT |
+ UF_INTERDOMAIN_TRUST_ACCOUNT |
+ UF_WORKSTATION_TRUST_ACCOUNT |
+ UF_SERVER_TRUST_ACCOUNT));
+ user_account_control |= samdb_acb2uf(r->in.acct_flags);
+
+ talloc_free(msg);
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(msg, a_state->account_dn);
+
+ if (samdb_msg_add_uint(a_state->sam_ctx, mem_ctx, msg,
+ "userAccountControl",
+ user_account_control) != 0) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to modify account record %s to set userAccountControl: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ ldb_transaction_cancel(d_state->sam_ctx);
+
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = ldb_transaction_commit(d_state->sam_ctx);
+ if (ret != 0) {
+ DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state->account_name = talloc_steal(a_state, account_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
+ if (!u_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle->data = talloc_steal(u_handle, a_state);
+
+ *r->out.user_handle = u_handle->wire_handle;
+ *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
+
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateUser
+*/
+static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateUser *r)
+{
+ struct samr_CreateUser2 r2;
+ uint32_t access_granted = 0;
+
+
+ /* a simple wrapper around samr_CreateUser2 works nicely */
+ r2.in.domain_handle = r->in.domain_handle;
+ r2.in.account_name = r->in.account_name;
+ r2.in.acct_flags = ACB_NORMAL;
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.user_handle = r->out.user_handle;
+ r2.out.access_granted = &access_granted;
+ r2.out.rid = r->out.rid;
+
+ return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
+}
+
+/*
+ samr_EnumDomainUsers
+*/
+static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainUsers *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_result *res;
+ int ret, num_filtered_entries, i, first;
+ struct samr_SamEntry *entries;
+ const char * const attrs[] = { "objectSid", "sAMAccountName", "userAccountControl", NULL };
+
+ *r->out.resume_handle = 0;
+ r->out.sam = NULL;
+ r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* don't have to worry about users in the builtin domain, as there are none */
+ ret = ldb_search_exp_fmt(d_state->sam_ctx, mem_ctx, &res, d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs, "objectClass=user");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3, ("Failed to search for Domain Users in %s: %s\n",
+ ldb_dn_get_linearized(d_state->domain_dn), ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, res->count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ num_filtered_entries = 0;
+ for (i=0;i<res->count;i++) {
+ /* Check if a mask has been requested */
+ if (r->in.acct_flags
+ && ((samdb_result_acct_flags(d_state->sam_ctx, mem_ctx, res->msgs[i],
+ d_state->domain_dn) & r->in.acct_flags) == 0)) {
+ continue;
+ }
+ entries[num_filtered_entries].idx = samdb_result_rid_from_sid(mem_ctx, res->msgs[i], "objectSid", 0);
+ entries[num_filtered_entries].name.string = samdb_result_string(res->msgs[i], "sAMAccountName", "");
+ num_filtered_entries++;
+ }
+
+ /* sort the results by rid */
+ qsort(entries, num_filtered_entries, sizeof(struct samr_SamEntry),
+ (comparison_fn_t)compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<num_filtered_entries && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 54 */
+ r->out.num_entries = num_filtered_entries - first;
+ r->out.num_entries = MIN(r->out.num_entries,
+ 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
+
+ r->out.sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!r->out.sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.sam->entries = entries+first;
+ r->out.sam->count = r->out.num_entries;
+
+ if (first == num_filtered_entries) {
+ return NT_STATUS_OK;
+ }
+
+ if (r->out.num_entries < num_filtered_entries - first) {
+ *r->out.resume_handle = entries[first+r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateDomAlias
+*/
+static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateDomAlias *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *alias_name, *name;
+ struct ldb_message *msg;
+ struct dom_sid *sid;
+ struct dcesrv_handle *a_handle;
+ int ret;
+
+ ZERO_STRUCTP(r->out.alias_handle);
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ alias_name = r->in.alias_name->string;
+
+ if (alias_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Check if alias already exists */
+ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "sAMAccountName",
+ "(sAMAccountName=%s)(objectclass=group))",
+ ldb_binary_encode_string(mem_ctx, alias_name));
+
+ if (name != NULL) {
+ return NT_STATUS_ALIAS_EXISTS;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* add core elements to the ldb_message for the alias */
+ msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", alias_name);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", alias_name);
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
+ samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "groupType", GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+
+ /* create the alias */
+ ret = ldb_add(d_state->sam_ctx, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return NT_STATUS_ALIAS_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ DEBUG(0,("Failed to create alias record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msg->dn);
+
+ /* retrieve the sid for the alias just created */
+ sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
+ msg->dn, "objectSid", NULL);
+
+ a_state->account_name = talloc_strdup(a_state, alias_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
+ if (a_handle == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ a_handle->data = talloc_steal(a_handle, a_state);
+
+ *r->out.alias_handle = a_handle->wire_handle;
+
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_EnumDomainAliases
+*/
+static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainAliases *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int ldb_cnt, count, i, first;
+ struct samr_SamEntry *entries;
+ const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
+
+ *r->out.resume_handle = 0;
+ r->out.sam = NULL;
+ r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* search for all domain groups in this domain. This could possibly be
+ cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn,
+ &res, attrs,
+ d_state->domain_sid,
+ "(&(|(grouptype=%d)(grouptype=%d)))"
+ "(objectclass=group))",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ldb_cnt == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ldb_cnt == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = 0;
+
+ for (i=0;i<ldb_cnt;i++) {
+ struct dom_sid *alias_sid;
+
+ alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+
+ if (alias_sid == NULL)
+ continue;
+
+ entries[count].idx =
+ alias_sid->sub_auths[alias_sid->num_auths-1];
+ entries[count].name.string =
+ samdb_result_string(res[i], "sAMAccountName", "");
+ count += 1;
+ }
+
+ /* sort the results by rid */
+ qsort(entries, count, sizeof(struct samr_SamEntry),
+ (comparison_fn_t)compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<count && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ if (first == count) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.num_entries = count - first;
+ r->out.num_entries = MIN(r->out.num_entries, 1000);
+
+ r->out.sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!r->out.sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.sam->entries = entries+first;
+ r->out.sam->count = r->out.num_entries;
+
+ if (r->out.num_entries < count - first) {
+ *r->out.resume_handle =
+ entries[first+r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetAliasMembership
+*/
+static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetAliasMembership *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int i, count = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.sids->num_sids > 0) {
+ const char *filter;
+ const char * const attrs[2] = { "objectSid", NULL };
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(|(grouptype=%d)(grouptype=%d))"
+ "(objectclass=group)(|",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (filter == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<r->in.sids->num_sids; i++) {
+ const char *memberdn;
+
+ memberdn =
+ samdb_search_string(d_state->sam_ctx,
+ mem_ctx, NULL, "distinguishedName",
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx,
+ r->in.sids->sids[i].sid));
+
+ if (memberdn == NULL)
+ continue;
+
+ filter = talloc_asprintf(mem_ctx, "%s(member=%s)",
+ filter, memberdn);
+ if (filter == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid, "%s))", filter);
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.rids->count = 0;
+ r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
+ if (r->out.rids->ids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<count; i++) {
+ struct dom_sid *alias_sid;
+
+ alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+
+ if (alias_sid == NULL) {
+ DEBUG(0, ("Could not find objectSid\n"));
+ continue;
+ }
+
+ r->out.rids->ids[r->out.rids->count] =
+ alias_sid->sub_auths[alias_sid->num_auths-1];
+ r->out.rids->count += 1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_LookupNames
+*/
+static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupNames *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ int i, num_mapped;
+ NTSTATUS status = NT_STATUS_OK;
+ const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
+ int count;
+
+ ZERO_STRUCT(r->out.rids);
+ ZERO_STRUCT(r->out.types);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.num_names == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.rids.ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
+ r->out.types.ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
+ if (!r->out.rids.ids || !r->out.types.ids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.rids.count = r->in.num_names;
+ r->out.types.count = r->in.num_names;
+
+ num_mapped = 0;
+
+ for (i=0;i<r->in.num_names;i++) {
+ struct ldb_message **res;
+ struct dom_sid *sid;
+ uint32_t atype, rtype;
+
+ r->out.rids.ids[i] = 0;
+ r->out.types.ids[i] = SID_NAME_UNKNOWN;
+
+ count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
+ "sAMAccountName=%s",
+ ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
+ if (count != 1) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ if (sid == NULL) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ atype = samdb_result_uint(res[0], "sAMAccountType", 0);
+ if (atype == 0) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ rtype = samdb_atype_map(atype);
+
+ if (rtype == SID_NAME_UNKNOWN) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ r->out.rids.ids[i] = sid->sub_auths[sid->num_auths-1];
+ r->out.types.ids[i] = rtype;
+ num_mapped++;
+ }
+
+ if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ return status;
+}
+
+
+/*
+ samr_LookupRids
+*/
+static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupRids *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ int i, total;
+ NTSTATUS status = NT_STATUS_OK;
+ struct lsa_String *names;
+ uint32_t *ids;
+
+ ZERO_STRUCT(r->out.names);
+ ZERO_STRUCT(r->out.types);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.num_rids == 0)
+ return NT_STATUS_OK;
+
+ names = talloc_array(mem_ctx, struct lsa_String, r->in.num_rids);
+ ids = talloc_array(mem_ctx, uint32_t, r->in.num_rids);
+
+ if ((names == NULL) || (ids == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ total = 0;
+
+ for (i=0; i<r->in.num_rids; i++) {
+ struct ldb_message **res;
+ int count;
+ const char * const attrs[] = { "sAMAccountType",
+ "sAMAccountName", NULL };
+ uint32_t atype;
+ struct dom_sid *sid;
+
+ ids[i] = SID_NAME_UNKNOWN;
+
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rids[i]);
+ if (sid == NULL) {
+ names[i].string = NULL;
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ count = gendb_search(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (count != 1) {
+ names[i].string = NULL;
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ names[i].string = samdb_result_string(res[0], "sAMAccountName",
+ NULL);
+
+ atype = samdb_result_uint(res[0], "sAMAccountType", 0);
+ if (atype == 0) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ ids[i] = samdb_atype_map(atype);
+
+ if (ids[i] == SID_NAME_UNKNOWN) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+ }
+
+ r->out.names.names = names;
+ r->out.names.count = r->in.num_rids;
+
+ r->out.types.ids = ids;
+ r->out.types.count = r->in.num_rids;
+
+ return status;
+}
+
+
+/*
+ samr_OpenGroup
+*/
+static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenGroup *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *groupname;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *g_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.group_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the group SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the group record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=group)"
+ "(grouptype=%d))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ groupname = samdb_result_string(msgs[0], "sAMAccountName", NULL);
+ if (groupname == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, groupname);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.group_handle = g_handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_QueryGroupInfo
+*/
+static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryGroupInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ const char * const attrs[4] = { "sAMAccountName", "description",
+ "numMembers", NULL };
+ int ret;
+
+ r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ ret = ldb_search_exp_fmt(a_state->sam_ctx, mem_ctx, &res, a_state->account_dn, LDB_SCOPE_SUBTREE, attrs, "objectClass=*");
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(2, ("Error reading group info: %s\n", ldb_errstring(a_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count != 1) {
+ DEBUG(2, ("Error finding group info, got %d entries\n", res->count));
+
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res->msgs[0];
+
+ /* allocate the info structure */
+ r->out.info = talloc(mem_ctx, union samr_GroupInfo);
+ if (r->out.info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(r->out.info);
+
+ /* Fill in the level */
+ switch (r->in.level) {
+ case GROUPINFOALL:
+ QUERY_STRING(msg, all.name, "sAMAccountName");
+ r->out.info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ QUERY_UINT (msg, all.num_members, "numMembers")
+ QUERY_STRING(msg, all.description, "description");
+ break;
+ case GROUPINFONAME:
+ QUERY_STRING(msg, name, "sAMAccountName");
+ break;
+ case GROUPINFOATTRIBUTES:
+ r->out.info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ break;
+ case GROUPINFODESCRIPTION:
+ QUERY_STRING(msg, description, "description");
+ break;
+ case GROUPINFOALL2:
+ QUERY_STRING(msg, all2.name, "sAMAccountName");
+ r->out.info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ QUERY_UINT (msg, all2.num_members, "numMembers")
+ QUERY_STRING(msg, all2.description, "description");
+ break;
+ default:
+ r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetGroupInfo
+*/
+static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetGroupInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *g_state;
+ struct ldb_message *msg;
+ struct ldb_context *sam_ctx;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ g_state = h->data;
+ sam_ctx = g_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case GROUPINFODESCRIPTION:
+ SET_STRING(msg, description, "description");
+ break;
+ case GROUPINFONAME:
+ /* On W2k3 this does not change the name, it changes the
+ * sAMAccountName attribute */
+ SET_STRING(msg, name, "sAMAccountName");
+ break;
+ case GROUPINFOATTRIBUTES:
+ /* This does not do anything obviously visible in W2k3 LDAP */
+ return NT_STATUS_OK;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(g_state->sam_ctx, msg);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_AddGroupMember
+*/
+static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct dom_sid *membersid;
+ const char *memberdn;
+ struct ldb_result *res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (membersid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* In native mode, AD can also nest domain groups. Not sure yet
+ * whether this is also available via RPC. */
+ ret = ldb_search_exp_fmt(d_state->sam_ctx, mem_ctx, &res,
+ d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, membersid));
+
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (res->count > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
+
+ if (memberdn == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+ return NT_STATUS_MEMBER_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+}
+
+
+/*
+ samr_DeleteDomainGroup
+*/
+static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteDomainGroup *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.group_handle = *r->in.group_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCTP(r->out.group_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteGroupMember
+*/
+static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct dom_sid *membersid;
+ const char *memberdn;
+ struct ldb_result *res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (membersid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* In native mode, AD can also nest domain groups. Not sure yet
+ * whether this is also available via RPC. */
+ ret = ldb_search_exp_fmt(d_state->sam_ctx, mem_ctx, &res,
+ d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, membersid));
+
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (res->count > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
+
+ if (memberdn == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_NO_SUCH_ATTRIBUTE:
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+}
+
+
+/*
+ samr_QueryGroupMember
+*/
+static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message **res;
+ struct ldb_message_element *el;
+ struct samr_RidTypeArray *array;
+ const char * const attrs[2] = { "member", NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ /* pull the member attribute */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ array = talloc(mem_ctx, struct samr_RidTypeArray);
+
+ if (array == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(array);
+
+ el = ldb_msg_find_element(res[0], "member");
+
+ if (el != NULL) {
+ int i;
+
+ array->count = el->num_values;
+
+ array->rids = talloc_array(mem_ctx, uint32_t,
+ el->num_values);
+ if (array->rids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ array->types = talloc_array(mem_ctx, uint32_t,
+ el->num_values);
+ if (array->types == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<el->num_values; i++) {
+ struct ldb_message **res2;
+ const char * const attrs2[2] = { "objectSid", NULL };
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ ldb_dn_new(mem_ctx, a_state->sam_ctx, (const char *)el->values[i].data),
+ &res2, attrs2);
+ if (ret != 1)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ array->rids[i] =
+ samdb_result_rid_from_sid(mem_ctx, res2[0],
+ "objectSid", 0);
+
+ if (array->rids[i] == 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ array->types[i] = 7; /* RID type of some kind, not sure what the value means. */
+ }
+ }
+
+ r->out.rids = array;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetMemberAttributesOfGroup
+*/
+static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetMemberAttributesOfGroup *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_OpenAlias
+*/
+static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenAlias *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *alias_name;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *g_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.alias_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the alias SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (sid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* search for the group record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=group)"
+ "(|(grouptype=%d)(grouptype=%d)))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ alias_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
+ if (alias_name == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, alias_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.alias_handle = g_handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryAliasInfo
+*/
+static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryAliasInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ const char * const attrs[4] = { "sAMAccountName", "description",
+ "numMembers", NULL };
+ int ret;
+
+ r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ /* pull all the alias attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn ,&res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ r->out.info = talloc(mem_ctx, union samr_AliasInfo);
+ if (r->out.info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(r->out.info);
+
+ switch(r->in.level) {
+ case ALIASINFOALL:
+ QUERY_STRING(msg, all.name, "sAMAccountName");
+ QUERY_UINT (msg, all.num_members, "numMembers");
+ QUERY_STRING(msg, all.description, "description");
+ break;
+ case ALIASINFONAME:
+ QUERY_STRING(msg, name, "sAMAccountName");
+ break;
+ case ALIASINFODESCRIPTION:
+ QUERY_STRING(msg, description, "description");
+ break;
+ default:
+ r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetAliasInfo
+*/
+static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetAliasInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ struct ldb_context *sam_ctx;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case ALIASINFODESCRIPTION:
+ SET_STRING(msg, description, "description");
+ break;
+ case ALIASINFONAME:
+ /* On W2k3 this does not change the name, it changes the
+ * sAMAccountName attribute */
+ SET_STRING(msg, name, "sAMAccountName");
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(a_state->sam_ctx, msg);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteDomAlias
+*/
+static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteDomAlias *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.alias_handle = *r->in.alias_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCTP(r->out.alias_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_AddAliasMember
+*/
+static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddAliasMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct ldb_message **msgs;
+ const char * const attrs[] = { NULL };
+ struct ldb_dn *memberdn = NULL;
+ int ret;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
+ &msgs, attrs, "(objectsid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+
+ if (ret == 1) {
+ memberdn = msgs[0]->dn;
+ } else if (ret > 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, r->in.sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ status = samdb_create_foreign_security_principal(d_state->sam_ctx, mem_ctx,
+ r->in.sid, &memberdn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ DEBUG(0, ("samdb_search returned %d: %s\n", ret, ldb_errstring(d_state->sam_ctx)));
+ }
+
+ if (memberdn == NULL) {
+ DEBUG(0, ("Could not find memberdn\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
+ ldb_dn_alloc_linearized(mem_ctx, memberdn)) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (ldb_modify(a_state->sam_ctx, mod) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteAliasMember
+*/
+static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteAliasMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ const char *memberdn;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "distinguishedName", "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+
+ if (memberdn == NULL)
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (ldb_modify(a_state->sam_ctx, mod) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetMembersInAlias
+*/
+static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetMembersInAlias *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message **msgs;
+ struct lsa_SidPtr *sids;
+ struct ldb_message_element *el;
+ const char * const attrs[2] = { "member", NULL};
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &msgs, attrs);
+
+ if (ret != 1)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ r->out.sids->num_sids = 0;
+ r->out.sids->sids = NULL;
+
+ el = ldb_msg_find_element(msgs[0], "member");
+
+ if (el != NULL) {
+ int i;
+
+ sids = talloc_array(mem_ctx, struct lsa_SidPtr,
+ el->num_values);
+
+ if (sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<el->num_values; i++) {
+ struct ldb_message **msgs2;
+ const char * const attrs2[2] = { "objectSid", NULL };
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ ldb_dn_new(mem_ctx, a_state->sam_ctx, (const char *)el->values[i].data),
+ &msgs2, attrs2);
+ if (ret != 1)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ sids[i].sid = samdb_result_dom_sid(mem_ctx, msgs2[0],
+ "objectSid");
+
+ if (sids[i].sid == NULL)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ r->out.sids->num_sids = el->num_values;
+ r->out.sids->sids = sids;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_OpenUser
+*/
+static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenUser *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *account_name;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *u_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.user_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the users SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the user record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n", ret,
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ account_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
+ if (account_name == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, account_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
+ if (!u_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle->data = talloc_steal(u_handle, a_state);
+
+ *r->out.user_handle = u_handle->wire_handle;
+
+ return NT_STATUS_OK;
+
+}
+
+
+/*
+ samr_DeleteUser
+*/
+static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.user_handle = *r->in.user_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != 0) {
+ DEBUG(1, ("Failed to delete user: %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(a_state->sam_ctx)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCTP(r->out.user_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryUserInfo
+*/
+static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryUserInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ int ret;
+ struct ldb_context *sam_ctx;
+
+ const char * const *attrs = NULL;
+
+ r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ /* fill in the reply */
+ switch (r->in.level) {
+ case 1:
+ {
+ static const char * const attrs2[] = {"sAMAccountName", "displayName",
+ "primaryroupID", "description",
+ "comment", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 2:
+ {
+ static const char * const attrs2[] = {"comment", "countryCode", "codePage", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 3:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "objectSid",
+ "primaryGroupID",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "userWorkstations",
+ "lastLogon",
+ "lastLogoff",
+ "pwdLastSet",
+ "logonHours",
+ "badPwdCount",
+ "logonCount",
+ "userAccountControl", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 4:
+ {
+ static const char * const attrs2[] = {"logonHours", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 5:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "objectSid",
+ "primaryGroupID",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "description",
+ "userWorkstations",
+ "lastLogon",
+ "lastLogoff",
+ "logonHours",
+ "badPwdCount",
+ "logonCount",
+ "pwdLastSet",
+ "accountExpires",
+ "userAccountControl",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 6:
+ {
+ static const char * const attrs2[] = {"sAMAccountName", "displayName", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 7:
+ {
+ static const char * const attrs2[] = {"sAMAccountName", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 8:
+ {
+ static const char * const attrs2[] = {"displayName", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 9:
+ {
+ static const char * const attrs2[] = {"primaryGroupID", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 10:
+ {
+ static const char * const attrs2[] = {"homeDirectory", "homeDrive", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 11:
+ {
+ static const char * const attrs2[] = {"scriptPath", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 12:
+ {
+ static const char * const attrs2[] = {"profilePath", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 13:
+ {
+ static const char * const attrs2[] = {"description", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 14:
+ {
+ static const char * const attrs2[] = {"userWorkstations", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 16:
+ {
+ static const char * const attrs2[] = {"userAccountControl", "pwdLastSet", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 17:
+ {
+ static const char * const attrs2[] = {"accountExpires", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 20:
+ {
+ static const char * const attrs2[] = {"userParameters", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 21:
+ {
+ static const char * const attrs2[] = {"lastLogon",
+ "lastLogoff",
+ "pwdLastSet",
+ "accountExpires",
+ "sAMAccountName",
+ "displayName",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "description",
+ "userWorkstations",
+ "comment",
+ "userParameters",
+ "objectSid",
+ "primaryGroupID",
+ "userAccountControl",
+ "logonHours",
+ "badPwdCount",
+ "logonCount",
+ "countryCode",
+ "codePage",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ }
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn ,&res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ r->out.info = talloc(mem_ctx, union samr_UserInfo);
+ if (r->out.info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(r->out.info);
+
+ /* fill in the reply */
+ switch (r->in.level) {
+ case 1:
+ QUERY_STRING(msg, info1.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info1.full_name, "displayName");
+ QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info1.description, "description");
+ QUERY_STRING(msg, info1.comment, "comment");
+ break;
+
+ case 2:
+ QUERY_STRING(msg, info2.comment, "comment");
+ QUERY_UINT (msg, info2.country_code, "countryCode");
+ QUERY_UINT (msg, info2.code_page, "codePage");
+ break;
+
+ case 3:
+ QUERY_STRING(msg, info3.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info3.full_name, "displayName");
+ QUERY_RID (msg, info3.rid, "objectSid");
+ QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info3.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info3.home_drive, "homeDrive");
+ QUERY_STRING(msg, info3.logon_script, "scriptPath");
+ QUERY_STRING(msg, info3.profile_path, "profilePath");
+ QUERY_STRING(msg, info3.workstations, "userWorkstations");
+ QUERY_UINT64(msg, info3.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
+ QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
+ QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
+ QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
+ QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
+ QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info3.logon_count, "logonCount");
+ QUERY_AFLAGS(msg, info3.acct_flags, "userAccountControl");
+ break;
+
+ case 4:
+ QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
+ break;
+
+ case 5:
+ QUERY_STRING(msg, info5.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info5.full_name, "displayName");
+ QUERY_RID (msg, info5.rid, "objectSid");
+ QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info5.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info5.home_drive, "homeDrive");
+ QUERY_STRING(msg, info5.logon_script, "scriptPath");
+ QUERY_STRING(msg, info5.profile_path, "profilePath");
+ QUERY_STRING(msg, info5.description, "description");
+ QUERY_STRING(msg, info5.workstations, "userWorkstations");
+ QUERY_UINT64(msg, info5.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
+ QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
+ QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info5.logon_count, "logonCount");
+ QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
+ QUERY_AFLAGS(msg, info5.acct_flags, "userAccountControl");
+ break;
+
+ case 6:
+ QUERY_STRING(msg, info6.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info6.full_name, "displayName");
+ break;
+
+ case 7:
+ QUERY_STRING(msg, info7.account_name, "sAMAccountName");
+ break;
+
+ case 8:
+ QUERY_STRING(msg, info8.full_name, "displayName");
+ break;
+
+ case 9:
+ QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
+ break;
+
+ case 10:
+ QUERY_STRING(msg, info10.home_directory,"homeDirectory");
+ QUERY_STRING(msg, info10.home_drive, "homeDrive");
+ break;
+
+ case 11:
+ QUERY_STRING(msg, info11.logon_script, "scriptPath");
+ break;
+
+ case 12:
+ QUERY_STRING(msg, info12.profile_path, "profilePath");
+ break;
+
+ case 13:
+ QUERY_STRING(msg, info13.description, "description");
+ break;
+
+ case 14:
+ QUERY_STRING(msg, info14.workstations, "userWorkstations");
+ break;
+
+ case 16:
+ QUERY_AFLAGS(msg, info16.acct_flags, "userAccountControl");
+ break;
+
+ case 17:
+ QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
+ break;
+
+ case 20:
+ QUERY_STRING(msg, info20.parameters, "userParameters");
+ break;
+
+ case 21:
+ QUERY_UINT64(msg, info21.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
+ QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
+ QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
+ QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
+ QUERY_STRING(msg, info21.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info21.full_name, "displayName");
+ QUERY_STRING(msg, info21.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info21.home_drive, "homeDrive");
+ QUERY_STRING(msg, info21.logon_script, "scriptPath");
+ QUERY_STRING(msg, info21.profile_path, "profilePath");
+ QUERY_STRING(msg, info21.description, "description");
+ QUERY_STRING(msg, info21.workstations, "userWorkstations");
+ QUERY_STRING(msg, info21.comment, "comment");
+ QUERY_STRING(msg, info21.parameters, "userParameters");
+ QUERY_RID (msg, info21.rid, "objectSid");
+ QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
+ QUERY_AFLAGS(msg, info21.acct_flags, "userAccountControl");
+ r->out.info->info21.fields_present = 0x00FFFFFF;
+ QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
+ QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info21.logon_count, "logonCount");
+ QUERY_UINT (msg, info21.country_code, "countryCode");
+ QUERY_UINT (msg, info21.code_page, "codePage");
+ break;
+
+
+ default:
+ r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetUserInfo
+*/
+static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetUserInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ int ret;
+ NTSTATUS status = NT_STATUS_OK;
+ struct ldb_context *sam_ctx;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 2:
+ SET_STRING(msg, info2.comment, "comment");
+ SET_UINT (msg, info2.country_code, "countryCode");
+ SET_UINT (msg, info2.code_page, "codePage");
+ break;
+
+ case 4:
+ SET_LHOURS(msg, info4.logon_hours, "logonHours");
+ break;
+
+ case 6:
+ SET_STRING(msg, info6.full_name, "displayName");
+ break;
+
+ case 7:
+ SET_STRING(msg, info7.account_name, "samAccountName");
+ break;
+
+ case 8:
+ SET_STRING(msg, info8.full_name, "displayName");
+ break;
+
+ case 9:
+ SET_UINT(msg, info9.primary_gid, "primaryGroupID");
+ break;
+
+ case 10:
+ SET_STRING(msg, info10.home_directory, "homeDirectory");
+ SET_STRING(msg, info10.home_drive, "homeDrive");
+ break;
+
+ case 11:
+ SET_STRING(msg, info11.logon_script, "scriptPath");
+ break;
+
+ case 12:
+ SET_STRING(msg, info12.profile_path, "profilePath");
+ break;
+
+ case 13:
+ SET_STRING(msg, info13.description, "description");
+ break;
+
+ case 14:
+ SET_STRING(msg, info14.workstations, "userWorkstations");
+ break;
+
+ case 16:
+ SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
+ break;
+
+ case 17:
+ SET_UINT64(msg, info17.acct_expiry, "accountExpires");
+ break;
+
+ case 20:
+ SET_STRING(msg, info20.parameters, "userParameters");
+ break;
+
+ case 21:
+#define IFSET(bit) if (bit & r->in.info->info21.fields_present)
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info21.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info21.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info21.full_name, "displayName");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info21.description, "description");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info21.comment, "comment");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info21.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info21.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_HOME_DIRECTORY)
+ SET_STRING(msg, info21.home_directory, "homeDirectory");
+ IFSET(SAMR_FIELD_HOME_DRIVE)
+ SET_STRING(msg, info21.home_drive, "homeDrive");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info21.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info21.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_STRING(msg, info21.parameters, "userParameters");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info21.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info21.code_page, "codePage");
+#undef IFSET
+ break;
+
+ case 23:
+#define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info23.info.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info23.info.full_name, "displayName");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info23.info.description, "description");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info23.info.comment, "comment");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info23.info.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info23.info.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info23.info.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_STRING(msg, info23.info.parameters, "userParameters");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info23.info.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info23.info.code_page, "codePage");
+ IFSET(SAMR_FIELD_PASSWORD) {
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info23.password);
+ } else IFSET(SAMR_FIELD_PASSWORD2) {
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info23.password);
+ }
+#undef IFSET
+ break;
+
+ /* the set password levels are handled separately */
+ case 24:
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info24.password);
+ break;
+
+ case 25:
+#define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info25.info.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info25.info.full_name, "displayName");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info25.info.description, "description");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info25.info.comment, "comment");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info25.info.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info25.info.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info25.info.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_STRING(msg, info25.info.parameters, "userParameters");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info25.info.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info25.info.code_page, "codePage");
+ IFSET(SAMR_FIELD_PASSWORD) {
+ status = samr_set_password_ex(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info25.password);
+ } else IFSET(SAMR_FIELD_PASSWORD2) {
+ status = samr_set_password_ex(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info25.password);
+ }
+#undef IFSET
+ break;
+
+ /* the set password levels are handled separately */
+ case 26:
+ status = samr_set_password_ex(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info26.password);
+ break;
+
+
+ default:
+ /* many info classes are not valid for SetUserInfo */
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(a_state->sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(a_state->sam_ctx)));
+
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetGroupsForUser
+*/
+static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetGroupsForUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ const char * const attrs[2] = { "objectSid", NULL };
+ struct samr_RidWithAttributeArray *array;
+ int count;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ count = samdb_search_domain(a_state->sam_ctx, mem_ctx, d_state->domain_dn, &res,
+ attrs, d_state->domain_sid,
+ "(&(member=%s)(grouptype=%d)(objectclass=group))",
+ ldb_dn_get_linearized(a_state->account_dn),
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
+ if (array == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ array->count = 0;
+ array->rids = NULL;
+
+ if (count > 0) {
+ int i;
+ array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
+ count);
+
+ if (array->rids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<count; i++) {
+ struct dom_sid *group_sid;
+
+ group_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+ if (group_sid == NULL) {
+ DEBUG(0, ("Couldn't find objectSid attrib\n"));
+ continue;
+ }
+
+ array->rids[array->count].rid =
+ group_sid->sub_auths[group_sid->num_auths-1];
+ array->rids[array->count].attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ array->count += 1;
+ }
+ }
+
+ r->out.rids = array;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryDisplayInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int ldb_cnt, count, i;
+ const char * const attrs[] = { "objectSid", "sAMAccountName", "displayName",
+ "description", "userAccountControl", "pwdLastSet", NULL };
+ struct samr_DispEntryFull *entriesFull = NULL;
+ struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
+ struct samr_DispEntryAscii *entriesAscii = NULL;
+ struct samr_DispEntryGeneral * entriesGeneral = NULL;
+ const char *filter;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ case 4:
+ filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
+ "(sAMAccountType=%u))",
+ ATYPE_NORMAL_ACCOUNT);
+ break;
+ case 2:
+ filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
+ "(sAMAccountType=%u))",
+ ATYPE_WORKSTATION_TRUST);
+ break;
+ case 3:
+ case 5:
+ filter = talloc_asprintf(mem_ctx, "(&(grouptype=%d)"
+ "(objectclass=group))",
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* search for all requested objects in this domain. This could
+ possibly be cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid, "%s", filter);
+ if (ldb_cnt == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ldb_cnt == 0 || r->in.max_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ entriesGeneral = talloc_array(mem_ctx,
+ struct samr_DispEntryGeneral,
+ ldb_cnt);
+ break;
+ case 2:
+ entriesFull = talloc_array(mem_ctx,
+ struct samr_DispEntryFull,
+ ldb_cnt);
+ break;
+ case 3:
+ entriesFullGroup = talloc_array(mem_ctx,
+ struct samr_DispEntryFullGroup,
+ ldb_cnt);
+ break;
+ case 4:
+ case 5:
+ entriesAscii = talloc_array(mem_ctx,
+ struct samr_DispEntryAscii,
+ ldb_cnt);
+ break;
+ }
+
+ if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
+ (entriesAscii == NULL) && (entriesFullGroup == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ count = 0;
+
+ for (i=0; i<ldb_cnt; i++) {
+ struct dom_sid *objectsid;
+
+ objectsid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+ if (objectsid == NULL)
+ continue;
+
+ switch(r->in.level) {
+ case 1:
+ entriesGeneral[count].idx = count + 1;
+ entriesGeneral[count].rid =
+ objectsid->sub_auths[objectsid->num_auths-1];
+ entriesGeneral[count].acct_flags =
+ samdb_result_acct_flags(d_state->sam_ctx, mem_ctx,
+ res[i],
+ d_state->domain_dn);
+ entriesGeneral[count].account_name.string =
+ samdb_result_string(res[i],
+ "sAMAccountName", "");
+ entriesGeneral[count].full_name.string =
+ samdb_result_string(res[i], "displayName", "");
+ entriesGeneral[count].description.string =
+ samdb_result_string(res[i], "description", "");
+ break;
+ case 2:
+ entriesFull[count].idx = count + 1;
+ entriesFull[count].rid =
+ objectsid->sub_auths[objectsid->num_auths-1];
+
+ /* No idea why we need to or in ACB_NORMAL here, but this is what Win2k3 seems to do... */
+ entriesFull[count].acct_flags =
+ samdb_result_acct_flags(d_state->sam_ctx, mem_ctx,
+ res[i],
+ d_state->domain_dn) | ACB_NORMAL;
+ entriesFull[count].account_name.string =
+ samdb_result_string(res[i], "sAMAccountName",
+ "");
+ entriesFull[count].description.string =
+ samdb_result_string(res[i], "description", "");
+ break;
+ case 3:
+ entriesFullGroup[count].idx = count + 1;
+ entriesFullGroup[count].rid =
+ objectsid->sub_auths[objectsid->num_auths-1];
+ /* We get a "7" here for groups */
+ entriesFullGroup[count].acct_flags
+ = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ entriesFullGroup[count].account_name.string =
+ samdb_result_string(res[i], "sAMAccountName",
+ "");
+ entriesFullGroup[count].description.string =
+ samdb_result_string(res[i], "description", "");
+ break;
+ case 4:
+ case 5:
+ entriesAscii[count].idx = count + 1;
+ entriesAscii[count].account_name.string =
+ samdb_result_string(res[i], "sAMAccountName",
+ "");
+ break;
+ }
+
+ count += 1;
+ }
+
+ r->out.total_size = count;
+
+ if (r->in.start_idx >= count) {
+ r->out.returned_size = 0;
+ switch(r->in.level) {
+ case 1:
+ r->out.info.info1.count = r->out.returned_size;
+ r->out.info.info1.entries = NULL;
+ break;
+ case 2:
+ r->out.info.info2.count = r->out.returned_size;
+ r->out.info.info2.entries = NULL;
+ break;
+ case 3:
+ r->out.info.info3.count = r->out.returned_size;
+ r->out.info.info3.entries = NULL;
+ break;
+ case 4:
+ r->out.info.info4.count = r->out.returned_size;
+ r->out.info.info4.entries = NULL;
+ break;
+ case 5:
+ r->out.info.info5.count = r->out.returned_size;
+ r->out.info.info5.entries = NULL;
+ break;
+ }
+ } else {
+ r->out.returned_size = MIN(count - r->in.start_idx,
+ r->in.max_entries);
+ switch(r->in.level) {
+ case 1:
+ r->out.info.info1.count = r->out.returned_size;
+ r->out.info.info1.entries =
+ &(entriesGeneral[r->in.start_idx]);
+ break;
+ case 2:
+ r->out.info.info2.count = r->out.returned_size;
+ r->out.info.info2.entries =
+ &(entriesFull[r->in.start_idx]);
+ break;
+ case 3:
+ r->out.info.info3.count = r->out.returned_size;
+ r->out.info.info3.entries =
+ &(entriesFullGroup[r->in.start_idx]);
+ break;
+ case 4:
+ r->out.info.info4.count = r->out.returned_size;
+ r->out.info.info4.entries =
+ &(entriesAscii[r->in.start_idx]);
+ break;
+ case 5:
+ r->out.info.info5.count = r->out.returned_size;
+ r->out.info.info5.entries =
+ &(entriesAscii[r->in.start_idx]);
+ break;
+ }
+ }
+
+ return (r->out.returned_size < (count - r->in.start_idx)) ?
+ STATUS_MORE_ENTRIES : NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetDisplayEnumerationIndex
+*/
+static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDisplayEnumerationIndex *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_TestPrivateFunctionsDomain
+*/
+static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_TestPrivateFunctionsDomain *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ samr_TestPrivateFunctionsUser
+*/
+static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_TestPrivateFunctionsUser *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ samr_GetUserPwInfo
+*/
+static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetUserPwInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+
+ ZERO_STRUCT(r->out.info);
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ r->out.info.min_password_length = samdb_search_uint(a_state->sam_ctx, mem_ctx, 0,
+ a_state->domain_state->domain_dn, "minPwdLength",
+ NULL);
+ r->out.info.password_properties = samdb_search_uint(a_state->sam_ctx, mem_ctx, 0,
+ a_state->account_dn,
+ "pwdProperties", NULL);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_RemoveMemberFromForeignDomain
+*/
+static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RemoveMemberFromForeignDomain *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ const char *memberdn;
+ struct ldb_message **res;
+ const char * const attrs[3] = { "distinguishedName", "objectSid", NULL };
+ int i, count;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "distinguishedName", "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+ /* Nothing to do */
+ if (memberdn == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ /* TODO: Does this call only remove alias members, or does it do this
+ * for domain groups as well? */
+
+ count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid,
+ "(&(member=%s)(objectClass=group)"
+ "(|(groupType=%d)(groupType=%d)))",
+ memberdn,
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ for (i=0; i<count; i++) {
+ struct ldb_message *mod;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = samdb_result_dn(d_state->sam_ctx, mod, res[i], "distinguishedName", NULL);
+ if (mod->dn == NULL) {
+ talloc_free(mod);
+ continue;
+ }
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
+ "member", memberdn) != 0)
+ return NT_STATUS_NO_MEMORY;
+
+ if (ldb_modify(d_state->sam_ctx, mod) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ talloc_free(mod);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryDomainInfo2
+
+ just an alias for samr_QueryDomainInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDomainInfo2 *r)
+{
+ struct samr_QueryDomainInfo r1;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r1.out);
+ r1.in.domain_handle = r->in.domain_handle;
+ r1.in.level = r->in.level;
+
+ status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
+
+ r->out.info = r1.out.info;
+
+ return status;
+}
+
+
+/*
+ samr_QueryUserInfo2
+
+ just an alias for samr_QueryUserInfo
+*/
+static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryUserInfo2 *r)
+{
+ struct samr_QueryUserInfo r1;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r1.out);
+ r1.in.user_handle = r->in.user_handle;
+ r1.in.level = r->in.level;
+
+ status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
+
+ r->out.info = r1.out.info;
+
+ return status;
+}
+
+
+/*
+ samr_QueryDisplayInfo2
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo2 *r)
+{
+ struct samr_QueryDisplayInfo q;
+ NTSTATUS result;
+
+ q.in.domain_handle = r->in.domain_handle;
+ q.in.level = r->in.level;
+ q.in.start_idx = r->in.start_idx;
+ q.in.max_entries = r->in.max_entries;
+ q.in.buf_size = r->in.buf_size;
+ ZERO_STRUCT(q.out);
+
+ result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
+
+ r->out.total_size = q.out.total_size;
+ r->out.returned_size = q.out.returned_size;
+ r->out.info = q.out.info;
+
+ return result;
+}
+
+
+/*
+ samr_GetDisplayEnumerationIndex2
+*/
+static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDisplayEnumerationIndex2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_QueryDisplayInfo3
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo3 *r)
+{
+ struct samr_QueryDisplayInfo q;
+ NTSTATUS result;
+
+ q.in.domain_handle = r->in.domain_handle;
+ q.in.level = r->in.level;
+ q.in.start_idx = r->in.start_idx;
+ q.in.max_entries = r->in.max_entries;
+ q.in.buf_size = r->in.buf_size;
+ ZERO_STRUCT(q.out);
+
+ result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
+
+ r->out.total_size = q.out.total_size;
+ r->out.returned_size = q.out.returned_size;
+ r->out.info = q.out.info;
+
+ return result;
+}
+
+
+/*
+ samr_AddMultipleMembersToAlias
+*/
+static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddMultipleMembersToAlias *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_RemoveMultipleMembersFromAlias
+*/
+static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RemoveMultipleMembersFromAlias *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_GetDomPwInfo
+
+ this fetches the default password properties for a domain
+
+ note that w2k3 completely ignores the domain name in this call, and
+ always returns the information for the servers primary domain
+*/
+static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDomPwInfo *r)
+{
+ struct ldb_message **msgs;
+ int ret;
+ const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
+ struct ldb_context *sam_ctx;
+
+ ZERO_STRUCT(r->out.info);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* The domain name in this call is ignored */
+ ret = gendb_search_dn(sam_ctx,
+ mem_ctx, NULL, &msgs, attrs);
+ if (ret <= 0) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (ret > 1) {
+ talloc_free(msgs);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.info.min_password_length = samdb_result_uint(msgs[0], "minPwdLength", 0);
+ r->out.info.password_properties = samdb_result_uint(msgs[0], "pwdProperties", 1);
+
+ talloc_free(msgs);
+
+ talloc_free(sam_ctx);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Connect2
+*/
+static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect2 *r)
+{
+ struct samr_Connect c;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_SetUserInfo2
+
+ just an alias for samr_SetUserInfo
+*/
+static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetUserInfo2 *r)
+{
+ struct samr_SetUserInfo r2;
+
+ r2.in.user_handle = r->in.user_handle;
+ r2.in.level = r->in.level;
+ r2.in.info = r->in.info;
+
+ return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
+}
+
+
+/*
+ samr_SetBootKeyInformation
+*/
+static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetBootKeyInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_GetBootKeyInformation
+*/
+static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetBootKeyInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_Connect3
+*/
+static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect3 *r)
+{
+ struct samr_Connect c;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_Connect4
+*/
+static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect4 *r)
+{
+ struct samr_Connect c;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_Connect5
+*/
+static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect5 *r)
+{
+ struct samr_Connect c;
+ NTSTATUS status;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+
+ r->out.info->info1.unknown1 = 3;
+ r->out.info->info1.unknown2 = 0;
+ r->out.level = r->in.level;
+
+ return status;
+}
+
+
+/*
+ samr_RidToSid
+*/
+static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RidToSid *r)
+{
+ struct samr_domain_state *d_state;
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the users SID */
+ r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!r->out.sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetDsrmPassword
+*/
+static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetDsrmPassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_ValidatePassword
+*/
+static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ValidatePassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_samr_s.c"
diff --git a/source4/rpc_server/samr/dcesrv_samr.h b/source4/rpc_server/samr/dcesrv_samr.h
new file mode 100644
index 0000000000..a28a4bec43
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.h
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe - definitions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "param/param.h"
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum samr_handle {
+ SAMR_HANDLE_CONNECT,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_HANDLE_USER,
+ SAMR_HANDLE_GROUP,
+ SAMR_HANDLE_ALIAS
+};
+
+
+/*
+ state asscoiated with a samr_Connect*() operation
+*/
+struct samr_connect_state {
+ void *sam_ctx;
+ uint32_t access_mask;
+};
+
+/*
+ state associated with a samr_OpenDomain() operation
+*/
+struct samr_domain_state {
+ struct samr_connect_state *connect_state;
+ void *sam_ctx;
+ uint32_t access_mask;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ struct ldb_dn *domain_dn;
+ enum server_role role;
+ bool builtin;
+ struct loadparm_context *lp_ctx;
+};
+
+/*
+ state associated with a open account handle
+*/
+struct samr_account_state {
+ struct samr_domain_state *domain_state;
+ void *sam_ctx;
+ uint32_t access_mask;
+ struct dom_sid *account_sid;
+ const char *account_name;
+ struct ldb_dn *account_dn;
+};
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
new file mode 100644
index 0000000000..b78a9ceaa7
--- /dev/null
+++ b/source4/rpc_server/samr/samr_password.c
@@ -0,0 +1,601 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ samr server password set/change handling
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+#include "system/time.h"
+#include "lib/crypto/crypto.h"
+#include "dsdb/common/flags.h"
+#include "libcli/ldap/ldap.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "rpc_server/samr/proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "util/util_ldb.h"
+#include "param/param.h"
+
+/*
+ samr_ChangePasswordUser
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_context *sam_ctx;
+ struct ldb_message **res, *msg;
+ int ret;
+ struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
+ struct samr_Password *lm_pwd, *nt_pwd;
+ NTSTATUS status = NT_STATUS_OK;
+ const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ /* basic sanity checking on parameters. Do this before any database ops */
+ if (!r->in.lm_present || !r->in.nt_present ||
+ !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
+ !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
+ /* we should really handle a change with lm not
+ present */
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* To change a password we need to open as system */
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /* fetch the old hashes */
+ ret = gendb_search_dn(sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+ if (ret != 1) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ msg = res[0];
+
+ status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
+ if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt and check the new lm hash */
+ D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
+ D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt and check the new nt hash */
+ D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
+ D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* The NT Cross is not required by Win2k3 R2, but if present
+ check the nt cross hash */
+ if (r->in.cross1_present && r->in.nt_cross) {
+ D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ /* The LM Cross is not required by Win2k3 R2, but if present
+ check the lm cross hash */
+ if (r->in.cross2_present && r->in.lm_cross) {
+ D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(msg, a_state->account_dn);
+ if (!msg->dn) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup password modify mods on the user DN specified. This may fail
+ * due to password policies. */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ a_state->account_dn, a_state->domain_state->domain_dn,
+ msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
+ true, /* this is a user password change */
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ return status;
+ }
+
+ /* The above call only setup the modifications, this actually
+ * makes the write to the database. */
+ ret = samdb_replace(sam_ctx, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(2,("Failed to modify record to change password on %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(sam_ctx)));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_OemChangePasswordUser2
+*/
+NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OemChangePasswordUser2 *r)
+{
+ NTSTATUS status;
+ char new_pass[512];
+ uint32_t new_pass_len;
+ struct samr_CryptPassword *pwbuf = r->in.password;
+ struct ldb_context *sam_ctx;
+ struct ldb_dn *user_dn;
+ int ret;
+ struct ldb_message **res, *mod;
+ const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
+ struct samr_Password *lm_pwd;
+ DATA_BLOB lm_pwd_blob;
+ uint8_t new_lm_hash[16];
+ struct samr_Password lm_verifier;
+
+ if (pwbuf == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.hash == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* To change a password we need to open as system */
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /* we need the users dn and the domain dn (derived from the
+ user SID). We also need the current lm password hash in
+ order to decrypt the incoming password */
+ ret = gendb_search(sam_ctx,
+ mem_ctx, NULL, &res, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account->string);
+ if (ret != 1) {
+ ldb_transaction_cancel(sam_ctx);
+ /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ user_dn = res[0]->dn;
+
+ status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
+ if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt the password we have been given */
+ lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
+ arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
+ data_blob_free(&lm_pwd_blob);
+
+ if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_ASCII)) {
+ ldb_transaction_cancel(sam_ctx);
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* check LM verifier */
+ if (lm_pwd == NULL) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ E_deshash(new_pass, new_lm_hash);
+ E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
+ if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = ldb_dn_copy(mod, user_dn);
+ if (!mod->dn) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set the password on the user DN specified. This may fail
+ * due to password policies */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ user_dn, NULL,
+ mod, new_pass,
+ NULL, NULL,
+ true, /* this is a user password change */
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ return status;
+ }
+
+ /* The above call only setup the modifications, this actually
+ * makes the write to the database. */
+ ret = samdb_replace(sam_ctx, mem_ctx, mod);
+ if (ret != 0) {
+ DEBUG(2,("Failed to modify record to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_ChangePasswordUser3
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser3 *r)
+{
+ NTSTATUS status;
+ char new_pass[512];
+ uint32_t new_pass_len;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_dn *user_dn;
+ int ret;
+ struct ldb_message **res, *mod;
+ const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
+ struct samr_Password *nt_pwd, *lm_pwd;
+ DATA_BLOB nt_pwd_blob;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+ enum samr_RejectReason reason = SAMR_REJECT_OTHER;
+ uint8_t new_nt_hash[16], new_lm_hash[16];
+ struct samr_Password nt_verifier, lm_verifier;
+
+ ZERO_STRUCT(r->out);
+
+ if (r->in.nt_password == NULL ||
+ r->in.nt_verifier == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* To change a password we need to open as system */
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret) {
+ talloc_free(sam_ctx);
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /* we need the users dn and the domain dn (derived from the
+ user SID). We also need the current lm and nt password hashes
+ in order to decrypt the incoming passwords */
+ ret = gendb_search(sam_ctx,
+ mem_ctx, NULL, &res, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account->string);
+ if (ret != 1) {
+ /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ user_dn = res[0]->dn;
+
+ status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
+ if (!NT_STATUS_IS_OK(status) ) {
+ goto failed;
+ }
+
+ if (!nt_pwd) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* decrypt the password we have been given */
+ nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
+ arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
+ data_blob_free(&nt_pwd_blob);
+
+ if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_UNICODE)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ if (r->in.nt_verifier == NULL) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* check NT verifier */
+ E_md4hash(new_pass, new_nt_hash);
+ E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
+ if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* check LM verifier */
+ if (lm_pwd && r->in.lm_verifier != NULL) {
+ E_deshash(new_pass, new_lm_hash);
+ E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
+ if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+ }
+
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = ldb_dn_copy(mod, user_dn);
+ if (!mod->dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ /* set the password on the user DN specified. This may fail
+ * due to password policies */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ user_dn, NULL,
+ mod, new_pass,
+ NULL, NULL,
+ true, /* this is a user password change */
+ &reason,
+ &dominfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* The above call only setup the modifications, this actually
+ * makes the write to the database. */
+ ret = samdb_replace(sam_ctx, mem_ctx, mod);
+ if (ret != 0) {
+ DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ status = NT_STATUS_TRANSACTION_ABORTED;
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ ldb_transaction_cancel(sam_ctx);
+ talloc_free(sam_ctx);
+
+ reject = talloc(mem_ctx, struct samr_ChangeReject);
+ r->out.dominfo = dominfo;
+ r->out.reject = reject;
+
+ if (reject == NULL) {
+ return status;
+ }
+ ZERO_STRUCTP(reject);
+
+ reject->reason = reason;
+
+ return status;
+}
+
+
+/*
+ samr_ChangePasswordUser2
+
+ easy - just a subset of samr_ChangePasswordUser3
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser2 *r)
+{
+ struct samr_ChangePasswordUser3 r2;
+
+ r2.in.server = r->in.server;
+ r2.in.account = r->in.account;
+ r2.in.nt_password = r->in.nt_password;
+ r2.in.nt_verifier = r->in.nt_verifier;
+ r2.in.lm_change = r->in.lm_change;
+ r2.in.lm_password = r->in.lm_password;
+ r2.in.lm_verifier = r->in.lm_verifier;
+ r2.in.password3 = NULL;
+
+ return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
+}
+
+
+/*
+ set password via a samr_CryptPassword buffer
+ this will in the 'msg' with modify operations that will update the user
+ password when applied
+*/
+NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
+ void *sam_ctx,
+ struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct samr_CryptPassword *pwbuf)
+{
+ NTSTATUS nt_status;
+ char new_pass[512];
+ uint32_t new_pass_len;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ arcfour_crypt_blob(pwbuf->data, 516, &session_key);
+
+ if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_UNICODE)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ return samdb_set_password(sam_ctx, mem_ctx,
+ account_dn, domain_dn,
+ msg, new_pass,
+ NULL, NULL,
+ false, /* This is a password set, not change */
+ NULL, NULL);
+}
+
+
+/*
+ set password via a samr_CryptPasswordEx buffer
+ this will in the 'msg' with modify operations that will update the user
+ password when applied
+*/
+NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
+ struct ldb_context *sam_ctx,
+ struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct samr_CryptPasswordEx *pwbuf)
+{
+ NTSTATUS nt_status;
+ char new_pass[512];
+ uint32_t new_pass_len;
+ DATA_BLOB co_session_key;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ struct MD5Context ctx;
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ if (!co_session_key.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, &pwbuf->data[516], 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(co_session_key.data, &ctx);
+
+ arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
+
+ if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
+ &new_pass_len, STR_UNICODE)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ return samdb_set_password(sam_ctx, mem_ctx,
+ account_dn, domain_dn,
+ msg, new_pass,
+ NULL, NULL,
+ false, /* This is a password set, not change */
+ NULL, NULL);
+}
+
+
diff --git a/source4/rpc_server/service_rpc.c b/source4/rpc_server/service_rpc.c
new file mode 100644
index 0000000000..b68cec4c7d
--- /dev/null
+++ b/source4/rpc_server/service_rpc.c
@@ -0,0 +1,486 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ smbd-specific dcerpc server code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2004,2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+#include "lib/events/events.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "system/filesys.h"
+#include "libcli/security/security.h"
+#include "lib/socket/socket.h"
+#include "lib/messaging/irpc.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+struct dcesrv_socket_context {
+ const struct dcesrv_endpoint *endpoint;
+ struct dcesrv_context *dcesrv_ctx;
+};
+
+/*
+ write_fn callback for dcesrv_output()
+*/
+static NTSTATUS dcerpc_write_fn(void *private_data, DATA_BLOB *out, size_t *nwritten)
+{
+ NTSTATUS status;
+ struct socket_context *sock = talloc_get_type(private_data, struct socket_context);
+ size_t sendlen;
+
+ status = socket_send(sock, out, &sendlen);
+ NT_STATUS_IS_ERR_RETURN(status);
+
+ *nwritten = sendlen;
+ return status;
+}
+
+static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dce_conn->transport.private_data,
+ struct stream_connection);
+
+ stream_terminate_connection(srv_conn, reason);
+}
+
+static void dcesrv_sock_report_output_data(struct dcesrv_connection *dcesrv_conn)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dcesrv_conn->transport.private_data,
+ struct stream_connection);
+
+ if (srv_conn && srv_conn->event.fde) {
+ EVENT_FD_WRITEABLE(srv_conn->event.fde);
+ }
+}
+
+static struct socket_address *dcesrv_sock_get_my_addr(struct dcesrv_connection *dcesrv_conn, TALLOC_CTX *mem_ctx)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dcesrv_conn->transport.private_data,
+ struct stream_connection);
+
+ return socket_get_my_addr(srv_conn->socket, mem_ctx);
+}
+
+static struct socket_address *dcesrv_sock_get_peer_addr(struct dcesrv_connection *dcesrv_conn, TALLOC_CTX *mem_ctx)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dcesrv_conn->transport.private_data,
+ struct stream_connection);
+
+ return socket_get_peer_addr(srv_conn->socket, mem_ctx);
+}
+
+static void dcesrv_sock_accept(struct stream_connection *srv_conn)
+{
+ NTSTATUS status;
+ struct dcesrv_socket_context *dcesrv_sock =
+ talloc_get_type(srv_conn->private, struct dcesrv_socket_context);
+ struct dcesrv_connection *dcesrv_conn = NULL;
+
+ if (!srv_conn->session_info) {
+ status = auth_anonymous_session_info(srv_conn,
+ srv_conn->event.ctx,
+ srv_conn->lp_ctx,
+ &srv_conn->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: auth_anonymous_session_info failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+ status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
+ srv_conn,
+ dcesrv_sock->endpoint,
+ srv_conn->session_info,
+ srv_conn->event.ctx,
+ srv_conn->msg_ctx,
+ srv_conn->server_id,
+ DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+ &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ dcesrv_conn->transport.private_data = srv_conn;
+ dcesrv_conn->transport.report_output_data = dcesrv_sock_report_output_data;
+ dcesrv_conn->transport.get_my_addr = dcesrv_sock_get_my_addr;
+ dcesrv_conn->transport.get_peer_addr = dcesrv_sock_get_peer_addr;
+
+ srv_conn->private = dcesrv_conn;
+
+ irpc_add_name(srv_conn->msg_ctx, "rpc_server");
+
+ return;
+}
+
+static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags)
+{
+ NTSTATUS status;
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private, struct dcesrv_connection);
+ DATA_BLOB tmp_blob;
+ size_t nread;
+
+ if (dce_conn->processing) {
+ EVENT_FD_NOT_READABLE(conn->event.fde);
+ return;
+ }
+
+ tmp_blob = data_blob_talloc(conn->socket, NULL, 0x1000);
+ if (tmp_blob.data == NULL) {
+ dcesrv_terminate_connection(dce_conn, "out of memory");
+ return;
+ }
+
+ status = socket_recv(conn->socket, tmp_blob.data, tmp_blob.length, &nread);
+ if (NT_STATUS_IS_ERR(status)) {
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+ if (nread == 0) {
+ talloc_free(tmp_blob.data);
+ return;
+ }
+
+ tmp_blob.length = nread;
+
+ dce_conn->processing = true;
+ status = dcesrv_input(dce_conn, &tmp_blob);
+ dce_conn->processing = false;
+ talloc_free(tmp_blob.data);
+
+ EVENT_FD_READABLE(conn->event.fde);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+
+ if (dce_conn->call_list && dce_conn->call_list->replies) {
+ EVENT_FD_WRITEABLE(conn->event.fde);
+ }
+}
+
+static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private, struct dcesrv_connection);
+ NTSTATUS status;
+
+ status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn);
+ if (NT_STATUS_IS_ERR(status)) {
+ dcesrv_terminate_connection(dce_conn, "eof on socket");
+ return;
+ }
+
+ if (!dce_conn->call_list || !dce_conn->call_list->replies) {
+ EVENT_FD_NOT_WRITEABLE(conn->event.fde);
+ }
+}
+
+
+static const struct stream_server_ops dcesrv_stream_ops = {
+ .name = "rpc",
+ .accept_connection = dcesrv_sock_accept,
+ .recv_handler = dcesrv_sock_recv,
+ .send_handler = dcesrv_sock_send,
+};
+
+
+
+static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ NTSTATUS status;
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", e->ep_description->endpoint, &port,
+ lp_socket_options(lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+ e->ep_description->endpoint, nt_errstr(status)));
+ }
+
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ char *full_path;
+ NTSTATUS status;
+
+ if (!e->ep_description->endpoint) {
+ /* No identifier specified: use DEFAULT.
+ * DO NOT hardcode this value anywhere else. Rather, specify
+ * no endpoint and let the epmapper worry about it. */
+ e->ep_description->endpoint = talloc_strdup(dce_ctx, "DEFAULT");
+ }
+
+ full_path = talloc_asprintf(dce_ctx, "%s/%s", lp_ncalrpc_dir(lp_ctx),
+ e->ep_description->endpoint);
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", full_path, &port,
+ lp_socket_options(lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
+ e->ep_description->endpoint, full_path, nt_errstr(status)));
+ }
+ return status;
+}
+
+
+/*
+ add a socket address to the list of events, one event per dcerpc endpoint
+*/
+static NTSTATUS add_socket_rpc_pipe_iface(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ NTSTATUS status;
+
+ if (e->ep_description->endpoint == NULL) {
+ DEBUG(0, ("Endpoint mandatory for named pipes\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = NT_STATUS_OK;
+#if 0
+
+ status = stream_setup_smb_pipe(event_ctx, model_ops, &dcesrv_stream_ops,
+ e->ep_description->endpoint, dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+ e->ep_description->endpoint, nt_errstr(status)));
+ }
+#endif
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+ NTSTATUS status;
+
+ status = add_socket_rpc_pipe_iface(dce_ctx, e, event_ctx, model_ops);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return status;
+}
+
+/*
+ add a socket address to the list of events, one event per dcerpc endpoint
+*/
+static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops,
+ const char *address)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 0;
+ NTSTATUS status;
+
+ if (e->ep_description->endpoint) {
+ port = atoi(e->ep_description->endpoint);
+ }
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(event_ctx, dce_ctx->lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(dce_ctx->lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n",
+ address, port, nt_errstr(status)));
+ }
+
+ if (e->ep_description->endpoint == NULL) {
+ e->ep_description->endpoint = talloc_asprintf(dce_ctx, "%d", port);
+ }
+
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+ NTSTATUS status;
+
+ /* Add TCP/IP sockets */
+ if (lp_interfaces(lp_ctx) && lp_bind_interfaces_only(lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(dce_ctx, lp_interfaces(lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, address);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+ } else {
+ status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops,
+ lp_socket_address(lp_ctx));
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+ switch (e->ep_description->transport) {
+ case NCACN_UNIX_STREAM:
+ return dcesrv_add_ep_unix(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ case NCALRPC:
+ return dcesrv_add_ep_ncalrpc(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ case NCACN_IP_TCP:
+ return dcesrv_add_ep_tcp(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ case NCACN_NP:
+ return dcesrv_add_ep_np(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+}
+
+/*
+ open the dcerpc server sockets
+*/
+static void dcesrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ struct dcesrv_endpoint *e;
+
+ task_server_set_title(task, "task[dcesrv]");
+
+ status = dcesrv_init_context(task->event_ctx,
+ task->lp_ctx,
+ lp_dcerpc_endpoint_servers(task->lp_ctx),
+ &dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* Make sure the directory for NCALRPC exists */
+ if (!directory_exist(lp_ncalrpc_dir(task->lp_ctx))) {
+ mkdir(lp_ncalrpc_dir(task->lp_ctx), 0755);
+ }
+
+ for (e=dce_ctx->endpoint_list;e;e=e->next) {
+ status = dcesrv_add_ep(dce_ctx, task->lp_ctx, e, task->event_ctx, task->model_ops);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ return;
+failed:
+ task_server_terminate(task, "Failed to startup dcerpc server task");
+}
+
+NTSTATUS server_service_rpc_init(void)
+{
+ extern NTSTATUS dcerpc_server_wkssvc_init(void);
+ extern NTSTATUS dcerpc_server_drsuapi_init(void);
+ extern NTSTATUS dcerpc_server_winreg_init(void);
+ extern NTSTATUS dcerpc_server_spoolss_init(void);
+ extern NTSTATUS dcerpc_server_epmapper_init(void);
+ extern NTSTATUS dcerpc_server_srvsvc_init(void);
+ extern NTSTATUS dcerpc_server_netlogon_init(void);
+ extern NTSTATUS dcerpc_server_rpcecho_init(void);
+ extern NTSTATUS dcerpc_server_unixinfo_init(void);
+ extern NTSTATUS dcerpc_server_samr_init(void);
+ extern NTSTATUS dcerpc_server_remote_init(void);
+ extern NTSTATUS dcerpc_server_lsa_init(void);
+ init_module_fn static_init[] = { STATIC_DCESRV_MODULES };
+ init_module_fn *shared_init = load_samba_modules(NULL, global_loadparm, "dcerpc_server");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ return register_server_service("rpc", dcesrv_task_init);
+}
diff --git a/source4/rpc_server/spoolss/dcesrv_spoolss.c b/source4/rpc_server/spoolss/dcesrv_spoolss.c
new file mode 100644
index 0000000000..28e30002e2
--- /dev/null
+++ b/source4/rpc_server/spoolss/dcesrv_spoolss.c
@@ -0,0 +1,1560 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the spoolss pipe
+
+ Copyright (C) Tim Potter 2004
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "rpc_server/common/common.h"
+#include "ntptr/ntptr.h"
+#include "lib/socket/socket.h"
+#include "smbd/service_stream.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+enum spoolss_handle {
+ SPOOLSS_NOTIFY
+};
+
+#define SPOOLSS_BUFFER_UNION(fn,info,level) \
+ ((info)?ndr_size_##fn(info, level, 0):0)
+
+#define SPOOLSS_BUFFER_UNION_ARRAY(fn,info,level,count) \
+ ((info)?ndr_size_##fn##_info(dce_call, level, count, info):0)
+
+#define SPOOLSS_BUFFER_OK(val_true,val_false) ((r->in.offered >= r->out.needed)?val_true:val_false)
+
+static WERROR dcesrv_spoolss_parse_printer_name(TALLOC_CTX *mem_ctx, const char *name,
+ const char **_server_name,
+ const char **_object_name,
+ enum ntptr_HandleType *_object_type)
+{
+ char *p;
+ char *server = NULL;
+ char *server_unc = NULL;
+ const char *object = name;
+
+ /* no printername is there it's like open server */
+ if (!name) {
+ *_server_name = NULL;
+ *_object_name = NULL;
+ *_object_type = NTPTR_HANDLE_SERVER;
+ return WERR_OK;
+ }
+
+ /* just "\\" is invalid */
+ if (strequal("\\\\", name)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ if (strncmp("\\\\", name, 2) == 0) {
+ server_unc = talloc_strdup(mem_ctx, name);
+ W_ERROR_HAVE_NO_MEMORY(server_unc);
+ server = server_unc + 2;
+
+ /* here we know we have "\\" in front not followed
+ * by '\0', now see if we have another "\" in the string
+ */
+ p = strchr_m(server, '\\');
+ if (!p) {
+ /* there's no other "\", so it's ("\\%s",server)
+ */
+ *_server_name = server_unc;
+ *_object_name = NULL;
+ *_object_type = NTPTR_HANDLE_SERVER;
+ return WERR_OK;
+ }
+ /* here we know that we have ("\\%s\",server),
+ * if we have '\0' as next then it's an invalid name
+ * otherwise the printer_name
+ */
+ p[0] = '\0';
+ /* everything that follows is the printer name */
+ p++;
+ object = p;
+
+ /* just "" as server is invalid */
+ if (strequal(server, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+ }
+
+ /* just "" is invalid */
+ if (strequal(object, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+#define XCV_PORT ",XcvPort "
+#define XCV_MONITOR ",XcvMonitor "
+ if (strncmp(object, XCV_PORT, strlen(XCV_PORT)) == 0) {
+ object += strlen(XCV_PORT);
+
+ /* just "" is invalid */
+ if (strequal(object, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ *_server_name = server_unc;
+ *_object_name = object;
+ *_object_type = NTPTR_HANDLE_PORT;
+ return WERR_OK;
+ } else if (strncmp(object, XCV_MONITOR, strlen(XCV_MONITOR)) == 0) {
+ object += strlen(XCV_MONITOR);
+
+ /* just "" is invalid */
+ if (strequal(object, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ *_server_name = server_unc;
+ *_object_name = object;
+ *_object_type = NTPTR_HANDLE_MONITOR;
+ return WERR_OK;
+ }
+
+ *_server_name = server_unc;
+ *_object_name = object;
+ *_object_type = NTPTR_HANDLE_PRINTER;
+ return WERR_OK;
+}
+
+/*
+ * Check server_name is:
+ * - "" , functions that don't allow "",
+ * should check that on their own, before calling this function
+ * - our name (only netbios yet, TODO: need to test dns name!)
+ * - our ip address of the current use socket
+ * otherwise return WERR_INVALID_PRINTER_NAME
+ */
+static WERROR dcesrv_spoolss_check_server_name(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ const char *server_name)
+{
+ bool ret;
+ struct socket_address *myaddr;
+ const char **aliases;
+ int i;
+
+ /* NULL is ok */
+ if (!server_name) return WERR_OK;
+
+ /* "" is ok */
+ ret = strequal("",server_name);
+ if (ret) return WERR_OK;
+
+ /* just "\\" is invalid */
+ if (strequal("\\\\", server_name)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ /* then we need "\\" */
+ if (strncmp("\\\\", server_name, 2) != 0) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ server_name += 2;
+
+ /* NETBIOS NAME is ok */
+ ret = strequal(lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx), server_name);
+ if (ret) return WERR_OK;
+
+ aliases = lp_netbios_aliases(dce_call->conn->dce_ctx->lp_ctx);
+
+ for (i=0; aliases && aliases[i]; i++) {
+ if (strequal(aliases[i], server_name)) {
+ return WERR_OK;
+ }
+ }
+
+ /* DNS NAME is ok
+ * TODO: we need to check if aliases are also ok
+ */
+ if (lp_realm(dce_call->conn->dce_ctx->lp_ctx)) {
+ char *str;
+
+ str = talloc_asprintf(mem_ctx, "%s.%s",
+ lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx),
+ lp_realm(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ ret = strequal(str, server_name);
+ talloc_free(str);
+ if (ret) return WERR_OK;
+ }
+
+ myaddr = dcesrv_connection_get_my_addr(dce_call->conn, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(myaddr);
+
+ ret = strequal(myaddr->addr, server_name);
+ talloc_free(myaddr);
+ if (ret) return WERR_OK;
+
+ return WERR_INVALID_PRINTER_NAME;
+}
+
+static NTSTATUS dcerpc_spoolss_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface)
+{
+ NTSTATUS status;
+ struct ntptr_context *ntptr;
+
+ status = ntptr_init_context(dce_call->context, dce_call->conn->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ lp_ntptr_providor(dce_call->conn->dce_ctx->lp_ctx), &ntptr);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ dce_call->context->private = ntptr;
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_SPOOLSS_BIND dcerpc_spoolss_bind
+
+/*
+ spoolss_EnumPrinters
+*/
+static WERROR dcesrv_spoolss_EnumPrinters(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinters *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private, struct ntptr_context);
+ WERROR status;
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumPrinters(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumPrinters, r->out.info, r->in.level, r->out.count);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ r->out.count = SPOOLSS_BUFFER_OK(r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+static WERROR dcesrv_spoolss_OpenPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r);
+/*
+ spoolss_OpenPrinter
+*/
+static WERROR dcesrv_spoolss_OpenPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinter *r)
+{
+ WERROR status;
+ struct spoolss_OpenPrinterEx *r2;
+
+ r2 = talloc(mem_ctx, struct spoolss_OpenPrinterEx);
+ W_ERROR_HAVE_NO_MEMORY(r2);
+
+ r2->in.printername = r->in.printername;
+ r2->in.datatype = r->in.datatype;
+ r2->in.devmode_ctr = r->in.devmode_ctr;
+ r2->in.access_mask = r->in.access_mask;
+ r2->in.level = 1;
+ r2->in.userlevel.level1 = NULL;
+
+ r2->out.handle = r->out.handle;
+
+ /* TODO: we should take care about async replies here,
+ if spoolss_OpenPrinterEx() would be async!
+ */
+ status = dcesrv_spoolss_OpenPrinterEx(dce_call, mem_ctx, r2);
+
+ r->out.handle = r2->out.handle;
+
+ return status;
+}
+
+
+/*
+ spoolss_SetJob
+*/
+static WERROR dcesrv_spoolss_SetJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetJob
+*/
+static WERROR dcesrv_spoolss_GetJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumJobs
+*/
+static WERROR dcesrv_spoolss_EnumJobs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumJobs *r)
+{
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_AddPrinter
+*/
+static WERROR dcesrv_spoolss_AddPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinter
+*/
+static WERROR dcesrv_spoolss_DeletePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_SetPrinter
+*/
+static WERROR dcesrv_spoolss_SetPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinter
+*/
+static WERROR dcesrv_spoolss_GetPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrinterDriver
+*/
+static WERROR dcesrv_spoolss_AddPrinterDriver(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterDriver *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterDrivers
+*/
+static WERROR dcesrv_spoolss_EnumPrinterDrivers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDrivers *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private, struct ntptr_context);
+ WERROR status;
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumPrinterDrivers(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumPrinterDrivers, r->out.info, r->in.level, r->out.count);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ r->out.count = SPOOLSS_BUFFER_OK(r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_GetPrinterDriver
+*/
+static WERROR dcesrv_spoolss_GetPrinterDriver(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriver *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterDriverDirectory
+*/
+static WERROR dcesrv_spoolss_GetPrinterDriverDirectory(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriverDirectory *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private, struct ntptr_context);
+ WERROR status;
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_GetPrinterDriverDirectory(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_DriverDirectoryInfo, r->out.info, r->in.level);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_DeletePrinterDriver
+*/
+static WERROR dcesrv_spoolss_DeletePrinterDriver(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDriver *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrintProcessor
+*/
+static WERROR dcesrv_spoolss_AddPrintProcessor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrintProcessor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrintProcessors
+*/
+static WERROR dcesrv_spoolss_EnumPrintProcessors(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrintProcessors *r)
+{
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_GetPrintProcessorDirectory
+*/
+static WERROR dcesrv_spoolss_GetPrintProcessorDirectory(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrintProcessorDirectory *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_StartDocPrinter
+*/
+static WERROR dcesrv_spoolss_StartDocPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartDocPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_StartPagePrinter
+*/
+static WERROR dcesrv_spoolss_StartPagePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartPagePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_WritePrinter
+*/
+static WERROR dcesrv_spoolss_WritePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_WritePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EndPagePrinter
+*/
+static WERROR dcesrv_spoolss_EndPagePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndPagePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AbortPrinter
+*/
+static WERROR dcesrv_spoolss_AbortPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AbortPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ReadPrinter
+*/
+static WERROR dcesrv_spoolss_ReadPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReadPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EndDocPrinter
+*/
+static WERROR dcesrv_spoolss_EndDocPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndDocPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddJob
+*/
+static WERROR dcesrv_spoolss_AddJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ScheduleJob
+*/
+static WERROR dcesrv_spoolss_ScheduleJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ScheduleJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterData
+*/
+static WERROR dcesrv_spoolss_GetPrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_GetPrintServerData(handle, mem_ctx, r);
+ break;
+ default:
+ status = WERR_FOOBAR;
+ break;
+ }
+
+ W_ERROR_NOT_OK_RETURN(status);
+
+ r->out.needed = ndr_size_spoolss_PrinterData(&r->out.data, r->out.type, 0);
+ r->out.type = SPOOLSS_BUFFER_OK(r->out.type, SPOOLSS_PRINTER_DATA_TYPE_NULL);
+ r->out.data = SPOOLSS_BUFFER_OK(r->out.data, r->out.data);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_MORE_DATA);
+}
+
+
+/*
+ spoolss_SetPrinterData
+*/
+static WERROR dcesrv_spoolss_SetPrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_WaitForPrinterChange
+*/
+static WERROR dcesrv_spoolss_WaitForPrinterChange(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_WaitForPrinterChange *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ClosePrinter
+*/
+static WERROR dcesrv_spoolss_ClosePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ClosePrinter *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_AddForm
+*/
+static WERROR dcesrv_spoolss_AddForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_AddPrintServerForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_AddPrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_DeleteForm
+*/
+static WERROR dcesrv_spoolss_DeleteForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_DeletePrintServerForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_DeletePrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_GetForm
+*/
+static WERROR dcesrv_spoolss_GetForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ /*
+ * stupid, but w2k3 returns WERR_BADFID here?
+ */
+ return WERR_BADFID;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_GetPrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_FormInfo, r->out.info, r->in.level);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_SetForm
+*/
+static WERROR dcesrv_spoolss_SetForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_SetPrintServerForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_SetPrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_EnumForms
+*/
+static WERROR dcesrv_spoolss_EnumForms(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_EnumPrintServerForms(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_EnumPrinterForms(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumForms, r->out.info, r->in.level, r->out.count);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ r->out.count = SPOOLSS_BUFFER_OK(r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_EnumPorts
+*/
+static WERROR dcesrv_spoolss_EnumPorts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPorts *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private, struct ntptr_context);
+ WERROR status;
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.servername);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumPorts(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumPorts, r->out.info, r->in.level, r->out.count);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ r->out.count = SPOOLSS_BUFFER_OK(r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_EnumMonitors
+*/
+static WERROR dcesrv_spoolss_EnumMonitors(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumMonitors *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private, struct ntptr_context);
+ WERROR status;
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.servername);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumMonitors(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumMonitors, r->out.info, r->in.level, r->out.count);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ r->out.count = SPOOLSS_BUFFER_OK(r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_AddPort
+*/
+static WERROR dcesrv_spoolss_AddPort(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPort *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ spoolss_ConfigurePort
+*/
+static WERROR dcesrv_spoolss_ConfigurePort(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ConfigurePort *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePort
+*/
+static WERROR dcesrv_spoolss_DeletePort(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePort *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_CreatePrinterIC
+*/
+static WERROR dcesrv_spoolss_CreatePrinterIC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_CreatePrinterIC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_PlayGDIScriptOnPrinterIC
+*/
+static WERROR dcesrv_spoolss_PlayGDIScriptOnPrinterIC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_PlayGDIScriptOnPrinterIC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterIC
+*/
+static WERROR dcesrv_spoolss_DeletePrinterIC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterIC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrinterConnection
+*/
+static WERROR dcesrv_spoolss_AddPrinterConnection(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterConnection *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterConnection
+*/
+static WERROR dcesrv_spoolss_DeletePrinterConnection(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterConnection *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_PrinterMessageBox
+*/
+static WERROR dcesrv_spoolss_PrinterMessageBox(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_PrinterMessageBox *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddMonitor
+*/
+static WERROR dcesrv_spoolss_AddMonitor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddMonitor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeleteMonitor
+*/
+static WERROR dcesrv_spoolss_DeleteMonitor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteMonitor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrintProcessor
+*/
+static WERROR dcesrv_spoolss_DeletePrintProcessor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrintProcessor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrintProvidor
+*/
+static WERROR dcesrv_spoolss_AddPrintProvidor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrintProvidor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrintProvidor
+*/
+static WERROR dcesrv_spoolss_DeletePrintProvidor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrintProvidor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrintProcDataTypes
+*/
+static WERROR dcesrv_spoolss_EnumPrintProcDataTypes(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrintProcDataTypes *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ResetPrinter
+*/
+static WERROR dcesrv_spoolss_ResetPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ResetPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterDriver2
+*/
+static WERROR dcesrv_spoolss_GetPrinterDriver2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriver2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_FindFirstPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_FindFirstPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_FindFirstPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_FindNextPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_FindNextPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_FindNextPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_FindClosePrinterNotify
+*/
+static WERROR dcesrv_spoolss_FindClosePrinterNotify(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_FindClosePrinterNotify *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RouterFindFirstPrinterChangeNotificationOld
+*/
+static WERROR dcesrv_spoolss_RouterFindFirstPrinterChangeNotificationOld(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterFindFirstPrinterChangeNotificationOld *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ReplyOpenPrinter
+*/
+static WERROR dcesrv_spoolss_ReplyOpenPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReplyOpenPrinter *r)
+{
+ struct dcesrv_handle *handle;
+
+ handle = dcesrv_handle_new(dce_call->context, SPOOLSS_NOTIFY);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+
+ /* For now, just return a handle */
+
+ *r->out.handle = handle->wire_handle;
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_RouterReplyPrinter
+*/
+static WERROR dcesrv_spoolss_RouterReplyPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterReplyPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ReplyClosePrinter
+*/
+static WERROR dcesrv_spoolss_ReplyClosePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReplyClosePrinter *r)
+{
+ struct dcesrv_handle *handle;
+
+ DCESRV_PULL_HANDLE_WERR(handle, r->in.handle, SPOOLSS_NOTIFY);
+
+ talloc_free(handle);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*
+ spoolss_AddPortEx
+*/
+static WERROR dcesrv_spoolss_AddPortEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPortEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RouterFindFirstPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_RouterFindFirstPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterFindFirstPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_SpoolerInit
+*/
+static WERROR dcesrv_spoolss_SpoolerInit(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SpoolerInit *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ResetPrinterEx
+*/
+static WERROR dcesrv_spoolss_ResetPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ResetPrinterEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RemoteFindFirstPrinterChangeNotifyEx
+*/
+static WERROR dcesrv_spoolss_RemoteFindFirstPrinterChangeNotifyEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RemoteFindFirstPrinterChangeNotifyEx *r)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding *binding;
+ NTSTATUS status;
+ struct spoolss_ReplyOpenPrinter rop;
+ struct cli_credentials *creds;
+ struct policy_handle notify_handle;
+
+ DEBUG(2, ("Received RFFPCNex from %s\n", r->in.str));
+
+ /*
+ * TODO: for now just open a connection to the client and drop it again
+ * to keep the w2k3 PrintServer
+ * happy to allow to open the Add Printer GUI
+ * and the torture suite passing
+ */
+
+ binding = talloc_zero(mem_ctx, struct dcerpc_binding);
+
+ binding->transport = NCACN_NP;
+ if (strncmp(r->in.str, "\\\\", 2))
+ return WERR_INVALID_COMPUTERNAME;
+ binding->host = r->in.str+2;
+
+ creds = cli_credentials_init_anon(mem_ctx); /* FIXME: Use machine credentials instead ? */
+
+ status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_spoolss,
+ creds, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("unable to call back to %s\n", r->in.str));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ ZERO_STRUCT(rop);
+ rop.in.server_name = lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(rop.in.server_name);
+ rop.in.printer_local = 0;
+ rop.in.type = REG_NONE;
+ rop.in.unknown1 = 0;
+ rop.in.unknown2 = 0;
+ rop.out.handle = &notify_handle;
+
+ status = dcerpc_spoolss_ReplyOpenPrinter(p, mem_ctx, &rop);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("unable to open remote printer %s\n", r->in.str));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ talloc_free(p);
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_RouterRefreshPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_RouterRefreshPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterRefreshPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RemoteFindNextPrinterChangeNotifyEx
+*/
+static WERROR dcesrv_spoolss_RemoteFindNextPrinterChangeNotifyEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RemoteFindNextPrinterChangeNotifyEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_44
+*/
+static WERROR dcesrv_spoolss_44(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_44 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ spoolss_OpenPrinterEx
+*/
+static WERROR dcesrv_spoolss_OpenPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private, struct ntptr_context);
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ const char *server;
+ const char *object;
+ enum ntptr_HandleType type;
+ WERROR status;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ status = dcesrv_spoolss_parse_printer_name(mem_ctx, r->in.printername, &server, &object, &type);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ switch (type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_OpenPrintServer(ntptr, mem_ctx, r, server, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PORT:
+ status = ntptr_OpenPort(ntptr, mem_ctx, r, object, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_MONITOR:
+ status = ntptr_OpenMonitor(ntptr, mem_ctx, r, object, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_OpenPrinter(ntptr, mem_ctx, r, object, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ h = dcesrv_handle_new(dce_call->context, handle->type);
+ W_ERROR_HAVE_NO_MEMORY(h);
+
+ h->data = talloc_steal(h, handle);
+
+ *r->out.handle = h->wire_handle;
+
+ return WERR_OK;
+}
+
+/*
+ spoolss_AddPrinterEx
+*/
+static WERROR dcesrv_spoolss_AddPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_47
+*/
+static WERROR dcesrv_spoolss_47(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_47 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterData
+*/
+static WERROR dcesrv_spoolss_EnumPrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterData
+*/
+static WERROR dcesrv_spoolss_DeletePrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_4a
+*/
+static WERROR dcesrv_spoolss_4a(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_4a *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_4b
+*/
+static WERROR dcesrv_spoolss_4b(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_4b *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_4c
+*/
+static WERROR dcesrv_spoolss_4c(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_4c *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_SetPrinterDataEx
+*/
+static WERROR dcesrv_spoolss_SetPrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterDataEx
+*/
+static WERROR dcesrv_spoolss_GetPrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterDataEx
+*/
+static WERROR dcesrv_spoolss_EnumPrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterKey
+*/
+static WERROR dcesrv_spoolss_EnumPrinterKey(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterKey *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterDataEx
+*/
+static WERROR dcesrv_spoolss_DeletePrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterKey
+*/
+static WERROR dcesrv_spoolss_DeletePrinterKey(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterKey *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_53
+*/
+static WERROR dcesrv_spoolss_53(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_53 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterDriverEx
+*/
+static WERROR dcesrv_spoolss_DeletePrinterDriverEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDriverEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_55
+*/
+static WERROR dcesrv_spoolss_55(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_55 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_56
+*/
+static WERROR dcesrv_spoolss_56(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_56 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_57
+*/
+static WERROR dcesrv_spoolss_57(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_57 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_XcvData
+*/
+static WERROR dcesrv_spoolss_XcvData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_XcvDataPrintServer(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_XcvDataPrinter(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PORT:
+ status = ntptr_XcvDataPort(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_MONITOR:
+ status = ntptr_XcvDataMonitor(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ /* TODO: handle the buffer sizes here! */
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_AddPrinterDriverEx
+*/
+static WERROR dcesrv_spoolss_AddPrinterDriverEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterDriverEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5a
+*/
+static WERROR dcesrv_spoolss_5a(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5a *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5b
+*/
+static WERROR dcesrv_spoolss_5b(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5b *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5c
+*/
+static WERROR dcesrv_spoolss_5c(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5c *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5d
+*/
+static WERROR dcesrv_spoolss_5d(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5d *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5e
+*/
+static WERROR dcesrv_spoolss_5e(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5e *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5f
+*/
+static WERROR dcesrv_spoolss_5f(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5f *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_spoolss_s.c"
diff --git a/source4/rpc_server/srvsvc/dcesrv_srvsvc.c b/source4/rpc_server/srvsvc/dcesrv_srvsvc.c
new file mode 100644
index 0000000000..8dc42bf43b
--- /dev/null
+++ b/source4/rpc_server/srvsvc/dcesrv_srvsvc.c
@@ -0,0 +1,2327 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the srvsvc pipe
+
+ Copyright (C) Stefan (metze) Metzmacher 2004-2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/common/proto.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "system/time.h"
+#include "rpc_server/srvsvc/proto.h"
+#include "param/param.h"
+
+#define SRVSVC_CHECK_ADMIN_ACCESS do { \
+ struct security_token *t = dce_call->conn->auth_state.session_info->security_token; \
+ if (!security_token_has_builtin_administrators(t) && \
+ !security_token_has_sid_string(t, SID_BUILTIN_SERVER_OPERATORS)) { \
+ return WERR_ACCESS_DENIED; \
+ } \
+} while (0)
+
+/*
+ srvsvc_NetCharDevEnum
+*/
+static WERROR dcesrv_srvsvc_NetCharDevEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevEnum *r)
+{
+ r->out.level = r->in.level;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 0:
+ r->out.ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetCharDevCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr0);
+
+ r->out.ctr.ctr0->count = 0;
+ r->out.ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ case 1:
+ r->out.ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetCharDevCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr1);
+
+ r->out.ctr.ctr1->count = 0;
+ r->out.ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetCharDevGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevGetInfo *r)
+{
+ ZERO_STRUCT(r->out);
+
+ switch (r->in.level) {
+ case 0:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevControl
+*/
+static WERROR dcesrv_srvsvc_NetCharDevControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevControl *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetCharDevQEnum
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQEnum *r)
+{
+ r->out.level = r->in.level;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ r->out.ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetCharDevQCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr0);
+
+ r->out.ctr.ctr0->count = 0;
+ r->out.ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetCharDevQCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr1);
+
+ r->out.ctr.ctr1->count = 0;
+ r->out.ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevQGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQGetInfo *r)
+{
+ ZERO_STRUCT(r->out);
+
+ switch (r->in.level) {
+ case 0:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevQSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQSetInfo *r)
+{
+ switch (r->in.level) {
+ case 0:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevQPurge
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQPurge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQPurge *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetCharDevQPurgeSelf
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQPurgeSelf(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQPurgeSelf *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetConnEnum
+*/
+static WERROR dcesrv_srvsvc_NetConnEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetConnEnum *r)
+{
+ r->out.level = r->in.level;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ r->out.ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetConnCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr0);
+
+ r->out.ctr.ctr0->count = 0;
+ r->out.ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetConnCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr1);
+
+ r->out.ctr.ctr1->count = 0;
+ r->out.ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetFileEnum
+*/
+static WERROR dcesrv_srvsvc_NetFileEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileEnum *r)
+{
+ r->out.level = r->in.level;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 2:
+ {
+ r->out.ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetFileCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr2);
+
+ r->out.ctr.ctr2->count = 0;
+ r->out.ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ r->out.ctr.ctr3 = talloc(mem_ctx, struct srvsvc_NetFileCtr3);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr3);
+
+ r->out.ctr.ctr3->count = 0;
+ r->out.ctr.ctr3->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetFileGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetFileGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileGetInfo *r)
+{
+ ZERO_STRUCT(r->out);
+
+ switch (r->in.level) {
+ case 2:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetFileClose
+*/
+static WERROR dcesrv_srvsvc_NetFileClose(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileClose *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetSessEnum
+*/
+static WERROR dcesrv_srvsvc_NetSessEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSessEnum *r)
+{
+ r->out.level = r->in.level;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ r->out.ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetSessCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr0);
+
+ r->out.ctr.ctr0->count = 0;
+ r->out.ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetSessCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr1);
+
+ r->out.ctr.ctr1->count = 0;
+ r->out.ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ r->out.ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetSessCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr2);
+
+ r->out.ctr.ctr2->count = 0;
+ r->out.ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 10:
+ {
+ r->out.ctr.ctr10 = talloc(mem_ctx, struct srvsvc_NetSessCtr10);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr10);
+
+ r->out.ctr.ctr2->count = 0;
+ r->out.ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 502:
+ {
+ r->out.ctr.ctr502 = talloc(mem_ctx, struct srvsvc_NetSessCtr502);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr.ctr502);
+
+ r->out.ctr.ctr2->count = 0;
+ r->out.ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetSessDel
+*/
+static WERROR dcesrv_srvsvc_NetSessDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSessDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareAdd
+*/
+static WERROR dcesrv_srvsvc_NetShareAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareAdd *r)
+{
+ switch (r->in.level) {
+ case 0:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ NTSTATUS nterr;
+ struct share_info *info;
+ struct share_context *sctx;
+ int count = 8;
+ int i;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ /* there are no more than 8 options in struct srvsvc_NetShareInfo2 */
+ info = talloc_array(mem_ctx, struct share_info, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ i = 0;
+
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (r->in.info.info2->type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ if (r->in.info.info2->path && r->in.info.info2->path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (r->in.info.info2->path[1] == ':') {
+ info[i].value = talloc_strdup(info, &r->in.info.info2->path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, r->in.info.info2->path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (r->in.info.info2->comment && r->in.info.info2->comment[0]) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info.info2->comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ if (r->in.info.info2->password && r->in.info.info2->password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info.info502->password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = r->in.info.info2->max_users;
+ i++;
+
+ /* TODO: security descriptor */
+
+ nterr = share_create(sctx, r->in.info.info2->name, info, i);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 502:
+ {
+ NTSTATUS nterr;
+ struct share_info *info;
+ struct share_context *sctx;
+ int count = 10;
+ int i;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ /* there are no more than 10 options in struct srvsvc_NetShareInfo502 */
+ info = talloc_array(mem_ctx, struct share_info, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ i = 0;
+
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (r->in.info.info502->type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ if (r->in.info.info502->path && r->in.info.info502->path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (r->in.info.info2->path[1] == ':') {
+ info[i].value = talloc_strdup(info, &r->in.info.info502->path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, r->in.info.info502->path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (r->in.info.info502->comment && r->in.info.info502->comment[0]) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info.info502->comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ if (r->in.info.info502->password && r->in.info.info502->password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info.info502->password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = r->in.info.info502->max_users;
+ i++;
+
+ /* TODO: security descriptor */
+
+ nterr = share_create(sctx, r->in.info.info502->name, info, i);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+static WERROR dcesrv_srvsvc_fiel_ShareInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct share_config *scfg, uint32_t level,
+ union srvsvc_NetShareInfo *info)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ switch (level) {
+ case 0:
+ {
+ info->info0->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info0->name);
+
+ return WERR_OK;
+ }
+ case 1:
+ {
+ info->info1->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info1->name);
+ info->info1->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info1->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info1->comment);
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ info->info2->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info2->name);
+ info->info2->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info2->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info2->comment);
+ info->info2->permissions = dcesrv_common_get_share_permissions(mem_ctx, dce_ctx, scfg);
+ info->info2->max_users = share_int_option(scfg, SHARE_MAX_CONNECTIONS, SHARE_MAX_CONNECTIONS_DEFAULT);
+ info->info2->current_users = dcesrv_common_get_share_current_users(mem_ctx, dce_ctx, scfg);
+ info->info2->path = dcesrv_common_get_share_path(mem_ctx, dce_ctx, scfg);
+ W_ERROR_HAVE_NO_MEMORY(info->info2->path);
+ info->info2->password = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_PASSWORD, NULL));
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ info->info501->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info501->name);
+ info->info501->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info501->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info501->comment);
+ info->info501->csc_policy = share_int_option(scfg, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT);
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ info->info502->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info502->name);
+ info->info502->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info502->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info502->comment);
+ info->info502->permissions = dcesrv_common_get_share_permissions(mem_ctx, dce_ctx, scfg);
+ info->info502->max_users = share_int_option(scfg, SHARE_MAX_CONNECTIONS, SHARE_MAX_CONNECTIONS_DEFAULT);
+ info->info502->current_users = dcesrv_common_get_share_current_users(mem_ctx, dce_ctx, scfg);
+ info->info502->path = dcesrv_common_get_share_path(mem_ctx, dce_ctx, scfg);
+ W_ERROR_HAVE_NO_MEMORY(info->info502->path);
+ info->info502->password = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_PASSWORD, NULL));
+ info->info502->unknown = dcesrv_common_get_share_unknown(mem_ctx, dce_ctx, scfg);
+ info->info502->sd = dcesrv_common_get_security_descriptor(mem_ctx, dce_ctx, scfg);
+
+ return WERR_OK;
+ }
+ case 1005:
+ {
+ info->info1005->dfs_flags = dcesrv_common_get_share_dfs_flags(mem_ctx, dce_ctx, scfg);
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/*
+ srvsvc_NetShareEnumAll
+*/
+static WERROR dcesrv_srvsvc_NetShareEnumAll(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareEnumAll *r)
+{
+ NTSTATUS nterr;
+ int numshares = 0;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+
+ r->out.level = r->in.level;
+ ZERO_STRUCT(r->out.ctr);
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ /* TODO: - paging of results
+ */
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &numshares, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ int i;
+ struct srvsvc_NetShareCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr0);
+
+ ctr0->count = numshares;
+ ctr0->array = NULL;
+
+ if (ctr0->count == 0) {
+ r->out.ctr.ctr0 = ctr0;
+ return WERR_OK;
+ }
+
+ ctr0->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo0, ctr0->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ for (i = 0; i < ctr0->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info0 = &ctr0->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr0 = ctr0;
+ r->out.totalentries = r->out.ctr.ctr0->count;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ int i;
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ ctr1->count = numshares;
+ ctr1->array = NULL;
+
+ if (ctr1->count == 0) {
+ r->out.ctr.ctr1 = ctr1;
+ return WERR_OK;
+ }
+
+ ctr1->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo1, ctr1->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (i=0; i < ctr1->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info1 = &ctr1->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr1 = ctr1;
+ r->out.totalentries = r->out.ctr.ctr1->count;
+ return WERR_OK;
+ }
+ case 2:
+ {
+ int i;
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr2 = talloc(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ ctr2->count = numshares;
+ ctr2->array = NULL;
+
+ if (ctr2->count == 0) {
+ r->out.ctr.ctr2 = ctr2;
+ return WERR_OK;
+ }
+
+ ctr2->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo2, ctr2->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr2->array);
+
+ for (i=0; i < ctr2->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info2 = &ctr2->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr2 = ctr2;
+ r->out.totalentries = r->out.ctr.ctr2->count;
+ return WERR_OK;
+ }
+ case 501:
+ {
+ int i;
+ struct srvsvc_NetShareCtr501 *ctr501;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr501 = talloc(mem_ctx, struct srvsvc_NetShareCtr501);
+ W_ERROR_HAVE_NO_MEMORY(ctr501);
+
+ ctr501->count = numshares;
+ ctr501->array = NULL;
+
+ if (ctr501->count == 0) {
+ r->out.ctr.ctr501 = ctr501;
+ return WERR_OK;
+ }
+
+ ctr501->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo501, ctr501->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr501->array);
+
+ for (i=0; i < ctr501->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info501 = &ctr501->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr501 = ctr501;
+ r->out.totalentries = r->out.ctr.ctr501->count;
+ return WERR_OK;
+ }
+ case 502:
+ {
+ int i;
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr502 = talloc(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ ctr502->count = numshares;
+ ctr502->array = NULL;
+
+ if (ctr502->count == 0) {
+ r->out.ctr.ctr502 = ctr502;
+ return WERR_OK;
+ }
+
+ ctr502->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo502, ctr502->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr502->array);
+
+ for (i=0; i < ctr502->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info502 = &ctr502->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr502 = ctr502;
+ r->out.totalentries = r->out.ctr.ctr502->count;
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetShareGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetShareGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareGetInfo *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx = NULL;
+ struct share_config *scfg = NULL;
+
+ ZERO_STRUCT(r->out);
+
+ /* TODO: - access check
+ */
+
+ if (strcmp("", r->in.share_name) == 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_get_config(mem_ctx, sctx, r->in.share_name, &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info0 = talloc(mem_ctx, struct srvsvc_NetShareInfo0);
+ W_ERROR_HAVE_NO_MEMORY(info.info0);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info.info0 = info.info0;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info1 = talloc(mem_ctx, struct srvsvc_NetShareInfo1);
+ W_ERROR_HAVE_NO_MEMORY(info.info1);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info.info1 = info.info1;
+ return WERR_OK;
+ }
+ case 2:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ info.info2 = talloc(mem_ctx, struct srvsvc_NetShareInfo2);
+ W_ERROR_HAVE_NO_MEMORY(info.info2);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info.info2 = info.info2;
+ return WERR_OK;
+ }
+ case 501:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info501 = talloc(mem_ctx, struct srvsvc_NetShareInfo501);
+ W_ERROR_HAVE_NO_MEMORY(info.info501);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info.info501 = info.info501;
+ return WERR_OK;
+ }
+ case 502:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ info.info502 = talloc(mem_ctx, struct srvsvc_NetShareInfo502);
+ W_ERROR_HAVE_NO_MEMORY(info.info502);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info.info502 = info.info502;
+ return WERR_OK;
+ }
+ case 1005:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info1005 = talloc(mem_ctx, struct srvsvc_NetShareInfo1005);
+ W_ERROR_HAVE_NO_MEMORY(info.info1005);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info.info1005 = info.info1005;
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+static WERROR dcesrv_srvsvc_fill_share_info(struct share_info *info, int *count,
+ const char *share_name, int level,
+ const char *name,
+ const char *path,
+ const char *comment,
+ const char *password,
+ enum srvsvc_ShareType type,
+ int32_t max_users,
+ uint32_t csc_policy,
+ struct security_descriptor *sd)
+{
+ int i = 0;
+
+ if (level == 501) {
+ info[i].name = SHARE_CSC_POLICY;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = csc_policy;
+ i++;
+ }
+
+ switch(level) {
+
+ case 502:
+ /* TODO: check if unknown is csc_policy */
+
+ /* TODO: security descriptor */
+
+ case 2:
+ if (path && path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (path[1] == ':') {
+ info[i].value = talloc_strdup(info, &path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (password && password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = max_users;
+ i++;
+
+ case 501:
+ case 1:
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ case 1004:
+ if (comment) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+ case 0:
+ if (name &&
+ strcasecmp(share_name, name) != 0) {
+ info[i].name = SHARE_NAME;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, name);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+ }
+
+ break;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *count = i;
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetShareSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetShareSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareSetInfo *r)
+{
+ NTSTATUS nterr;
+ WERROR status;
+ struct share_context *sctx = NULL;
+ struct share_info *info;
+ int count;
+
+ /* TODO: - access check
+ */
+
+ /* there are no more than 10 options in all struct srvsvc_NetShareInfoXXX */
+ info = talloc_array(mem_ctx, struct share_info, 10);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ ZERO_STRUCT(r->out);
+
+ if (strcmp("", r->in.share_name) == 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info.info0->name,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info.info1->name,
+ NULL,
+ r->in.info.info1->comment,
+ NULL,
+ r->in.info.info1->type,
+ 0,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 2:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info.info2->name,
+ r->in.info.info2->path,
+ r->in.info.info2->comment,
+ r->in.info.info2->password,
+ r->in.info.info2->type,
+ r->in.info.info2->max_users,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 501:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info.info501->name,
+ NULL,
+ r->in.info.info501->comment,
+ NULL,
+ r->in.info.info501->type,
+ 0,
+ r->in.info.info501->csc_policy,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 502:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info.info502->name,
+ r->in.info.info502->path,
+ r->in.info.info502->comment,
+ r->in.info.info502->password,
+ r->in.info.info502->type,
+ r->in.info.info502->max_users,
+ 0,
+ r->in.info.info502->sd);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1004:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ NULL,
+ NULL,
+ r->in.info.info1004->comment,
+ NULL,
+ 0,
+ 0,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1005:
+ {
+ /* r->in.info.dfs_flags; */
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ nterr = share_set(sctx, r->in.share_name, info, count);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetShareDelSticky
+*/
+static WERROR dcesrv_srvsvc_NetShareDelSticky(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelSticky *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareCheck
+*/
+static WERROR dcesrv_srvsvc_NetShareCheck(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareCheck *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx = NULL;
+ struct share_config *scfg = NULL;
+ char *device;
+ const char **names;
+ int count, i;
+
+ ZERO_STRUCT(r->out);
+
+ /* TODO: - access check
+ */
+
+ if (strcmp("", r->in.device_name) == 0) {
+ r->out.type = STYPE_IPC;
+ return WERR_OK;
+ }
+
+ /* copy the path skipping C:\ */
+ if (strncasecmp(r->in.device_name, "C:", 2) == 0) {
+ device = talloc_strdup(mem_ctx, &r->in.device_name[2]);
+ } else {
+ /* no chance we have a share that doesn't start with C:\ */
+ return WERR_DEVICE_NOT_SHARED;
+ }
+ all_string_sub(device, "\\", "/", 0);
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &count, &names);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ for (i = 0; i < count; i++) {
+ const char *path;
+ const char *type;
+
+ nterr = share_get_config(mem_ctx, sctx, names[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+ path = share_string_option(scfg, SHARE_PATH, NULL);
+ if (!path) continue;
+
+ if (strcmp(device, path) == 0) {
+ type = share_string_option(scfg, SHARE_TYPE, NULL);
+ if (!type) continue;
+
+ if (strcmp(type, "DISK") == 0) {
+ r->out.type = STYPE_DISKTREE;
+ return WERR_OK;
+ }
+
+ if (strcmp(type, "IPC") == 0) {
+ r->out.type = STYPE_IPC;
+ return WERR_OK;
+ }
+
+ if (strcmp(type, "PRINTER") == 0) {
+ r->out.type = STYPE_PRINTQ;
+ return WERR_OK;
+ }
+ }
+ }
+
+ return WERR_DEVICE_NOT_SHARED;
+}
+
+
+/*
+ srvsvc_NetSrvGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetSrvGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSrvGetInfo *r)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ ZERO_STRUCT(r->out);
+
+ switch (r->in.level) {
+ case 100:
+ {
+ struct srvsvc_NetSrvInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct srvsvc_NetSrvInfo100);
+ W_ERROR_HAVE_NO_MEMORY(info100);
+
+ info100->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info100->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info100->server_name);
+
+ r->out.info.info100 = info100;
+ return WERR_OK;
+ }
+ case 101:
+ {
+ struct srvsvc_NetSrvInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct srvsvc_NetSrvInfo101);
+ W_ERROR_HAVE_NO_MEMORY(info101);
+
+ info101->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info101->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info101->server_name);
+
+ info101->version_major = dcesrv_common_get_version_major(mem_ctx, dce_ctx->lp_ctx);
+ info101->version_minor = dcesrv_common_get_version_minor(mem_ctx, dce_ctx->lp_ctx);
+ info101->server_type = dcesrv_common_get_server_type(mem_ctx, dce_call->event_ctx, dce_ctx);
+ info101->comment = talloc_strdup(mem_ctx, lp_serverstring(dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(info101->comment);
+
+ r->out.info.info101 = info101;
+ return WERR_OK;
+ }
+ case 102:
+ {
+ struct srvsvc_NetSrvInfo102 *info102;
+
+ info102 = talloc(mem_ctx, struct srvsvc_NetSrvInfo102);
+ W_ERROR_HAVE_NO_MEMORY(info102);
+
+ info102->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info102->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info102->server_name);
+
+ info102->version_major = dcesrv_common_get_version_major(mem_ctx, dce_ctx->lp_ctx);
+ info102->version_minor = dcesrv_common_get_version_minor(mem_ctx, dce_ctx->lp_ctx);
+ info102->server_type = dcesrv_common_get_server_type(mem_ctx, dce_call->event_ctx, dce_ctx);
+ info102->comment = talloc_strdup(mem_ctx, lp_serverstring(dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(info102->comment);
+
+ info102->users = dcesrv_common_get_users(mem_ctx, dce_ctx);
+ info102->disc = dcesrv_common_get_disc(mem_ctx, dce_ctx);
+ info102->hidden = dcesrv_common_get_hidden(mem_ctx, dce_ctx);
+ info102->announce = dcesrv_common_get_announce(mem_ctx, dce_ctx);
+ info102->anndelta = dcesrv_common_get_anndelta(mem_ctx, dce_ctx);
+ info102->licenses = dcesrv_common_get_licenses(mem_ctx, dce_ctx);
+ info102->userpath = dcesrv_common_get_userpath(mem_ctx, dce_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info102->userpath);
+
+ r->out.info.info102 = info102;
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetSrvSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetSrvSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSrvSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetDiskEnum
+*/
+static WERROR dcesrv_srvsvc_NetDiskEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetDiskEnum *r)
+{
+ r->out.info.disks = NULL;
+ r->out.info.count = 0;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ /* we can safely hardcode the reply and report we have only one disk (C:) */
+ /* for some reason Windows wants 2 entries with the second being empty */
+ r->out.info.disks = talloc_array(mem_ctx, struct srvsvc_NetDiskInfo0, 2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info.disks);
+ r->out.info.count = 2;
+
+ r->out.info.disks[0].disk = talloc_strdup(mem_ctx, "C:");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info.disks[0].disk);
+
+ r->out.info.disks[1].disk = talloc_strdup(mem_ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info.disks[1].disk);
+
+ r->out.totalentries = 1;
+ r->out.resume_handle = r->in.resume_handle;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetServerStatisticsGet
+*/
+static WERROR dcesrv_srvsvc_NetServerStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerStatisticsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetTransportAdd
+*/
+static WERROR dcesrv_srvsvc_NetTransportAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetTransportEnum
+*/
+static WERROR dcesrv_srvsvc_NetTransportEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportEnum *r)
+{
+ r->out.level = r->in.level;
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ r->out.transports.ctr0 = talloc(mem_ctx, struct srvsvc_NetTransportCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports.ctr0);
+
+ r->out.transports.ctr0->count = 0;
+ r->out.transports.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.transports.ctr1 = talloc(mem_ctx, struct srvsvc_NetTransportCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports.ctr1);
+
+ r->out.transports.ctr1->count = 0;
+ r->out.transports.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ r->out.transports.ctr2 = talloc(mem_ctx, struct srvsvc_NetTransportCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports.ctr2);
+
+ r->out.transports.ctr2->count = 0;
+ r->out.transports.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ r->out.transports.ctr3 = talloc(mem_ctx, struct srvsvc_NetTransportCtr3);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports.ctr3);
+
+ r->out.transports.ctr3->count = 0;
+ r->out.transports.ctr3->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/*
+ srvsvc_NetTransportDel
+*/
+static WERROR dcesrv_srvsvc_NetTransportDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetRemoteTOD
+*/
+static WERROR dcesrv_srvsvc_NetRemoteTOD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetRemoteTOD *r)
+{
+ struct timeval tval;
+ time_t t;
+ struct tm tm;
+
+ r->out.info = talloc(mem_ctx, struct srvsvc_NetRemoteTODInfo);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info);
+
+ GetTimeOfDay(&tval);
+ t = tval.tv_sec;
+
+ gmtime_r(&t, &tm);
+
+ r->out.info->elapsed = t;
+ /* TODO: fake the uptime: just return the milliseconds till 0:00:00 today */
+ r->out.info->msecs = (tm.tm_hour*60*60*1000)
+ + (tm.tm_min*60*1000)
+ + (tm.tm_sec*1000)
+ + (tval.tv_usec/1000);
+ r->out.info->hours = tm.tm_hour;
+ r->out.info->mins = tm.tm_min;
+ r->out.info->secs = tm.tm_sec;
+ r->out.info->hunds = tval.tv_usec/10000;
+ r->out.info->timezone = get_time_zone(t)/60;
+ r->out.info->tinterval = 310; /* just return the same as windows */
+ r->out.info->day = tm.tm_mday;
+ r->out.info->month = tm.tm_mon + 1;
+ r->out.info->year = tm.tm_year + 1900;
+ r->out.info->weekday = tm.tm_wday;
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetPathType
+*/
+static WERROR dcesrv_srvsvc_NetPathType(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathType *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetPathCanonicalize
+*/
+static WERROR dcesrv_srvsvc_NetPathCanonicalize(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathCanonicalize *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetPathCompare
+*/
+static WERROR dcesrv_srvsvc_NetPathCompare(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathCompare *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetNameValidate
+*/
+static WERROR dcesrv_srvsvc_NetNameValidate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetNameValidate *r)
+{
+ int len;
+
+ if ((r->in.flags != 0x0) && (r->in.flags != 0x80000000)) {
+ return WERR_INVALID_NAME;
+ }
+
+ switch (r->in.name_type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return WERR_NOT_SUPPORTED;
+
+ case 9: /* validate share name */
+
+ len = strlen_m(r->in.name);
+ if ((r->in.flags == 0x0) && (len > 81)) {
+ return WERR_INVALID_NAME;
+ }
+ if ((r->in.flags == 0x80000000) && (len > 13)) {
+ return WERR_INVALID_NAME;
+ }
+ if (! dcesrv_common_validate_share_name(mem_ctx, r->in.name)) {
+ return WERR_INVALID_NAME;
+ }
+ return WERR_OK;
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ return WERR_NOT_SUPPORTED;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+
+/*
+ srvsvc_NetPRNameCompare
+*/
+static WERROR dcesrv_srvsvc_NetPRNameCompare(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPRNameCompare *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareEnum
+*/
+static WERROR dcesrv_srvsvc_NetShareEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareEnum *r)
+{
+ NTSTATUS nterr;
+ int numshares = 0;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ r->out.level = r->in.level;
+ ZERO_STRUCT(r->out.ctr);
+ r->out.totalentries = 0;
+ r->out.resume_handle = NULL;
+
+ /* TODO: - paging of results
+ */
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &numshares, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr0);
+
+ count = numshares;
+ ctr0->count = count;
+ ctr0->array = NULL;
+
+ if (ctr0->count == 0) {
+ r->out.ctr.ctr0 = ctr0;
+ return WERR_OK;
+ }
+
+ ctr0->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo0, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr0->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info0 = &ctr0->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr0 = ctr0;
+ r->out.totalentries = r->out.ctr.ctr0->count;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ count = numshares;
+ ctr1->count = count;
+ ctr1->array = NULL;
+
+ if (ctr1->count == 0) {
+ r->out.ctr.ctr1 = ctr1;
+ return WERR_OK;
+ }
+
+ ctr1->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo1, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr1->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info1 = &ctr1->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr1 = ctr1;
+ r->out.totalentries = r->out.ctr.ctr1->count;
+ return WERR_OK;
+ }
+ case 2:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr2 = talloc(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ count = numshares;
+ ctr2->count = count;
+ ctr2->array = NULL;
+
+ if (ctr2->count == 0) {
+ r->out.ctr.ctr2 = ctr2;
+ return WERR_OK;
+ }
+
+ ctr2->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo2, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr2->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr2->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info2 = &ctr2->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr2 = ctr2;
+ r->out.totalentries = r->out.ctr.ctr2->count;
+ return WERR_OK;
+ }
+ case 502:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr502 = talloc(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ count = numshares;
+ ctr502->count = count;
+ ctr502->array = NULL;
+
+ if (ctr502->count == 0) {
+ r->out.ctr.ctr502 = ctr502;
+ return WERR_OK;
+ }
+
+ ctr502->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo502, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr502->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr502->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info502 = &ctr502->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.ctr.ctr502 = ctr502;
+ r->out.totalentries = r->out.ctr.ctr502->count;
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetShareDelStart
+*/
+static WERROR dcesrv_srvsvc_NetShareDelStart(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelStart *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareDelCommit
+*/
+static WERROR dcesrv_srvsvc_NetShareDelCommit(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelCommit *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetGetFileSecurity
+*/
+static WERROR dcesrv_srvsvc_NetGetFileSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetGetFileSecurity *r)
+{
+ struct sec_desc_buf *sd_buf;
+ struct ntvfs_context *ntvfs_ctx = NULL;
+ struct ntvfs_request *ntvfs_req;
+ union smb_fileinfo *io;
+ NTSTATUS nt_status;
+
+ nt_status = srvsvc_create_ntvfs_context(dce_call, mem_ctx, r->in.share, &ntvfs_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ ntvfs_req = ntvfs_request_create(ntvfs_ctx, mem_ctx,
+ dce_call->conn->auth_state.session_info,
+ 0,
+ dce_call->time,
+ NULL, NULL, 0);
+ W_ERROR_HAVE_NO_MEMORY(ntvfs_req);
+
+ sd_buf = talloc(mem_ctx, struct sec_desc_buf);
+ W_ERROR_HAVE_NO_MEMORY(sd_buf);
+
+ io = talloc(mem_ctx, union smb_fileinfo);
+ W_ERROR_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.path = r->in.file;
+ io->query_secdesc.in.secinfo_flags = r->in.securityinformation;
+
+ nt_status = ntvfs_qpathinfo(ntvfs_req, io);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ sd_buf->sd = io->query_secdesc.out.sd;
+
+ r->out.sd_buf = sd_buf;
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetSetFileSecurity
+*/
+static WERROR dcesrv_srvsvc_NetSetFileSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSetFileSecurity *r)
+{
+ struct ntvfs_context *ntvfs_ctx;
+ struct ntvfs_request *ntvfs_req;
+ union smb_setfileinfo *io;
+ NTSTATUS nt_status;
+
+ nt_status = srvsvc_create_ntvfs_context(dce_call, mem_ctx, r->in.share, &ntvfs_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ ntvfs_req = ntvfs_request_create(ntvfs_ctx, mem_ctx,
+ dce_call->conn->auth_state.session_info,
+ 0,
+ dce_call->time,
+ NULL, NULL, 0);
+ W_ERROR_HAVE_NO_MEMORY(ntvfs_req);
+
+ io = talloc(mem_ctx, union smb_setfileinfo);
+ W_ERROR_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.path = r->in.file;
+ io->set_secdesc.in.secinfo_flags = r->in.securityinformation;
+ io->set_secdesc.in.sd = r->in.sd_buf.sd;
+
+ nt_status = ntvfs_setpathinfo(ntvfs_req, io);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetServerTransportAddEx
+*/
+static WERROR dcesrv_srvsvc_NetServerTransportAddEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerTransportAddEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetServerSetServiceBitsEx
+*/
+static WERROR dcesrv_srvsvc_NetServerSetServiceBitsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerSetServiceBitsEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSGETVERSION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSGETVERSION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSGETVERSION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSCREATELOCALPARTITION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSCREATELOCALPARTITION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSCREATELOCALPARTITION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSDELETELOCALPARTITION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSDELETELOCALPARTITION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSDELETELOCALPARTITION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSSETLOCALVOLUMESTATE
+*/
+static WERROR dcesrv_srvsvc_NETRDFSSETLOCALVOLUMESTATE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSSETLOCALVOLUMESTATE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSSETSERVERINFO
+*/
+static WERROR dcesrv_srvsvc_NETRDFSSETSERVERINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSSETSERVERINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSCREATEEXITPOINT
+*/
+static WERROR dcesrv_srvsvc_NETRDFSCREATEEXITPOINT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSCREATEEXITPOINT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSDELETEEXITPOINT
+*/
+static WERROR dcesrv_srvsvc_NETRDFSDELETEEXITPOINT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSDELETEEXITPOINT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSMODIFYPREFIX
+*/
+static WERROR dcesrv_srvsvc_NETRDFSMODIFYPREFIX(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSMODIFYPREFIX *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSFIXLOCALVOLUME
+*/
+static WERROR dcesrv_srvsvc_NETRDFSFIXLOCALVOLUME(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSFIXLOCALVOLUME *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSMANAGERREPORTSITEINFO
+*/
+static WERROR dcesrv_srvsvc_NETRDFSMANAGERREPORTSITEINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSMANAGERREPORTSITEINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRSERVERTRANSPORTDELEX
+*/
+static WERROR dcesrv_srvsvc_NETRSERVERTRANSPORTDELEX(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRSERVERTRANSPORTDELEX *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ srvsvc_NetShareDel
+*/
+static WERROR dcesrv_srvsvc_NetShareDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDel *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_remove(sctx, r->in.share_name);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetSetServiceBits
+*/
+static WERROR dcesrv_srvsvc_NetSetServiceBits(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSetServiceBits *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ srvsvc_NETRPRNAMECANONICALIZE
+*/
+static WERROR dcesrv_srvsvc_NETRPRNAMECANONICALIZE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRPRNAMECANONICALIZE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_srvsvc_s.c"
diff --git a/source4/rpc_server/srvsvc/srvsvc_ntvfs.c b/source4/rpc_server/srvsvc/srvsvc_ntvfs.c
new file mode 100644
index 0000000000..f1cb35bdd8
--- /dev/null
+++ b/source4/rpc_server/srvsvc/srvsvc_ntvfs.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ srvsvc pipe ntvfs helper functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/srvsvc/proto.h"
+#include "lib/socket/socket.h"
+#include "param/param.h"
+
+struct socket_address *srvsvc_get_my_addr(void *p, TALLOC_CTX *mem_ctx)
+{
+ struct dcesrv_connection *conn = talloc_get_type(p, struct dcesrv_connection);
+ return dcesrv_connection_get_my_addr(conn, mem_ctx);
+}
+
+struct socket_address *srvsvc_get_peer_addr(void *p, TALLOC_CTX *mem_ctx)
+{
+ struct dcesrv_connection *conn = talloc_get_type(p, struct dcesrv_connection);
+ return dcesrv_connection_get_peer_addr(conn, mem_ctx);
+}
+
+struct srvsvc_ntvfs_ctx {
+ struct ntvfs_context *ntvfs;
+};
+
+static int srvsvc_ntvfs_ctx_destructor(struct srvsvc_ntvfs_ctx *c)
+{
+ ntvfs_disconnect(c->ntvfs);
+ return 0;
+}
+
+NTSTATUS srvsvc_create_ntvfs_context(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ const char *share,
+ struct ntvfs_context **_ntvfs)
+{
+ NTSTATUS status;
+ struct srvsvc_ntvfs_ctx *c;
+ struct ntvfs_request *ntvfs_req;
+ enum ntvfs_type type;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ const char *sharetype;
+
+ status = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = share_get_config(mem_ctx, sctx, share, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: couldn't find service %s\n", share));
+ return status;
+ }
+
+#if 0 /* TODO: fix access cecking */
+ if (!socket_check_access(dce_call->connection->socket,
+ scfg->name,
+ share_string_list_option(scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+#endif
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ } else if (sharetype && strcmp(sharetype, "PRINTER")) {
+ type = NTVFS_PRINT;
+ } else {
+ type = NTVFS_DISK;
+ }
+
+ c = talloc(mem_ctx, struct srvsvc_ntvfs_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(c);
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(c, scfg, type,
+ PROTOCOL_NT1,
+ 0,/* ntvfs_client_caps */
+ dce_call->event_ctx,
+ dce_call->conn->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->server_id,
+ &c->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("srvsvc_create_ntvfs_context: ntvfs_init_connection failed for service %s\n",
+ scfg->name));
+ return status;
+ }
+ talloc_set_destructor(c, srvsvc_ntvfs_ctx_destructor);
+
+ /*
+ * NOTE: we only set the addr callbacks as we're not interesseted in oplocks or in getting file handles
+ */
+ status = ntvfs_set_addr_callbacks(c->ntvfs, srvsvc_get_my_addr, srvsvc_get_peer_addr, dce_call->conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: NTVFS failed to set the addr callbacks!\n"));
+ return status;
+ }
+
+ ntvfs_req = ntvfs_request_create(c->ntvfs, mem_ctx,
+ dce_call->conn->auth_state.session_info,
+ 0, /* TODO: fill in PID */
+ dce_call->time,
+ NULL, NULL, 0);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs_req);
+
+ /* Invoke NTVFS connection hook */
+ status = ntvfs_connect(ntvfs_req, scfg->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: NTVFS ntvfs_connect() failed!\n"));
+ return status;
+ }
+
+ *_ntvfs = c->ntvfs;
+ return NT_STATUS_OK;
+}
diff --git a/source4/rpc_server/unixinfo/dcesrv_unixinfo.c b/source4/rpc_server/unixinfo/dcesrv_unixinfo.c
new file mode 100644
index 0000000000..e6313b771c
--- /dev/null
+++ b/source4/rpc_server/unixinfo/dcesrv_unixinfo.c
@@ -0,0 +1,242 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the unixinfo pipe
+
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "librpc/gen_ndr/ndr_unixinfo.h"
+#include "libcli/wbclient/wbclient.h"
+#include "lib/events/events.h"
+#include "system/passwd.h"
+#include "param/param.h"
+
+static NTSTATUS dcerpc_unixinfo_bind(struct dcesrv_call_state *dce_call,
+ const struct dcesrv_interface *iface)
+{
+ struct wbc_context *wbc_ctx;
+
+ wbc_ctx = wbc_init(dce_call->context, dce_call->msg_ctx,
+ dce_call->event_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(wbc_ctx);
+
+ dce_call->context->private = wbc_ctx;
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_UNIXINFO_BIND dcerpc_unixinfo_bind
+
+static NTSTATUS dcesrv_unixinfo_SidToUid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_SidToUid *r)
+{
+ NTSTATUS status;
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ DEBUG(5, ("dcesrv_unixinfo_SidToUid called\n"));
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = &r->in.sid;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = NULL;
+ ctx = wbc_sids_to_xids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_UID) {
+ *r->out.uid = ids->unixid->id;
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+}
+
+static NTSTATUS dcesrv_unixinfo_UidToSid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_UidToSid *r)
+{
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+ uint32_t uid;
+ NTSTATUS status;
+
+ DEBUG(5, ("dcesrv_unixinfo_UidToSid called\n"));
+
+ uid = r->in.uid; /* This cuts uid to 32 bit */
+ if ((uint64_t)uid != r->in.uid) {
+ DEBUG(10, ("uid out of range\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = NULL;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids->unixid);
+
+ ids->unixid->id = uid;
+ ids->unixid->type = ID_TYPE_UID;
+
+ ctx = wbc_xids_to_sids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.sid = ids->sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_unixinfo_SidToGid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_SidToGid *r)
+{
+ NTSTATUS status;
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ DEBUG(5, ("dcesrv_unixinfo_SidToGid called\n"));
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = &r->in.sid;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = NULL;
+ ctx = wbc_sids_to_xids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_GID) {
+ *r->out.gid = ids->unixid->id;
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+}
+
+static NTSTATUS dcesrv_unixinfo_GidToSid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_GidToSid *r)
+{
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+ uint32_t gid;
+ NTSTATUS status;
+
+ DEBUG(5, ("dcesrv_unixinfo_GidToSid called\n"));
+
+ gid = r->in.gid; /* This cuts gid to 32 bit */
+ if ((uint64_t)gid != r->in.gid) {
+ DEBUG(10, ("gid out of range\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = NULL;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids->unixid);
+
+ ids->unixid->id = gid;
+ ids->unixid->type = ID_TYPE_GID;
+
+ ctx = wbc_xids_to_sids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.sid = ids->sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_unixinfo_GetPWUid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_GetPWUid *r)
+{
+ int i;
+
+ *r->out.count = 0;
+
+ r->out.infos = talloc_zero_array(mem_ctx, struct unixinfo_GetPWUidInfo,
+ *r->in.count);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.infos);
+ *r->out.count = *r->in.count;
+
+ for (i=0; i < *r->in.count; i++) {
+ uid_t uid;
+ struct passwd *pwd;
+
+ uid = r->in.uids[i];
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ DEBUG(10, ("uid %d not found\n", uid));
+ r->out.infos[i].homedir = "";
+ r->out.infos[i].shell = "";
+ r->out.infos[i].status = NT_STATUS_NO_SUCH_USER;
+ continue;
+ }
+
+ r->out.infos[i].homedir = talloc_strdup(mem_ctx, pwd->pw_dir);
+ r->out.infos[i].shell = talloc_strdup(mem_ctx, pwd->pw_shell);
+
+ if ((r->out.infos[i].homedir == NULL) ||
+ (r->out.infos[i].shell == NULL)) {
+ r->out.infos[i].homedir = "";
+ r->out.infos[i].shell = "";
+ r->out.infos[i].status = NT_STATUS_NO_MEMORY;
+ continue;
+ }
+
+ r->out.infos[i].status = NT_STATUS_OK;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_unixinfo_s.c"
diff --git a/source4/rpc_server/winreg/README b/source4/rpc_server/winreg/README
new file mode 100644
index 0000000000..03a81e776b
--- /dev/null
+++ b/source4/rpc_server/winreg/README
@@ -0,0 +1,3 @@
+This is the RPC server for the registry for Samba. It is basically a
+front-end for the registry library in lib/registry. See
+lib/registry/README for more details.
diff --git a/source4/rpc_server/winreg/rpc_winreg.c b/source4/rpc_server/winreg/rpc_winreg.c
new file mode 100644
index 0000000000..22c60c354c
--- /dev/null
+++ b/source4/rpc_server/winreg/rpc_winreg.c
@@ -0,0 +1,665 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the winreg pipe
+
+ Copyright (C) Jelmer Vernooij 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "lib/registry/registry.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "rpc_server/common/common.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+
+enum handle_types { HTYPE_REGVAL, HTYPE_REGKEY };
+
+static NTSTATUS dcerpc_winreg_bind(struct dcesrv_call_state *dce_call,
+ const struct dcesrv_interface *iface)
+{
+ struct registry_context *ctx;
+ WERROR err;
+
+ err = reg_open_samba(dce_call->context,
+ &ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info,
+ NULL);
+
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("Error opening registry: %s\n", win_errstr(err)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dce_call->context->private = ctx;
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_WINREG_BIND dcerpc_winreg_bind
+
+static WERROR dcesrv_winreg_openhive(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx, uint32_t hkey,
+ struct policy_handle **outh)
+{
+ struct registry_context *ctx = dce_call->context->private;
+ struct dcesrv_handle *h;
+ WERROR error;
+
+ h = dcesrv_handle_new(dce_call->context, HTYPE_REGKEY);
+
+ error = reg_get_predefined_key(ctx, hkey,
+ (struct registry_key **)&h->data);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ *outh = &h->wire_handle;
+
+ return error;
+}
+
+#define func_winreg_OpenHive(k,n) static WERROR dcesrv_winreg_Open ## k (struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct winreg_Open ## k *r) \
+{ \
+ return dcesrv_winreg_openhive (dce_call, mem_ctx, n, &r->out.handle);\
+}
+
+func_winreg_OpenHive(HKCR,HKEY_CLASSES_ROOT)
+func_winreg_OpenHive(HKCU,HKEY_CURRENT_USER)
+func_winreg_OpenHive(HKLM,HKEY_LOCAL_MACHINE)
+func_winreg_OpenHive(HKPD,HKEY_PERFORMANCE_DATA)
+func_winreg_OpenHive(HKU,HKEY_USERS)
+func_winreg_OpenHive(HKCC,HKEY_CURRENT_CONFIG)
+func_winreg_OpenHive(HKDD,HKEY_DYN_DATA)
+func_winreg_OpenHive(HKPT,HKEY_PERFORMANCE_TEXT)
+func_winreg_OpenHive(HKPN,HKEY_PERFORMANCE_NLSTEXT)
+
+/*
+ winreg_CloseKey
+*/
+static WERROR dcesrv_winreg_CloseKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_CloseKey *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*
+ winreg_CreateKey
+*/
+static WERROR dcesrv_winreg_CreateKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_CreateKey *r)
+{
+ struct dcesrv_handle *h, *newh;
+ WERROR error;
+ struct security_descriptor sd;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ newh = dcesrv_handle_new(dce_call->context, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ /* the security descriptor is optional */
+ if (r->in.secdesc != NULL) {
+ DATA_BLOB sdblob;
+ enum ndr_err_code ndr_err;
+ sdblob.data = r->in.secdesc->sd.data;
+ sdblob.length = r->in.secdesc->sd.len;
+ if (sdblob.data == NULL) {
+ return WERR_INVALID_PARAM;
+ }
+ ndr_err = ndr_pull_struct_blob_all(&sdblob, mem_ctx, NULL, &sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAM;
+ }
+ }
+
+ error = reg_key_add_name(newh, (struct registry_key *)h->data,
+ r->in.name.name, NULL, r->in.secdesc?&sd:NULL,
+ (struct registry_key **)&newh->data);
+ if (W_ERROR_IS_OK(error)) {
+ r->out.new_handle = &newh->wire_handle;
+ } else {
+ talloc_free(newh);
+ }
+
+ return error;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_DeleteKey
+*/
+static WERROR dcesrv_winreg_DeleteKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteKey *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_key_del((struct registry_key *)h->data, r->in.key.name);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_DeleteValue
+*/
+static WERROR dcesrv_winreg_DeleteValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ key = h->data;
+
+ return reg_del_value(key, r->in.value.name);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_EnumKey
+*/
+static WERROR dcesrv_winreg_EnumKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_EnumKey *r)
+{
+ struct dcesrv_handle *h;
+ const char *name;
+ NTTIME last_mod;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ r->out.result = reg_key_get_subkey_by_index(mem_ctx,
+ (struct registry_key *)h->data,
+ r->in.enum_index,
+ &name, NULL, &last_mod);
+
+ if (W_ERROR_IS_OK(r->out.result)) {
+ if (2*strlen_m_term(name) > r->in.name->size) {
+ return WERR_MORE_DATA;
+ }
+ r->out.name->length = 2*strlen_m_term(name);
+ r->out.name->name = name;
+ r->out.keyclass = talloc_zero(mem_ctx, struct winreg_StringBuf);
+ if (r->in.last_changed_time) {
+ r->out.last_changed_time = &last_mod;
+ }
+ }
+
+ return r->out.result;
+}
+
+
+/*
+ winreg_EnumValue
+*/
+static WERROR dcesrv_winreg_EnumValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_EnumValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ WERROR result;
+ const char *data_name;
+ uint32_t data_type;
+ DATA_BLOB data;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ key = h->data;
+
+ result = reg_key_get_value_by_index(mem_ctx, key, r->in.enum_index,
+ &data_name,
+ &data_type, &data);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ /* the client can optionally pass a NULL for type, meaning they don't
+ want that back */
+ if (r->in.type != NULL) {
+ r->out.type = talloc(mem_ctx, enum winreg_Type);
+ *r->out.type = data_type;
+ }
+
+ /* check the client has enough room for the value */
+ if (r->in.value != NULL &&
+ r->in.size != NULL &&
+ data.length > *r->in.size) {
+ return WERR_MORE_DATA;
+ }
+
+ /* and enough room for the name */
+ if (r->in.name->size < 2*strlen_m_term(data_name)) {
+ return WERR_MORE_DATA;
+ }
+
+ r->out.name->name = data_name;
+ r->out.name->length = 2*strlen_m_term(data_name);
+ r->out.name->size = r->in.name->size;
+
+ if (r->in.value) {
+ r->out.value = data.data;
+ }
+
+ if (r->in.size) {
+ r->out.size = talloc(mem_ctx, uint32_t);
+ *r->out.size = data.length;
+ r->out.length = r->out.size;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ winreg_FlushKey
+*/
+static WERROR dcesrv_winreg_FlushKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_FlushKey *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_key_flush(h->data);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_GetKeySecurity
+*/
+static WERROR dcesrv_winreg_GetKeySecurity(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_GetKeySecurity *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_LoadKey
+*/
+static WERROR dcesrv_winreg_LoadKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_LoadKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_NotifyChangeKeyValue
+*/
+static WERROR dcesrv_winreg_NotifyChangeKeyValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_NotifyChangeKeyValue *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_OpenKey
+*/
+static WERROR dcesrv_winreg_OpenKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_OpenKey *r)
+{
+ struct dcesrv_handle *h, *newh;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.parent_handle, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ if (r->in.keyname.name && strcmp(r->in.keyname.name, "") == 0) {
+ newh = talloc_reference(dce_call->context, h);
+ result = WERR_OK;
+ } else {
+ newh = dcesrv_handle_new(dce_call->context, HTYPE_REGKEY);
+ result = reg_open_key(newh, (struct registry_key *)h->data,
+ r->in.keyname.name,
+ (struct registry_key **)&newh->data);
+ }
+
+ if (W_ERROR_IS_OK(result)) {
+ r->out.handle = &newh->wire_handle;
+ } else {
+ talloc_free(newh);
+ }
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+
+}
+
+
+/*
+ winreg_QueryInfoKey
+*/
+static WERROR dcesrv_winreg_QueryInfoKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryInfoKey *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *k;
+ WERROR ret;
+ const char *classname = NULL;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ k = h->data;
+
+ ret = reg_key_get_info(mem_ctx, k, &classname, r->out.num_subkeys,
+ r->out.num_values, r->out.last_changed_time,
+ r->out.max_subkeylen, r->out.max_valnamelen,
+ r->out.max_valbufsize);
+
+ if (r->out.classname != NULL)
+ r->out.classname->name = classname;
+
+ return ret;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_QueryValue
+*/
+static WERROR dcesrv_winreg_QueryValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ uint32_t value_type;
+ DATA_BLOB value_data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ key = h->data;
+
+ result = reg_key_get_value_by_name(mem_ctx, key, r->in.value_name.name,
+ &value_type, &value_data);
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ /* Just asking for the size of the buffer */
+ r->out.type = talloc(mem_ctx, uint32_t);
+ if (!r->out.type) {
+ return WERR_NOMEM;
+ }
+ *r->out.type = value_type;
+ r->out.length = talloc(mem_ctx, uint32_t);
+ if (!r->out.length) {
+ return WERR_NOMEM;
+ }
+ *r->out.length = value_data.length;
+ if (r->in.data == NULL) {
+ r->out.size = talloc(mem_ctx, uint32_t);
+ *r->out.size = value_data.length;
+ } else {
+ r->out.size = r->in.size;
+ r->out.data = value_data.data;
+ }
+
+ return WERR_OK;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_ReplaceKey
+*/
+static WERROR dcesrv_winreg_ReplaceKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_ReplaceKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_RestoreKey
+*/
+static WERROR dcesrv_winreg_RestoreKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_RestoreKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SaveKey
+*/
+static WERROR dcesrv_winreg_SaveKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SaveKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SetKeySecurity
+*/
+static WERROR dcesrv_winreg_SetKeySecurity(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SetKeySecurity *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SetValue
+*/
+static WERROR dcesrv_winreg_SetValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SetValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ WERROR result;
+ DATA_BLOB data;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ data.data = r->in.data;
+ data.length = r->in.size;
+ result = reg_val_set(key, r->in.name.name, r->in.type, data);
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_UnLoadKey
+*/
+static WERROR dcesrv_winreg_UnLoadKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_UnLoadKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_InitiateSystemShutdown
+*/
+static WERROR dcesrv_winreg_InitiateSystemShutdown(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_InitiateSystemShutdown *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_AbortSystemShutdown
+*/
+static WERROR dcesrv_winreg_AbortSystemShutdown(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_AbortSystemShutdown *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_GetVersion
+*/
+static WERROR dcesrv_winreg_GetVersion(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_GetVersion *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ r->out.version = talloc(mem_ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(r->out.version);
+
+ *r->out.version = 5;
+
+ return WERR_OK;
+}
+
+
+/*
+ winreg_QueryMultipleValues
+*/
+static WERROR dcesrv_winreg_QueryMultipleValues(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryMultipleValues *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_InitiateSystemShutdownEx
+*/
+static WERROR dcesrv_winreg_InitiateSystemShutdownEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_InitiateSystemShutdownEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SaveKeyEx
+*/
+static WERROR dcesrv_winreg_SaveKeyEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SaveKeyEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_QueryMultipleValues2
+*/
+static WERROR dcesrv_winreg_QueryMultipleValues2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryMultipleValues2 *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_winreg_s.c"
diff --git a/source4/rpc_server/wkssvc/dcesrv_wkssvc.c b/source4/rpc_server/wkssvc/dcesrv_wkssvc.c
new file mode 100644
index 0000000000..cbade288ca
--- /dev/null
+++ b/source4/rpc_server/wkssvc/dcesrv_wkssvc.c
@@ -0,0 +1,416 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the wkssvc pipe
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "rpc_server/common/common.h"
+
+/*
+ wkssvc_NetWkstaGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetWkstaGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaGetInfo *r)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ ZERO_STRUCT(r->out);
+ r->out.info = talloc_zero(mem_ctx, union wkssvc_NetWkstaInfo);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info);
+
+ /* NOTE: win2k3 ignores r->in.server_name completly so we do --metze */
+
+ switch(r->in.level) {
+ case 100:
+ {
+ struct wkssvc_NetWkstaInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo100);
+ W_ERROR_HAVE_NO_MEMORY(info100);
+
+ info100->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info100->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, NULL);
+ W_ERROR_HAVE_NO_MEMORY(info100->server_name);
+ info100->domain_name = dcesrv_common_get_domain_name(mem_ctx, dce_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info100->domain_name);
+ info100->version_major = dcesrv_common_get_version_major(mem_ctx, dce_ctx->lp_ctx);
+ info100->version_minor = dcesrv_common_get_version_minor(mem_ctx, dce_ctx->lp_ctx);
+
+ r->out.info->info100 = info100;
+ return WERR_OK;
+ }
+ case 101:
+ {
+ struct wkssvc_NetWkstaInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo101);
+ W_ERROR_HAVE_NO_MEMORY(info101);
+
+ info101->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info101->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, NULL);
+ W_ERROR_HAVE_NO_MEMORY(info101->server_name);
+ info101->domain_name = dcesrv_common_get_domain_name(mem_ctx, dce_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info101->domain_name);
+ info101->version_major = dcesrv_common_get_version_major(mem_ctx, dce_ctx->lp_ctx);
+ info101->version_minor = dcesrv_common_get_version_minor(mem_ctx, dce_ctx->lp_ctx);
+ info101->lan_root = dcesrv_common_get_lan_root(mem_ctx, dce_ctx);
+
+ r->out.info->info101 = info101;
+ return WERR_OK;
+ }
+ case 102:
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ case 502:
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ wkssvc_NetWkstaSetInfo
+*/
+static WERROR dcesrv_wkssvc_NetWkstaSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetWkstaEnumUsers
+*/
+static WERROR dcesrv_wkssvc_NetWkstaEnumUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaEnumUsers *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaUserGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaUserGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaUserGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaUserSetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaUserSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaUserSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetWkstaTransportEnum
+*/
+static WERROR dcesrv_wkssvc_NetWkstaTransportEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaTransportEnum *r)
+{
+ r->out.total_entries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.info->level) {
+ case 0:
+ r->out.info = talloc(mem_ctx, struct wkssvc_NetWkstaTransportInfo);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info);
+ r->out.info->level = r->in.info->level;
+ r->out.info->ctr.ctr0 = talloc(mem_ctx, struct wkssvc_NetWkstaTransportCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->ctr.ctr0);
+
+ r->out.info->ctr.ctr0->count = 0;
+ r->out.info->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ wkssvc_NetrWkstaTransportAdd
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaTransportAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaTransportAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaTransportDel
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaTransportDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaTransportDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseAdd
+*/
+static WERROR dcesrv_wkssvc_NetrUseAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrUseGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseDel
+*/
+static WERROR dcesrv_wkssvc_NetrUseDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseEnum
+*/
+static WERROR dcesrv_wkssvc_NetrUseEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseEnum *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrMessageBufferSend
+*/
+static WERROR dcesrv_wkssvc_NetrMessageBufferSend(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrMessageBufferSend *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWorkstationStatisticsGet
+*/
+static WERROR dcesrv_wkssvc_NetrWorkstationStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWorkstationStatisticsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrLogonDomainNameAdd
+*/
+static WERROR dcesrv_wkssvc_NetrLogonDomainNameAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrLogonDomainNameAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrLogonDomainNameDel
+*/
+static WERROR dcesrv_wkssvc_NetrLogonDomainNameDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrLogonDomainNameDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrJoinDomain
+*/
+static WERROR dcesrv_wkssvc_NetrJoinDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrJoinDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUnjoinDomain
+*/
+static WERROR dcesrv_wkssvc_NetrUnjoinDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUnjoinDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRenameMachineInDomain
+*/
+static WERROR dcesrv_wkssvc_NetrRenameMachineInDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRenameMachineInDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrValidateName
+*/
+static WERROR dcesrv_wkssvc_NetrValidateName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrValidateName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinInformation
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinableOus
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinableOus(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinableOus *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ WKSSVC_NETRJOINDOMAIN2
+*/
+static WERROR dcesrv_wkssvc_NetrJoinDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrJoinDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ WKSSVC_NETRUNJOINDOMAIN2
+*/
+static WERROR dcesrv_wkssvc_NetrUnjoinDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUnjoinDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRenameMachineInDomain2
+*/
+static WERROR dcesrv_wkssvc_NetrRenameMachineInDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRenameMachineInDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrValidateName2
+*/
+static WERROR dcesrv_wkssvc_NetrValidateName2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrValidateName2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinableOus2
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinableOus2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinableOus2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrAddAlternateComputername
+*/
+static WERROR dcesrv_wkssvc_NetrAddAlternateComputerName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrAddAlternateComputerName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRemoveAlternateComputername
+*/
+static WERROR dcesrv_wkssvc_NetrRemoveAlternateComputerName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRemoveAlternateComputerName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrSetPrimaryComputername
+*/
+static WERROR dcesrv_wkssvc_NetrSetPrimaryComputername(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrSetPrimaryComputername *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrEnumerateComputerNames
+*/
+static WERROR dcesrv_wkssvc_NetrEnumerateComputerNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrEnumerateComputerNames *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_wkssvc_s.c"