diff options
Diffstat (limited to 'source4/rpc_server')
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 = ¬ify_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" |