summaryrefslogtreecommitdiffstats
path: root/source3/rpc_server
diff options
context:
space:
mode:
Diffstat (limited to 'source3/rpc_server')
-rw-r--r--source3/rpc_server/dfs/srv_dfs_nt.c606
-rw-r--r--source3/rpc_server/dssetup/srv_dssetup_nt.c232
-rw-r--r--source3/rpc_server/echo/srv_echo_nt.c126
-rw-r--r--source3/rpc_server/epmapper/srv_epmapper.c1063
-rw-r--r--source3/rpc_server/eventlog/srv_eventlog_nt.c1047
-rw-r--r--source3/rpc_server/eventlog/srv_eventlog_reg.c267
-rw-r--r--source3/rpc_server/eventlog/srv_eventlog_reg.h29
-rw-r--r--source3/rpc_server/fss/srv_fss_agent.c1776
-rw-r--r--source3/rpc_server/fss/srv_fss_private.h92
-rw-r--r--source3/rpc_server/fss/srv_fss_state.c698
-rw-r--r--source3/rpc_server/initshutdown/srv_initshutdown_nt.c84
-rw-r--r--source3/rpc_server/lsa/srv_lsa_nt.c5235
-rw-r--r--source3/rpc_server/mdssvc/README14
-rw-r--r--source3/rpc_server/mdssvc/dalloc.c404
-rw-r--r--source3/rpc_server/mdssvc/dalloc.h165
-rw-r--r--source3/rpc_server/mdssvc/elasticsearch_mappings.json142
-rw-r--r--source3/rpc_server/mdssvc/es_lexer.l92
-rw-r--r--source3/rpc_server/mdssvc/es_mapping.c241
-rw-r--r--source3/rpc_server/mdssvc/es_mapping.h49
-rw-r--r--source3/rpc_server/mdssvc/es_parser.y686
-rw-r--r--source3/rpc_server/mdssvc/es_parser_test.c97
-rw-r--r--source3/rpc_server/mdssvc/marshalling.c1422
-rw-r--r--source3/rpc_server/mdssvc/marshalling.h63
-rw-r--r--source3/rpc_server/mdssvc/mdssvc.c1893
-rw-r--r--source3/rpc_server/mdssvc/mdssvc.h168
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_es.c865
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_es.h108
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_noindex.c57
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_noindex.h26
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_tracker.c499
-rw-r--r--source3/rpc_server/mdssvc/mdssvc_tracker.h62
-rw-r--r--source3/rpc_server/mdssvc/sparql_lexer.l67
-rw-r--r--source3/rpc_server/mdssvc/sparql_mapping.c378
-rw-r--r--source3/rpc_server/mdssvc/sparql_mapping.h58
-rw-r--r--source3/rpc_server/mdssvc/sparql_parser.y483
-rw-r--r--source3/rpc_server/mdssvc/sparql_parser_test.c47
-rw-r--r--source3/rpc_server/mdssvc/srv_mdssvc_nt.c319
-rw-r--r--source3/rpc_server/mdssvc/srv_mdssvc_nt.h27
-rw-r--r--source3/rpc_server/mdssvc/test_mdsparser_es.c304
-rw-r--r--source3/rpc_server/netlogon/srv_netlog_nt.c2937
-rw-r--r--source3/rpc_server/ntsvcs/srv_ntsvcs_nt.c810
-rw-r--r--source3/rpc_server/rpc_config.c77
-rw-r--r--source3/rpc_server/rpc_config.h30
-rw-r--r--source3/rpc_server/rpc_handles.c233
-rw-r--r--source3/rpc_server/rpc_host.c2984
-rw-r--r--source3/rpc_server/rpc_ncacn_np.c217
-rw-r--r--source3/rpc_server/rpc_ncacn_np.h58
-rw-r--r--source3/rpc_server/rpc_pipes.h73
-rw-r--r--source3/rpc_server/rpc_server.c309
-rw-r--r--source3/rpc_server/rpc_server.h72
-rw-r--r--source3/rpc_server/rpc_sock_helper.c399
-rw-r--r--source3/rpc_server/rpc_sock_helper.h36
-rw-r--r--source3/rpc_server/rpc_worker.c1287
-rw-r--r--source3/rpc_server/rpc_worker.h40
-rw-r--r--source3/rpc_server/rpcd_classic.c149
-rw-r--r--source3/rpc_server/rpcd_epmapper.c109
-rw-r--r--source3/rpc_server/rpcd_fsrvp.c82
-rw-r--r--source3/rpc_server/rpcd_lsad.c141
-rw-r--r--source3/rpc_server/rpcd_mdssvc.c71
-rw-r--r--source3/rpc_server/rpcd_rpcecho.c89
-rw-r--r--source3/rpc_server/rpcd_spoolss.c91
-rw-r--r--source3/rpc_server/rpcd_winreg.c71
-rw-r--r--source3/rpc_server/rpcd_witness.c120
-rw-r--r--source3/rpc_server/samr/srv_samr_chgpasswd.c1421
-rw-r--r--source3/rpc_server/samr/srv_samr_nt.c7910
-rw-r--r--source3/rpc_server/samr/srv_samr_util.c756
-rw-r--r--source3/rpc_server/samr/srv_samr_util.h89
-rw-r--r--source3/rpc_server/spoolss/iremotewinspool_util.c156
-rw-r--r--source3/rpc_server/spoolss/iremotewinspool_util.h21
-rw-r--r--source3/rpc_server/spoolss/srv_iremotewinspool.c2382
-rw-r--r--source3/rpc_server/spoolss/srv_iremotewinspool_nt.c924
-rw-r--r--source3/rpc_server/spoolss/srv_spoolss_handle.h77
-rw-r--r--source3/rpc_server/spoolss/srv_spoolss_nt.c11622
-rw-r--r--source3/rpc_server/spoolss/srv_spoolss_nt.h40
-rw-r--r--source3/rpc_server/spoolss/srv_spoolss_util.c917
-rw-r--r--source3/rpc_server/spoolss/srv_spoolss_util.h190
-rw-r--r--source3/rpc_server/srv_access_check.c168
-rw-r--r--source3/rpc_server/srv_access_check.h44
-rw-r--r--source3/rpc_server/srv_pipe_hnd.c368
-rw-r--r--source3/rpc_server/srv_pipe_hnd.h50
-rw-r--r--source3/rpc_server/srvsvc/srv_srvsvc_nt.c3202
-rw-r--r--source3/rpc_server/svcctl/srv_svcctl_nt.c1497
-rw-r--r--source3/rpc_server/svcctl/srv_svcctl_nt.h33
-rw-r--r--source3/rpc_server/svcctl/srv_svcctl_reg.c678
-rw-r--r--source3/rpc_server/svcctl/srv_svcctl_reg.h29
-rw-r--r--source3/rpc_server/winreg/srv_winreg_nt.c1126
-rw-r--r--source3/rpc_server/witness/srv_witness_nt.c2465
-rw-r--r--source3/rpc_server/wkssvc/srv_wkssvc_nt.c964
-rw-r--r--source3/rpc_server/wscript_build308
89 files changed, 67913 insertions, 0 deletions
diff --git a/source3/rpc_server/dfs/srv_dfs_nt.c b/source3/rpc_server/dfs/srv_dfs_nt.c
new file mode 100644
index 0000000..8eaa59a
--- /dev/null
+++ b/source3/rpc_server/dfs/srv_dfs_nt.c
@@ -0,0 +1,606 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for Dfs
+ * Copyright (C) Shirish Kalele 2000.
+ * Copyright (C) Jeremy Allison 2001-2007.
+ * Copyright (C) Jelmer Vernooij 2005-2006.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This is the implementation of the dfs pipe. */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_dfs.h"
+#include "librpc/gen_ndr/ndr_dfs_scompat.h"
+#include "msdfs.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "auth.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_MSDFS
+
+/* This function does not return a WERROR or NTSTATUS code but rather 1 if
+ dfs exists, or 0 otherwise. */
+
+void _dfs_GetManagerVersion(struct pipes_struct *p, struct dfs_GetManagerVersion *r)
+{
+ if (lp_host_msdfs()) {
+ *r->out.version = DFS_MANAGER_VERSION_NT4;
+ } else {
+ *r->out.version = (enum dfs_ManagerVersion)0;
+ }
+}
+
+WERROR _dfs_Add(struct pipes_struct *p, struct dfs_Add *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct junction_map *jn = NULL;
+ struct referral *old_referral_list = NULL;
+ bool self_ref = False;
+ size_t consumedcnt = 0;
+ char *altpath = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_tos();
+ const char *pathnamep = r->in.path;
+
+ if (session_info->unix_token->uid != sec_initial_uid()) {
+ DEBUG(10,("_dfs_add: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ jn = talloc_zero(ctx, struct junction_map);
+ if (!jn) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ DEBUG(5,("init_reply_dfs_add: Request to add %s -> %s\\%s.\n",
+ r->in.path, r->in.server, r->in.share));
+
+ altpath = talloc_asprintf(ctx, "%s\\%s",
+ r->in.server,
+ r->in.share);
+ if (!altpath) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ while (IS_DIRECTORY_SEP(pathnamep[0]) &&
+ IS_DIRECTORY_SEP(pathnamep[1])) {
+ pathnamep++;
+ }
+
+ /* The following call can change the cwd. */
+ status = get_referred_path(ctx,
+ session_info,
+ pathnamep,
+ remote_address,
+ local_address,
+ jn, &consumedcnt, &self_ref);
+ if(!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ jn->referral_count += 1;
+ old_referral_list = jn->referral_list;
+
+ if (jn->referral_count < 1) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ jn->referral_list = talloc_array(ctx, struct referral, jn->referral_count);
+ if(jn->referral_list == NULL) {
+ DEBUG(0,("init_reply_dfs_add: talloc failed for referral list!\n"));
+ return WERR_NERR_DFSINTERNALERROR;
+ }
+
+ if(old_referral_list && jn->referral_list) {
+ memcpy(jn->referral_list, old_referral_list,
+ sizeof(struct referral)*jn->referral_count-1);
+ }
+
+ jn->referral_list[jn->referral_count-1].proximity = 0;
+ jn->referral_list[jn->referral_count-1].ttl = REFERRAL_TTL;
+ jn->referral_list[jn->referral_count-1].alternate_path = altpath;
+
+ if (!create_msdfs_link(jn, session_info)) {
+ return WERR_NERR_DFSCANTCREATEJUNCTIONPOINT;
+ }
+
+ return WERR_OK;
+}
+
+WERROR _dfs_Remove(struct pipes_struct *p, struct dfs_Remove *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct junction_map *jn = NULL;
+ bool self_ref = False;
+ size_t consumedcnt = 0;
+ bool found = False;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *altpath = NULL;
+ NTSTATUS status;
+ const char *pathnamep = r->in.dfs_entry_path;
+
+ if (session_info->unix_token->uid != sec_initial_uid()) {
+ DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ jn = talloc_zero(ctx, struct junction_map);
+ if (!jn) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (r->in.servername && r->in.sharename) {
+ altpath = talloc_asprintf(ctx, "%s\\%s",
+ r->in.servername,
+ r->in.sharename);
+ if (!altpath) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (!strlower_m(altpath)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
+ r->in.dfs_entry_path, r->in.servername, r->in.sharename));
+ }
+
+ while (IS_DIRECTORY_SEP(pathnamep[0]) &&
+ IS_DIRECTORY_SEP(pathnamep[1])) {
+ pathnamep++;
+ }
+
+ status = get_referred_path(ctx,
+ session_info,
+ pathnamep,
+ remote_address,
+ local_address,
+ jn, &consumedcnt, &self_ref);
+ if(!NT_STATUS_IS_OK(status)) {
+ return WERR_NERR_DFSNOSUCHVOLUME;
+ }
+
+ /* if no server-share pair given, remove the msdfs link completely */
+ if(!r->in.servername && !r->in.sharename) {
+ if(!remove_msdfs_link(jn, session_info)) {
+ return WERR_NERR_DFSNOSUCHVOLUME;
+ }
+ } else {
+ size_t i = 0;
+ /* compare each referral in the list with the one to remove */
+ DBG_DEBUG("altpath: .%s. refcnt: %zu\n",
+ altpath,
+ jn->referral_count);
+ for(i=0;i<jn->referral_count;i++) {
+ char *refpath = talloc_strdup(ctx,
+ jn->referral_list[i].alternate_path);
+ if (!refpath) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ trim_char(refpath, '\\', '\\');
+ DEBUG(10,("_dfs_remove: refpath: .%s.\n", refpath));
+ if(strequal(refpath, altpath)) {
+ *(jn->referral_list[i].alternate_path)='\0';
+ DEBUG(10,("_dfs_remove: Removal request matches referral %s\n",
+ refpath));
+ found = True;
+ }
+ }
+
+ if(!found) {
+ return WERR_NERR_DFSNOSUCHSHARE;
+ }
+
+ /* Only one referral, remove it */
+ if(jn->referral_count == 1) {
+ if(!remove_msdfs_link(jn, session_info)) {
+ return WERR_NERR_DFSNOSUCHVOLUME;
+ }
+ } else {
+ if(!create_msdfs_link(jn, session_info)) {
+ return WERR_NERR_DFSCANTCREATEJUNCTIONPOINT;
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+
+static bool init_reply_dfs_info_1(TALLOC_CTX *mem_ctx, struct junction_map* j,struct dfs_Info1* dfs1)
+{
+ dfs1->path = talloc_asprintf(mem_ctx,
+ "\\\\%s\\%s\\%s", lp_netbios_name(),
+ j->service_name, j->volume_name);
+ if (dfs1->path == NULL)
+ return False;
+
+ DEBUG(5,("init_reply_dfs_info_1: initing entrypath: %s\n",dfs1->path));
+ return True;
+}
+
+static bool init_reply_dfs_info_2(TALLOC_CTX *mem_ctx, struct junction_map* j, struct dfs_Info2* dfs2)
+{
+ dfs2->path = talloc_asprintf(mem_ctx,
+ "\\\\%s\\%s\\%s", lp_netbios_name(), j->service_name, j->volume_name);
+ if (dfs2->path == NULL)
+ return False;
+ dfs2->comment = talloc_strdup(mem_ctx, j->comment);
+ dfs2->state = 1; /* set up state of dfs junction as OK */
+ dfs2->num_stores = j->referral_count;
+ return True;
+}
+
+static bool init_reply_dfs_info_3(TALLOC_CTX *mem_ctx, struct junction_map* j, struct dfs_Info3* dfs3)
+{
+ size_t ii;
+ if (j->volume_name[0] == '\0')
+ dfs3->path = talloc_asprintf(mem_ctx, "\\\\%s\\%s",
+ lp_netbios_name(), j->service_name);
+ else
+ dfs3->path = talloc_asprintf(mem_ctx, "\\\\%s\\%s\\%s", lp_netbios_name(),
+ j->service_name, j->volume_name);
+
+ if (dfs3->path == NULL)
+ return False;
+
+ dfs3->comment = talloc_strdup(mem_ctx, j->comment);
+ dfs3->state = 1;
+ dfs3->num_stores = j->referral_count;
+
+ /* also enumerate the stores */
+ if (j->referral_count) {
+ dfs3->stores = talloc_array(mem_ctx, struct dfs_StorageInfo, j->referral_count);
+ if (!dfs3->stores)
+ return False;
+ memset(dfs3->stores, '\0', j->referral_count * sizeof(struct dfs_StorageInfo));
+ } else {
+ dfs3->stores = NULL;
+ }
+
+ for(ii=0;ii<j->referral_count;ii++) {
+ char* p;
+ char *path = NULL;
+ struct dfs_StorageInfo* stor = &(dfs3->stores[ii]);
+ struct referral* ref = &(j->referral_list[ii]);
+
+ path = talloc_strdup(mem_ctx, ref->alternate_path);
+ if (!path) {
+ return False;
+ }
+ trim_char(path,'\\','\0');
+ p = strrchr_m(path,'\\');
+ if(p==NULL) {
+ DEBUG(4,("init_reply_dfs_info_3: invalid path: no \\ found in %s\n",path));
+ continue;
+ }
+ *p = '\0';
+ DBG_INFO("storage %zu: %s.%s\n",ii,path,p+1);
+ stor->state = 2; /* set all stores as ONLINE */
+ stor->server = talloc_strdup(mem_ctx, path);
+ stor->share = talloc_strdup(mem_ctx, p+1);
+ }
+ return True;
+}
+
+static bool init_reply_dfs_info_100(TALLOC_CTX *mem_ctx, struct junction_map* j, struct dfs_Info100* dfs100)
+{
+ dfs100->comment = talloc_strdup(mem_ctx, j->comment);
+ return True;
+}
+
+WERROR _dfs_Enum(struct pipes_struct *p, struct dfs_Enum *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct junction_map *jn = NULL;
+ size_t num_jn = 0;
+ size_t i;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ jn = enum_msdfs_links(ctx, session_info, &num_jn);
+ if (!jn || num_jn == 0) {
+ num_jn = 0;
+ jn = NULL;
+ }
+
+ DEBUG(5,("_dfs_Enum: %u junctions found in Dfs, doing level %d\n",
+ (unsigned int)num_jn, r->in.level));
+
+ *r->out.total = num_jn;
+
+ /* Create the return array */
+ switch (r->in.level) {
+ case 1:
+ if (num_jn) {
+ if ((r->out.info->e.info1->s = talloc_array(ctx, struct dfs_Info1, num_jn)) == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ r->out.info->e.info1->s = NULL;
+ }
+ r->out.info->e.info1->count = num_jn;
+ break;
+ case 2:
+ if (num_jn) {
+ if ((r->out.info->e.info2->s = talloc_array(ctx, struct dfs_Info2, num_jn)) == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ r->out.info->e.info2->s = NULL;
+ }
+ r->out.info->e.info2->count = num_jn;
+ break;
+ case 3:
+ if (num_jn) {
+ if ((r->out.info->e.info3->s = talloc_array(ctx, struct dfs_Info3, num_jn)) == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ r->out.info->e.info3->s = NULL;
+ }
+ r->out.info->e.info3->count = num_jn;
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ for (i = 0; i < num_jn; i++) {
+ switch (r->in.level) {
+ case 1:
+ init_reply_dfs_info_1(ctx, &jn[i], &r->out.info->e.info1->s[i]);
+ break;
+ case 2:
+ init_reply_dfs_info_2(ctx, &jn[i], &r->out.info->e.info2->s[i]);
+ break;
+ case 3:
+ init_reply_dfs_info_3(ctx, &jn[i], &r->out.info->e.info3->s[i]);
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ return WERR_OK;
+}
+
+WERROR _dfs_GetInfo(struct pipes_struct *p, struct dfs_GetInfo *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ size_t consumedcnt = 0;
+ struct junction_map *jn = NULL;
+ bool self_ref = False;
+ TALLOC_CTX *ctx = talloc_tos();
+ bool ret;
+ NTSTATUS status;
+ const char *pathnamep = r->in.dfs_entry_path;
+
+ jn = talloc_zero(ctx, struct junction_map);
+ if (!jn) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ while (IS_DIRECTORY_SEP(pathnamep[0]) &&
+ IS_DIRECTORY_SEP(pathnamep[1])) {
+ pathnamep++;
+ }
+
+ ret = create_junction(ctx, pathnamep, jn);
+ if (!ret) {
+ return WERR_NERR_DFSNOSUCHSERVER;
+ }
+
+ /* The following call can change the cwd. */
+ status = get_referred_path(ctx,
+ session_info,
+ pathnamep,
+ remote_address,
+ local_address,
+ jn, &consumedcnt, &self_ref);
+ if(!NT_STATUS_IS_OK(status) || consumedcnt < strlen(pathnamep)) {
+ return WERR_NERR_DFSNOSUCHVOLUME;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ r->out.info->info1 = talloc_zero(ctx,struct dfs_Info1);
+ if (!r->out.info->info1) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ ret = init_reply_dfs_info_1(ctx, jn, r->out.info->info1);
+ break;
+ case 2:
+ r->out.info->info2 = talloc_zero(ctx,struct dfs_Info2);
+ if (!r->out.info->info2) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ ret = init_reply_dfs_info_2(ctx, jn, r->out.info->info2);
+ break;
+ case 3:
+ r->out.info->info3 = talloc_zero(ctx,struct dfs_Info3);
+ if (!r->out.info->info3) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ ret = init_reply_dfs_info_3(ctx, jn, r->out.info->info3);
+ break;
+ case 100:
+ r->out.info->info100 = talloc_zero(ctx,struct dfs_Info100);
+ if (!r->out.info->info100) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ ret = init_reply_dfs_info_100(ctx, jn, r->out.info->info100);
+ break;
+ default:
+ r->out.info->info1 = NULL;
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!ret)
+ return WERR_INVALID_PARAMETER;
+
+ return WERR_OK;
+}
+
+WERROR _dfs_SetInfo(struct pipes_struct *p, struct dfs_SetInfo *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Rename(struct pipes_struct *p, struct dfs_Rename *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Move(struct pipes_struct *p, struct dfs_Move *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_ManagerGetConfigInfo(struct pipes_struct *p, struct dfs_ManagerGetConfigInfo *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_ManagerSendSiteInfo(struct pipes_struct *p, struct dfs_ManagerSendSiteInfo *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_AddFtRoot(struct pipes_struct *p, struct dfs_AddFtRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_RemoveFtRoot(struct pipes_struct *p, struct dfs_RemoveFtRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_AddStdRoot(struct pipes_struct *p, struct dfs_AddStdRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_RemoveStdRoot(struct pipes_struct *p, struct dfs_RemoveStdRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_ManagerInitialize(struct pipes_struct *p, struct dfs_ManagerInitialize *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_AddStdRootForced(struct pipes_struct *p, struct dfs_AddStdRootForced *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_GetDcAddress(struct pipes_struct *p, struct dfs_GetDcAddress *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_SetDcAddress(struct pipes_struct *p, struct dfs_SetDcAddress *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_FlushFtTable(struct pipes_struct *p, struct dfs_FlushFtTable *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Add2(struct pipes_struct *p, struct dfs_Add2 *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Remove2(struct pipes_struct *p, struct dfs_Remove2 *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_EnumEx(struct pipes_struct *p, struct dfs_EnumEx *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_SetInfo2(struct pipes_struct *p, struct dfs_SetInfo2 *r)
+{
+ /* FIXME: Implement your code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_dfs_scompat.c"
diff --git a/source3/rpc_server/dssetup/srv_dssetup_nt.c b/source3/rpc_server/dssetup/srv_dssetup_nt.c
new file mode 100644
index 0000000..932452b
--- /dev/null
+++ b/source3/rpc_server/dssetup/srv_dssetup_nt.c
@@ -0,0 +1,232 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997.
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997.
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Gerald Carter 2002.
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "librpc/gen_ndr/ndr_dssetup_scompat.h"
+#include "secrets.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ Fill in a dssetup_DsRolePrimaryDomInfoBasic structure
+ ********************************************************************/
+
+static WERROR fill_dsrole_dominfo_basic(TALLOC_CTX *ctx,
+ struct dssetup_DsRolePrimaryDomInfoBasic **info)
+{
+ struct dssetup_DsRolePrimaryDomInfoBasic *basic = NULL;
+ char *dnsdomain = NULL;
+
+ DEBUG(10,("fill_dsrole_dominfo_basic: enter\n"));
+
+ basic = talloc_zero(ctx, struct dssetup_DsRolePrimaryDomInfoBasic);
+ if (!basic) {
+ DEBUG(0,("fill_dsrole_dominfo_basic: out of memory\n"));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ switch (lp_server_role()) {
+ case ROLE_STANDALONE:
+ basic->role = DS_ROLE_STANDALONE_SERVER;
+ basic->domain = get_global_sam_name();
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ basic->role = DS_ROLE_MEMBER_SERVER;
+ basic->domain = lp_workgroup();
+ break;
+ case ROLE_DOMAIN_BDC:
+ basic->role = DS_ROLE_BACKUP_DC;
+ basic->domain = get_global_sam_name();
+ break;
+ case ROLE_DOMAIN_PDC:
+ case ROLE_IPA_DC:
+ basic->role = DS_ROLE_PRIMARY_DC;
+ basic->domain = get_global_sam_name();
+ break;
+ }
+
+ if (secrets_fetch_domain_guid(lp_workgroup(), &basic->domain_guid)) {
+ basic->flags |= DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ }
+
+ /* fill in some additional fields if we are a member of an AD domain */
+
+ if (lp_security() == SEC_ADS) {
+ dnsdomain = talloc_strdup(ctx, lp_realm());
+ if (!dnsdomain) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (!strlower_m(dnsdomain)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ basic->dns_domain = dnsdomain;
+
+ /* FIXME!! We really should fill in the correct forest
+ name. Should get this information from winbindd. */
+ basic->forest = dnsdomain;
+ } else {
+ /* security = domain should not fill in the dns or
+ forest name */
+ basic->dns_domain = NULL;
+ basic->forest = NULL;
+ }
+
+ *info = basic;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ Implement the _dssetup_DsRoleGetPrimaryDomainInformation() call
+ ********************************************************************/
+
+WERROR _dssetup_DsRoleGetPrimaryDomainInformation(struct pipes_struct *p,
+ struct dssetup_DsRoleGetPrimaryDomainInformation *r)
+{
+ WERROR werr = WERR_OK;
+
+ switch (r->in.level) {
+
+ case DS_ROLE_BASIC_INFORMATION: {
+ struct dssetup_DsRolePrimaryDomInfoBasic *basic = NULL;
+ werr = fill_dsrole_dominfo_basic(p->mem_ctx, &basic);
+ if (W_ERROR_IS_OK(werr)) {
+ r->out.info->basic = *basic;
+ }
+ break;
+ }
+ default:
+ DEBUG(0,("_dssetup_DsRoleGetPrimaryDomainInformation: "
+ "Unknown info level [%d]!\n", r->in.level));
+ werr = WERR_INVALID_LEVEL;
+ }
+
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDnsNameToFlatName(struct pipes_struct *p,
+ struct dssetup_DsRoleDnsNameToFlatName *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDcAsDc(struct pipes_struct *p,
+ struct dssetup_DsRoleDcAsDc *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDcAsReplica(struct pipes_struct *p,
+ struct dssetup_DsRoleDcAsReplica *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDemoteDc(struct pipes_struct *p,
+ struct dssetup_DsRoleDemoteDc *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleGetDcOperationProgress(struct pipes_struct *p,
+ struct dssetup_DsRoleGetDcOperationProgress *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleGetDcOperationResults(struct pipes_struct *p,
+ struct dssetup_DsRoleGetDcOperationResults *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleCancel(struct pipes_struct *p,
+ struct dssetup_DsRoleCancel *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleServerSaveStateForUpgrade(struct pipes_struct *p,
+ struct dssetup_DsRoleServerSaveStateForUpgrade *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleUpgradeDownlevelServer(struct pipes_struct *p,
+ struct dssetup_DsRoleUpgradeDownlevelServer *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleAbortDownlevelServerUpgrade(struct pipes_struct *p,
+ struct dssetup_DsRoleAbortDownlevelServerUpgrade *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_dssetup_scompat.c"
diff --git a/source3/rpc_server/echo/srv_echo_nt.c b/source3/rpc_server/echo/srv_echo_nt.c
new file mode 100644
index 0000000..d5b3ddc
--- /dev/null
+++ b/source3/rpc_server/echo/srv_echo_nt.c
@@ -0,0 +1,126 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for rpcecho
+ * Copyright (C) Tim Potter 2003
+ * Copyright (C) Jelmer Vernooij 2006
+ * Copyright (C) Gerald (Jerry) Carter 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/>.
+ */
+
+/* This is the interface to the rpcecho pipe. */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "librpc/gen_ndr/ndr_echo_scompat.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* Add one to the input and return it */
+
+void _echo_AddOne(struct pipes_struct *p, struct echo_AddOne *r )
+{
+ DEBUG(10, ("_echo_AddOne\n"));
+
+ *r->out.out_data = r->in.in_data + 1;
+}
+
+/* Echo back an array of data */
+
+void _echo_EchoData(struct pipes_struct *p, struct echo_EchoData *r)
+{
+ DEBUG(10, ("_echo_EchoData\n"));
+
+ if ( r->in.len == 0 ) {
+ r->out.out_data = NULL;
+ return;
+ }
+
+ r->out.out_data = talloc_array(p->mem_ctx, uint8_t, r->in.len);
+ memcpy( r->out.out_data, r->in.in_data, r->in.len );
+ return;
+}
+
+/* Sink an array of data */
+
+void _echo_SinkData(struct pipes_struct *p, struct echo_SinkData *r)
+{
+ DEBUG(10, ("_echo_SinkData\n"));
+
+ /* My that was some yummy data! */
+ return;
+}
+
+/* Source an array of data */
+
+void _echo_SourceData(struct pipes_struct *p, struct echo_SourceData *r)
+{
+ uint32_t i;
+
+ DEBUG(10, ("_echo_SourceData\n"));
+
+ if ( r->in.len == 0 ) {
+ r->out.data = NULL;
+ return;
+ }
+
+ r->out.data = talloc_array(p->mem_ctx, uint8_t, r->in.len );
+
+ for (i = 0; i < r->in.len; i++ ) {
+ r->out.data[i] = i & 0xff;
+ }
+
+ return;
+}
+
+void _echo_TestCall(struct pipes_struct *p, struct echo_TestCall *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return;
+}
+
+NTSTATUS _echo_TestCall2(struct pipes_struct *p, struct echo_TestCall2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_OK;
+}
+
+uint32_t _echo_TestSleep(struct pipes_struct *p, struct echo_TestSleep *r)
+{
+ smb_msleep(r->in.seconds * 1000);
+ return 0;
+}
+
+void _echo_TestEnum(struct pipes_struct *p, struct echo_TestEnum *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return;
+}
+
+void _echo_TestSurrounding(struct pipes_struct *p, struct echo_TestSurrounding *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return;
+}
+
+uint16_t _echo_TestDoublePointer(struct pipes_struct *p, struct echo_TestDoublePointer *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return 0;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_echo_scompat.c"
diff --git a/source3/rpc_server/epmapper/srv_epmapper.c b/source3/rpc_server/epmapper/srv_epmapper.c
new file mode 100644
index 0000000..cf6b268
--- /dev/null
+++ b/source3/rpc_server/epmapper/srv_epmapper.c
@@ -0,0 +1,1063 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Endpoint server for the epmapper pipe
+
+ Copyright (C) 2010-2011 Andreas Schneider <asn@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "../libcli/security/security.h"
+#include "../lib/tsocket/tsocket.h"
+#include "auth.h"
+
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "librpc/gen_ndr/ndr_epmapper_scompat.h"
+#include "rpc_server/rpc_server.h"
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/strv.h"
+
+static struct tdb_wrap *epmdb = NULL;
+
+/* handle types for this module */
+enum handle_types {HTYPE_LOOKUP};
+
+typedef uint32_t error_status_t;
+
+/* An endpoint combined with an interface description */
+struct dcesrv_ep_iface {
+ const char *name;
+ struct ndr_syntax_id syntax_id;
+ struct epm_tower ep;
+};
+
+/* A rpc service interface like samr, lsarpc or netlogon */
+struct dcesrv_iface {
+ const char *name;
+ struct ndr_syntax_id syntax_id;
+};
+
+struct dcesrv_iface_list {
+ struct dcesrv_iface_list *next, *prev;
+ struct dcesrv_iface *iface;
+};
+
+/*
+ * An endpoint can serve multiple rpc services interfaces.
+ * For example \\pipe\netlogon can be used by lsarpc and netlogon.
+ */
+struct dcesrv_epm_endpoint {
+ struct dcesrv_epm_endpoint *next, *prev;
+
+ /* The type and the location of the endpoint */
+ struct dcerpc_binding *ep_description;
+
+ /* A list of rpc services able to connect to the endpoint */
+ struct dcesrv_iface_list *iface_list;
+};
+
+struct rpc_eps {
+ struct dcesrv_ep_iface *e;
+ uint32_t count;
+};
+
+struct build_ep_list_state {
+ const struct GUID *uuid;
+ const char *srv_addr;
+ TALLOC_CTX *mem_ctx;
+ struct dcesrv_ep_iface *ifaces;
+};
+
+static bool build_ep_list_fill_iface(
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax_id,
+ const char *endpoint,
+ const char *name,
+ const char *srv_addr,
+ struct dcesrv_ep_iface *dst)
+{
+ struct dcesrv_ep_iface iface = {
+ .syntax_id = *syntax_id,
+ };
+ struct dcerpc_binding *binding = NULL;
+ enum dcerpc_transport_t transport;
+ char *name_dup = NULL;
+ const char *host_addr = NULL;
+ NTSTATUS status;
+
+ /* copy without const for error path TALLOC_FREE */
+ name_dup = talloc_strdup(mem_ctx, name);
+ if (name_dup == NULL) {
+ goto fail;
+ }
+ iface.name = name_dup;
+
+ status = dcerpc_parse_binding(mem_ctx, endpoint, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_parse_binding failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dcerpc_binding_set_abstract_syntax(binding, syntax_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_set_abstract_syntax failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ transport = dcerpc_binding_get_transport(binding);
+ if (transport == NCACN_IP_TCP) {
+ const char *host = NULL;
+
+ host = dcerpc_binding_get_string_option(binding, "host");
+ if (host == NULL) {
+ host_addr = srv_addr;
+ } else if (!is_ipaddress_v4(host)) {
+ host_addr = srv_addr;
+ } else if (strcmp(host, "0.0.0.0") == 0) {
+ host_addr = srv_addr;
+ }
+ }
+
+ if (host_addr != NULL) {
+ status = dcerpc_binding_set_string_option(
+ binding, "host", host_addr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_set_string_option "
+ "failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ status = dcerpc_binding_build_tower(mem_ctx, binding, &iface.ep);
+ TALLOC_FREE(binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_build_tower failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ *dst = iface;
+ return true;
+
+fail:
+ TALLOC_FREE(binding);
+ TALLOC_FREE(name_dup);
+ TALLOC_FREE(iface.ep.floors);
+ return false;
+}
+
+static int build_ep_list_fn(
+ struct tdb_context *tdb,
+ TDB_DATA key,
+ TDB_DATA value,
+ void *private_data)
+{
+ struct build_ep_list_state *state = private_data;
+ struct ndr_syntax_id syntax_id = { .if_version = 0 };
+ const char *name = NULL;
+ char *endpoints = NULL;
+ const char *endpoint = NULL;
+ bool ok;
+
+ if ((key.dsize == 0) || (key.dptr[key.dsize-1] != '\0') ||
+ (value.dsize == 0) || (value.dptr[value.dsize-1] != '\0')) {
+ DBG_DEBUG("Invalid record\n");
+ return 0;
+ }
+
+ ok = ndr_syntax_id_from_string((char *)key.dptr, &syntax_id);
+ if (!ok) {
+ DBG_DEBUG("Invalid interface: %s\n", (char *)key.dptr);
+ return 0;
+ }
+
+ endpoints = (char *)value.dptr;
+ endpoint = endpoints;
+ name = endpoints;
+
+ while ((endpoint = strv_len_next(endpoints, value.dsize, endpoint))) {
+ size_t num_ifaces = talloc_array_length(state->ifaces);
+ struct dcesrv_ep_iface *tmp = NULL;
+
+ if (num_ifaces+1 < num_ifaces) {
+ return 1;
+ }
+
+ tmp = talloc_realloc(
+ state->mem_ctx,
+ state->ifaces,
+ struct dcesrv_ep_iface,
+ num_ifaces+1);
+ if (tmp == NULL) {
+ return 1;
+ }
+ state->ifaces = tmp;
+
+ ok = build_ep_list_fill_iface(
+ state->ifaces,
+ &syntax_id,
+ endpoint,
+ name,
+ state->srv_addr,
+ &state->ifaces[num_ifaces]);
+ if (!ok) {
+ state->ifaces = talloc_realloc(
+ state->mem_ctx,
+ state->ifaces,
+ struct dcesrv_ep_iface,
+ num_ifaces);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Build a list of all interfaces handled by all endpoint servers.
+ */
+static uint32_t build_ep_list(TALLOC_CTX *mem_ctx,
+ const struct GUID *uuid,
+ const char *srv_addr,
+ struct dcesrv_ep_iface **peps)
+{
+ struct build_ep_list_state state = {
+ .mem_ctx = mem_ctx, .uuid = uuid, .srv_addr = srv_addr,
+ };
+ int ret;
+
+ ret = tdb_traverse_read(epmdb->tdb, build_ep_list_fn, &state);
+ if (ret == -1) {
+ DBG_DEBUG("tdb_traverse_read failed\n");
+ return 0;
+ }
+
+ *peps = state.ifaces;
+ return talloc_array_length(*peps);
+}
+
+/*
+ * epm_Insert
+ *
+ * Add the specified entries to an endpoint map.
+ */
+error_status_t _epm_Insert(struct pipes_struct *p,
+ struct epm_Insert *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+}
+
+/*
+ * epm_Delete
+ *
+ * Delete the specified entries from an endpoint map.
+ */
+error_status_t _epm_Delete(struct pipes_struct *p,
+ struct epm_Delete *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+}
+
+/*
+ * epm_Lookup
+ *
+ * Lookup entries in an endpoint map.
+ */
+error_status_t _epm_Lookup(struct pipes_struct *p,
+ struct epm_Lookup *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ struct policy_handle *entry_handle;
+ struct rpc_eps *eps;
+ TALLOC_CTX *tmp_ctx;
+ error_status_t rc;
+ uint32_t count = 0;
+ uint32_t num_ents = 0;
+ uint32_t i;
+ bool match = false;
+ bool ok;
+ NTSTATUS status;
+
+ *r->out.num_ents = 0;
+ r->out.entries = NULL;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5, ("_epm_Lookup: Trying to lookup max. %u entries.\n",
+ r->in.max_ents));
+
+ if (r->in.entry_handle == NULL ||
+ ndr_policy_handle_empty(r->in.entry_handle)) {
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ char *srv_addr = NULL;
+
+ DEBUG(7, ("_epm_Lookup: No entry_handle found, creating it.\n"));
+
+ eps = talloc_zero(tmp_ctx, struct rpc_eps);
+ if (eps == NULL) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (local_address != NULL &&
+ tsocket_address_is_inet(local_address, "ipv4"))
+ {
+ srv_addr = tsocket_address_inet_addr_string(
+ local_address, tmp_ctx);
+ }
+
+ switch (r->in.inquiry_type) {
+ case RPC_C_EP_ALL_ELTS:
+ /*
+ * Return all elements from the endpoint map. The
+ * interface_id, vers_option, and object parameters MUST
+ * be ignored.
+ */
+ eps->count = build_ep_list(eps,
+ NULL,
+ srv_addr,
+ &eps->e);
+ break;
+ case RPC_C_EP_MATCH_BY_IF:
+ /*
+ * Return endpoint map elements that contain the
+ * interface identifier specified by the interface_id
+ * and vers_option values.
+ *
+ * RPC_C_EP_MATCH_BY_IF and RPC_C_EP_MATCH_BY_BOTH
+ * need both the same endpoint list. There is a second
+ * check for the inquiry_type below which differentiates
+ * between them.
+ */
+ case RPC_C_EP_MATCH_BY_BOTH:
+ /*
+ * Return endpoint map elements that contain the
+ * interface identifier and object UUID specified by
+ * interface_id, vers_option, and object.
+ */
+ eps->count = build_ep_list(eps,
+ &r->in.interface_id->uuid,
+ srv_addr,
+ &eps->e);
+ break;
+ case RPC_C_EP_MATCH_BY_OBJ:
+ /*
+ * Return endpoint map elements that contain the object
+ * UUID specified by object.
+ */
+ eps->count = build_ep_list(eps,
+ r->in.object,
+ srv_addr,
+ &eps->e);
+ break;
+ default:
+ rc = EPMAPPER_STATUS_CANT_PERFORM_OP;
+ goto done;
+ }
+
+ if (eps->count == 0) {
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ ok = create_policy_hnd(p, r->out.entry_handle, HTYPE_LOOKUP, eps);
+ if (!ok) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ eps = find_policy_by_hnd(p,
+ r->out.entry_handle,
+ HTYPE_LOOKUP,
+ struct rpc_eps,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+ entry_handle = r->out.entry_handle;
+ } else {
+ DEBUG(7, ("_epm_Lookup: Trying to find entry_handle.\n"));
+
+ eps = find_policy_by_hnd(p,
+ r->in.entry_handle,
+ HTYPE_LOOKUP,
+ struct rpc_eps,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+ entry_handle = r->in.entry_handle;
+ }
+
+ if (eps == NULL || eps->e == NULL) {
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ /* return the next N elements */
+ count = r->in.max_ents;
+ if (count > eps->count) {
+ count = eps->count;
+ }
+
+ DEBUG(5, ("_epm_Lookup: Find %u entries\n", count));
+
+ if (count == 0) {
+ close_policy_hnd(p, entry_handle);
+ ZERO_STRUCTP(r->out.entry_handle);
+
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ r->out.entries = talloc_array(p->mem_ctx, struct epm_entry_t, count);
+ if (r->out.entries == NULL) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ match = false;
+
+ switch (r->in.inquiry_type) {
+ case RPC_C_EP_ALL_ELTS:
+ /*
+ * Return all elements from the endpoint map. The
+ * interface_id, vers_option, and object parameters MUST
+ * be ignored.
+ */
+ match = true;
+ break;
+ case RPC_C_EP_MATCH_BY_IF:
+ /*
+ * Return endpoint map elements that contain the
+ * interface identifier specified by the interface_id
+ * and vers_option values.
+ */
+ if (GUID_equal(&r->in.interface_id->uuid,
+ &eps->e[i].syntax_id.uuid)) {
+ match = true;
+ }
+ break;
+ case RPC_C_EP_MATCH_BY_OBJ:
+ /*
+ * Return endpoint map elements that contain the object
+ * UUID specified by object.
+ */
+ if (GUID_equal(r->in.object,
+ &eps->e[i].syntax_id.uuid)) {
+ match = true;
+ }
+ break;
+ case RPC_C_EP_MATCH_BY_BOTH:
+ /*
+ * Return endpoint map elements that contain the
+ * interface identifier and object UUID specified by
+ * interface_id, vers_option, and object.
+ */
+ if (GUID_equal(&r->in.interface_id->uuid,
+ &eps->e[i].syntax_id.uuid) &&
+ GUID_equal(r->in.object, &eps->e[i].syntax_id.uuid)) {
+ match = true;
+ }
+ break;
+ default:
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+ }
+
+ if (match) {
+ if (r->in.inquiry_type == RPC_C_EP_MATCH_BY_IF ||
+ r->in.inquiry_type == RPC_C_EP_MATCH_BY_OBJ) {
+ /* Check interface version */
+
+ match = false;
+ switch (r->in.vers_option) {
+ case RPC_C_VERS_ALL:
+ /*
+ * Return endpoint map elements that
+ * contain the specified interface UUID,
+ * regardless of the version numbers.
+ */
+ match = true;
+ break;
+ case RPC_C_VERS_COMPATIBLE:
+ /*
+ * Return the endpoint map elements that
+ * contain the same major versions of
+ * the specified interface UUID and a
+ * minor version greater than or equal
+ * to the minor version of the specified
+ * UUID.
+ */
+ if (r->in.interface_id->vers_major ==
+ (eps->e[i].syntax_id.if_version >> 16) &&
+ r->in.interface_id->vers_minor <=
+ (eps->e[i].syntax_id.if_version & 0xFFFF)) {
+ match = true;
+ }
+ break;
+ case RPC_C_VERS_EXACT:
+ /*
+ * Return endpoint map elements that
+ * contain the specified version of the
+ * specified interface UUID.
+ */
+ if (r->in.interface_id->vers_major ==
+ (eps->e[i].syntax_id.if_version >> 16) &&
+ r->in.interface_id->vers_minor ==
+ (eps->e[i].syntax_id.if_version & 0xFFFF)) {
+ match = true;
+ }
+ match = true;
+ break;
+ case RPC_C_VERS_MAJOR_ONLY:
+ /*
+ * Return endpoint map elements that
+ * contain the same version of the
+ * specified interface UUID and ignore
+ * the minor version.
+ */
+ if (r->in.interface_id->vers_major ==
+ (eps->e[i].syntax_id.if_version >> 16)) {
+ match = true;
+ }
+ match = true;
+ break;
+ case RPC_C_VERS_UPTO:
+ /*
+ * Return endpoint map elements that
+ * contain a version of the specified
+ * interface UUID less than or equal to
+ * the specified major and minor
+ * version.
+ */
+ if (r->in.interface_id->vers_major >
+ eps->e[i].syntax_id.if_version >> 16) {
+ match = true;
+ } else {
+ if (r->in.interface_id->vers_major ==
+ (eps->e[i].syntax_id.if_version >> 16) &&
+ r->in.interface_id->vers_minor >=
+ (eps->e[i].syntax_id.if_version & 0xFFFF)) {
+ match = true;
+ }
+ }
+ break;
+ default:
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+ }
+ }
+ }
+
+ if (match) {
+ ZERO_STRUCT(r->out.entries[num_ents].object);
+
+ DEBUG(10, ("_epm_Lookup: Adding tower for '%s'\n",
+ eps->e[i].name));
+ r->out.entries[num_ents].annotation = talloc_strdup(r->out.entries,
+ eps->e[i].name);
+ r->out.entries[num_ents].tower = talloc(r->out.entries,
+ struct epm_twr_t);
+ if (r->out.entries[num_ents].tower == NULL) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+ r->out.entries[num_ents].tower->tower.floors = talloc_move(r->out.entries[num_ents].tower, &eps->e[i].ep.floors);
+ r->out.entries[num_ents].tower->tower.num_floors = eps->e[i].ep.num_floors;
+ r->out.entries[num_ents].tower->tower_length = 0;
+
+ num_ents++;
+ }
+ } /* end for loop */
+
+ *r->out.num_ents = num_ents;
+
+ eps->count -= count;
+ eps->e += count;
+ if (eps->count == 0) {
+ close_policy_hnd(p, entry_handle);
+ ZERO_STRUCTP(r->out.entry_handle);
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ rc = EPMAPPER_STATUS_OK;
+done:
+ talloc_free(tmp_ctx);
+
+ return rc;
+}
+
+static struct rpc_eps *epm_map_get_towers(
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *iface,
+ enum dcerpc_transport_t transport,
+ const char *local_address)
+{
+ struct ndr_syntax_id_buf idbuf;
+ char *iface_string = ndr_syntax_id_buf_string(iface, &idbuf);
+ struct rpc_eps *eps = NULL;
+ uint8_t *buf = NULL;
+ size_t buflen;
+ char *bindings = NULL;
+ char *binding = NULL;
+ char *name = NULL;
+ NTSTATUS status;
+ int ret;
+
+ DBG_DEBUG("Mapping interface %s\n", iface_string);
+
+ eps = talloc_zero(mem_ctx, struct rpc_eps);
+ if (eps == NULL) {
+ goto fail;
+ }
+
+ ret = tdb_fetch_talloc(
+ epmdb->tdb, string_term_tdb_data(iface_string), eps, &buf);
+ if (ret != 0) {
+ DBG_DEBUG("Could not find epm entry for %s: %s\n",
+ iface_string,
+ strerror(ret));
+ goto fail;
+ }
+ buflen = talloc_array_length(buf);
+
+ if ((buflen < 1) || (buf[buflen-1] != '\0')) {
+ DBG_DEBUG("epm entry for %s invalid\n", iface_string);
+ goto fail;
+ }
+ bindings = (char *)buf;
+
+ name = bindings; /* name comes first */
+ binding = name; /* strv_next will skip name */
+
+ while ((binding = strv_next(bindings, binding)) != NULL) {
+ struct dcerpc_binding *b = NULL;
+ enum dcerpc_transport_t found_transport;
+ struct dcesrv_ep_iface *tmp = NULL, *new_ep = NULL;
+
+ DBG_DEBUG("Found %s for %s\n", binding, name);
+
+ status = dcerpc_parse_binding(mem_ctx, binding, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_parse_binding() for %s failed: %s\n",
+ binding,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ found_transport = dcerpc_binding_get_transport(b);
+ if (found_transport != transport) {
+ DBG_DEBUG("Transport %d does not match %d\n",
+ (int)found_transport,
+ (int)transport);
+ TALLOC_FREE(b);
+ continue;
+ }
+
+ if (found_transport == NCACN_IP_TCP) {
+ status = dcerpc_binding_set_string_option(
+ b, "host", local_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not set host: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ status = dcerpc_binding_set_abstract_syntax(b, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not set abstract syntax: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ tmp = talloc_realloc(
+ eps,
+ eps->e,
+ struct dcesrv_ep_iface,
+ eps->count+1);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ eps->e = tmp;
+
+ new_ep = &eps->e[eps->count];
+
+ new_ep->name = talloc_strdup(eps->e, name);
+ if (new_ep->name == NULL) {
+ goto fail;
+ }
+ new_ep->syntax_id = *iface;
+
+ status = dcerpc_binding_build_tower(eps->e, b, &new_ep->ep);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_build_tower failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ eps->count += 1;
+
+ TALLOC_FREE(b);
+ }
+ return eps;
+
+fail:
+ TALLOC_FREE(eps);
+ return NULL;
+}
+
+/*
+ * epm_Map
+ *
+ * Apply some algorithm (using the fields in the map_tower) to an endpoint map
+ * to produce a list of protocol towers.
+ */
+error_status_t _epm_Map(struct pipes_struct *p,
+ struct epm_Map *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ struct policy_handle *entry_handle;
+ enum dcerpc_transport_t transport;
+ struct ndr_syntax_id ifid;
+ struct epm_floor *floors;
+ struct rpc_eps *eps;
+ TALLOC_CTX *tmp_ctx;
+ error_status_t rc;
+ uint32_t count = 0;
+ uint32_t num_towers = 0;
+ uint32_t i;
+ bool ok;
+ NTSTATUS status;
+
+ *r->out.num_towers = 0;
+ r->out.towers = NULL;
+
+ if (r->in.map_tower == NULL || r->in.max_towers == 0 ||
+ r->in.map_tower->tower.num_floors < 3) {
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ }
+
+ tmp_ctx = talloc_stackframe();
+
+ ZERO_STRUCTP(r->out.entry_handle);
+
+ DEBUG(5, ("_epm_Map: Trying to map max. %u towers.\n",
+ r->in.max_towers));
+
+ /*
+ * A tower has normally up to 6 floors
+ *
+ * +-----------------------------------------------------------------+
+ * | Floor 1 | Provides the RPC interface identifier. (e.g. UUID for |
+ * | | netlogon) |
+ * +---------+-------------------------------------------------------+
+ * | Floor 2 | Transfer syntax (NDR encoded) |
+ * +---------+-------------------------------------------------------+
+ * | Floor 3 | RPC protocol identifier (ncacn_tcp_ip, ncacn_np, ...) |
+ * +---------+-------------------------------------------------------+
+ * | Floor 4 | Port address (e.g. TCP Port: 49156) |
+ * +---------+-------------------------------------------------------+
+ * | Floor 5 | Transport (e.g. IP:192.168.51.10) |
+ * +---------+-------------------------------------------------------+
+ * | Floor 6 | Routing |
+ * +---------+-------------------------------------------------------+
+ */
+ floors = r->in.map_tower->tower.floors;
+
+ /* We accept NDR as the transfer syntax */
+ status = dcerpc_floor_get_uuid_full(&floors[1], &ifid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_floor_get_uuid_full() failed: %s\n",
+ nt_errstr(status));
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ if (floors[1].lhs.protocol != EPM_PROTOCOL_UUID ||
+ !ndr_syntax_id_equal(&ifid, &ndr_transfer_syntax_ndr)) {
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ /* We only talk to sane transports */
+ transport = dcerpc_transport_by_tower(&r->in.map_tower->tower);
+ if (transport == NCA_UNKNOWN) {
+ DEBUG(2, ("epm_Map: 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"));
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ if (r->in.entry_handle == NULL ||
+ ndr_policy_handle_empty(r->in.entry_handle)) {
+ const struct tsocket_address *local_addr =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ char *local_address = NULL;
+ struct ndr_syntax_id_buf buf;
+ char *if_string = NULL;
+
+ DEBUG(7, ("_epm_Map: No entry_handle found, creating it.\n"));
+
+ status = dcerpc_floor_get_uuid_full(&floors[0], &ifid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_floor_get_uuid_full() failed: %s\n",
+ nt_errstr(status));
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ if_string = ndr_syntax_id_buf_string(&ifid, &buf);
+
+ DBG_INFO("Mapping interface %s\n", if_string);
+
+ if ((transport == NCACN_IP_TCP) &&
+ tsocket_address_is_inet(local_addr, "ip")) {
+ /*
+ * We don't have the host ip in the epm
+ * database. For NCACN_IP_TCP, add the IP that
+ * the client connected to.
+ */
+ local_address = tsocket_address_inet_addr_string(
+ local_addr, tmp_ctx);
+ }
+
+ eps = epm_map_get_towers(
+ tmp_ctx, &ifid, transport, local_address);
+ if (eps == NULL) {
+ DBG_DEBUG("No bindings found\n");
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ ok = create_policy_hnd(p, r->out.entry_handle, HTYPE_LOOKUP, eps);
+ if (!ok) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ eps = find_policy_by_hnd(p,
+ r->out.entry_handle,
+ HTYPE_LOOKUP,
+ struct rpc_eps,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+ entry_handle = r->out.entry_handle;
+ } else {
+ DEBUG(7, ("_epm_Map: Trying to find entry_handle.\n"));
+
+ eps = find_policy_by_hnd(p,
+ r->in.entry_handle,
+ HTYPE_LOOKUP,
+ struct rpc_eps,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+ entry_handle = r->in.entry_handle;
+ }
+
+ if (eps == NULL || eps->e == NULL) {
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ /* return the next N elements */
+ count = r->in.max_towers;
+ if (count > eps->count) {
+ count = eps->count;
+ }
+
+ if (count == 0) {
+ close_policy_hnd(p, entry_handle);
+ ZERO_STRUCTP(r->out.entry_handle);
+
+ rc = EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ goto done;
+ }
+
+ r->out.towers = talloc_array(p->mem_ctx, struct epm_twr_p_t, count);
+ if (r->out.towers == NULL) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ DEBUG(7, ("_epm_Map: Map tower for '%s'\n",
+ eps->e[i].name));
+
+ r->out.towers[num_towers].twr = talloc(r->out.towers,
+ struct epm_twr_t);
+ if (r->out.towers[num_towers].twr == NULL) {
+ rc = EPMAPPER_STATUS_NO_MEMORY;
+ goto done;
+ }
+ r->out.towers[num_towers].twr->tower.floors = talloc_move(r->out.towers[num_towers].twr, &eps->e[i].ep.floors);
+ r->out.towers[num_towers].twr->tower.num_floors = eps->e[i].ep.num_floors;
+ r->out.towers[num_towers].twr->tower_length = 0;
+
+ num_towers++;
+ }
+
+ *r->out.num_towers = num_towers;
+
+ eps->count -= count;
+ eps->e += count;
+ if (eps->count == 0) {
+ close_policy_hnd(p, entry_handle);
+ ZERO_STRUCTP(r->out.entry_handle);
+ }
+
+ rc = EPMAPPER_STATUS_OK;
+done:
+ talloc_free(tmp_ctx);
+
+ return rc;
+}
+
+/*
+ * epm_LookupHandleFree
+ */
+error_status_t _epm_LookupHandleFree(struct pipes_struct *p,
+ struct epm_LookupHandleFree *r)
+{
+ if (r->in.entry_handle == NULL) {
+ return EPMAPPER_STATUS_OK;
+ }
+
+ if (is_valid_policy_hnd(r->in.entry_handle)) {
+ close_policy_hnd(p, r->in.entry_handle);
+ }
+
+ r->out.entry_handle = r->in.entry_handle;
+
+ return EPMAPPER_STATUS_OK;
+}
+
+
+/*
+ * epm_InqObject
+ *
+ * A client implementation SHOULD NOT call this method. These extensions do not
+ * provide an alternative method.
+ */
+error_status_t _epm_InqObject(struct pipes_struct *p,
+ struct epm_InqObject *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+}
+
+
+/*
+ * epm_MgmtDelete
+ *
+ * A client implementation SHOULD NOT call this method. These extensions do not
+ * provide an alternative method.
+*/
+error_status_t _epm_MgmtDelete(struct pipes_struct *p,
+ struct epm_MgmtDelete *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+}
+
+
+/*
+ epm_MapAuth
+*/
+error_status_t _epm_MapAuth(struct pipes_struct *p,
+ struct epm_MapAuth *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return EPMAPPER_STATUS_CANT_PERFORM_OP;
+}
+
+static NTSTATUS epmapper__op_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+#define DCESRV_INTERFACE_EPMAPPER_SHUTDOWN_SERVER \
+ epmapper_shutdown_server
+
+static NTSTATUS epmapper_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ return epmapper__op_shutdown_server(dce_ctx, ep_server);
+}
+
+static NTSTATUS epmapper__op_init_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+static NTSTATUS epmapper_init_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ char *epmdb_path = NULL;
+ NTSTATUS status;
+
+ epmdb_path = lock_path(dce_ctx, "epmdb.tdb");
+ if (epmdb_path == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ epmdb = tdb_wrap_open(
+ dce_ctx,
+ epmdb_path,
+ 0,
+ TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+ O_RDONLY,
+ 0644);
+ if (epmdb == NULL) {
+ DBG_DEBUG("Could not open epmdb.tdb: %s\n", strerror(errno));
+ return map_nt_error_from_unix(errno);
+ }
+ TALLOC_FREE(epmdb_path);
+
+ status = epmapper__op_init_server(dce_ctx, ep_server);
+ return status;
+}
+
+#define DCESRV_INTERFACE_EPMAPPER_INIT_SERVER epmapper_init_server
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_epmapper_scompat.c"
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/eventlog/srv_eventlog_nt.c b/source3/rpc_server/eventlog/srv_eventlog_nt.c
new file mode 100644
index 0000000..a946c57
--- /dev/null
+++ b/source3/rpc_server/eventlog/srv_eventlog_nt.c
@@ -0,0 +1,1047 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Brian Moran 2005,
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ * Copyright (C) Guenther Deschner 2009.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/passwd.h" /* uid_wrapper */
+#include "ntdomain.h"
+#include "lib/eventlog/eventlog.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "rpc_client/cli_winreg_int.h"
+#include "rpc_client/cli_winreg.h"
+#include "smbd/smbd.h"
+#include "auth.h"
+#include "util_tdb.h"
+
+#include "rpc_server/rpc_server.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_eventlog_scompat.h"
+#include "rpc_server/eventlog/srv_eventlog_reg.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define TOP_LEVEL_EVENTLOG_KEY "SYSTEM\\CurrentControlSet\\Services\\Eventlog"
+
+typedef struct {
+ char *logname;
+ ELOG_TDB *etdb;
+ uint32_t current_record;
+ uint32_t num_records;
+ uint32_t oldest_entry;
+ uint32_t flags;
+ uint32_t access_granted;
+} EVENTLOG_INFO;
+
+/********************************************************************
+ ********************************************************************/
+
+static int eventlog_info_destructor(EVENTLOG_INFO *elog)
+{
+ if (elog->etdb) {
+ elog_close_tdb(elog->etdb, false);
+ }
+ return 0;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static EVENTLOG_INFO *find_eventlog_info_by_hnd( struct pipes_struct * p,
+ struct policy_handle * handle )
+{
+ EVENTLOG_INFO *info;
+ NTSTATUS status;
+
+ info = find_policy_by_hnd(p,
+ handle,
+ DCESRV_HANDLE_ANY,
+ EVENTLOG_INFO,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG( 2,
+ ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
+ return NULL;
+ }
+
+ return info;
+}
+
+/********************************************************************
+ Pull the NT ACL from a file on disk or the OpenEventlog() access
+ check. Caller is responsible for freeing the returned security
+ descriptor via TALLOC_FREE(). This is designed for dealing with
+ user space access checks in smbd outside of the VFS. For example,
+ checking access rights in OpenEventlog() or from python.
+
+********************************************************************/
+
+static NTSTATUS get_nt_acl_no_snum(TALLOC_CTX *ctx,
+ struct auth_session_info *session_info,
+ const char *fname,
+ uint32_t security_info_wanted,
+ struct security_descriptor **sd)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct conn_struct_tos *c = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ struct smb_filename *pathref_fname = NULL;
+
+ if (!posix_locking_init(false)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = create_conn_struct_tos(global_messaging_context(),
+ -1,
+ "/",
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("create_conn_struct_tos() returned %s.\n",
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = synthetic_pathref(talloc_tos(),
+ c->conn->cwd_fsp,
+ fname,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ &pathref_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("synthetic_pathref for file %s returned %s.\n",
+ fname, nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+ status = SMB_VFS_FGET_NT_ACL(pathref_fname->fsp,
+ security_info_wanted,
+ ctx,
+ sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_FGET_NT_ACL for file %s returned %s.\n",
+ fname, nt_errstr(status));
+ }
+
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool elog_check_access(EVENTLOG_INFO *info,
+ struct auth_session_info *session_info)
+{
+ const struct security_token *token = session_info->security_token;
+ char *tdbname = elog_tdbname(talloc_tos(), info->logname );
+ struct security_descriptor *sec_desc;
+ struct security_ace *ace;
+ NTSTATUS status;
+
+ if ( !tdbname )
+ return False;
+
+ /* get the security descriptor for the file */
+
+ status = get_nt_acl_no_snum( info,
+ session_info,
+ tdbname,
+ SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL,
+ &sec_desc);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("elog_check_access: Unable to get NT ACL for %s: %s\n",
+ tdbname, nt_errstr(status)));
+ TALLOC_FREE(tdbname);
+ return False;
+ }
+ TALLOC_FREE(tdbname);
+
+ ace = talloc_zero(sec_desc, struct security_ace);
+ if (ace == NULL) {
+ TALLOC_FREE(sec_desc);
+ return false;
+ }
+
+ ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace->flags = 0;
+ ace->access_mask = REG_KEY_ALL;
+ ace->trustee = global_sid_System;
+
+ status = security_descriptor_dacl_add(sec_desc, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(sec_desc);
+ return false;
+ }
+
+ /* root free pass */
+
+ if ( geteuid() == sec_initial_uid() ) {
+ DEBUG(5,("elog_check_access: running as root, using system token\n"));
+ token = get_system_token();
+ }
+
+ /* run the check, try for the max allowed */
+
+ status = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
+ &info->access_granted);
+
+ TALLOC_FREE(sec_desc);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(8,("elog_check_access: se_access_check() return %s\n",
+ nt_errstr(status)));
+ return False;
+ }
+
+ /* we have to have READ permission for a successful open */
+
+ return ( info->access_granted & SEC_FILE_READ_DATA );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static bool elog_validate_logname( const char *name )
+{
+ int i;
+ const char **elogs = lp_eventlog_list();
+
+ if (!elogs) {
+ return False;
+ }
+
+ for ( i=0; elogs[i]; i++ ) {
+ if ( strequal( name, elogs[i] ) )
+ return True;
+ }
+
+ return False;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool get_num_records_hook( EVENTLOG_INFO * info )
+{
+ int next_record;
+ int oldest_record;
+
+ if ( !info->etdb ) {
+ DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
+ return False;
+ }
+
+ /* lock the tdb since we have to get 2 records */
+
+ tdb_lock_bystring_with_timeout( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD, 1 );
+ next_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
+ oldest_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_OLDEST_ENTRY);
+ tdb_unlock_bystring( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
+
+ DEBUG( 8,
+ ( "Oldest Record %d; Next Record %d\n", oldest_record,
+ next_record ) );
+
+ info->num_records = ( next_record - oldest_record );
+ info->oldest_entry = oldest_record;
+
+ return True;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static bool get_oldest_entry_hook( EVENTLOG_INFO * info )
+{
+ /* it's the same thing */
+ return get_num_records_hook( info );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static NTSTATUS elog_open( struct pipes_struct * p, const char *logname, struct policy_handle *hnd )
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ EVENTLOG_INFO *elog;
+
+ /* first thing is to validate the eventlog name */
+
+ if ( !elog_validate_logname( logname ) )
+ return NT_STATUS_OBJECT_PATH_INVALID;
+
+ if ( !(elog = talloc_zero( NULL, EVENTLOG_INFO )) )
+ return NT_STATUS_NO_MEMORY;
+ talloc_set_destructor(elog, eventlog_info_destructor);
+
+ elog->logname = talloc_strdup( elog, logname );
+
+ /* Open the tdb first (so that we can create any new tdbs if necessary).
+ We have to do this as root and then use an internal access check
+ on the file permissions since you can only have a tdb open once
+ in a single process */
+
+ become_root();
+ elog->etdb = elog_open_tdb( elog->logname, False, False );
+ unbecome_root();
+
+ if ( !elog->etdb ) {
+ /* according to MSDN, if the logfile cannot be found, we should
+ default to the "Application" log */
+
+ if ( !strequal( logname, ELOG_APPL ) ) {
+
+ TALLOC_FREE( elog->logname );
+
+ elog->logname = talloc_strdup( elog, ELOG_APPL );
+
+ /* do the access check */
+ if ( !elog_check_access( elog, session_info) ) {
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ become_root();
+ elog->etdb = elog_open_tdb( elog->logname, False, False );
+ unbecome_root();
+ }
+
+ if ( !elog->etdb ) {
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED; /* ??? */
+ }
+ }
+
+ /* now do the access check. Close the tdb if we fail here */
+
+ if ( !elog_check_access( elog, session_info) ) {
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* create the policy handle */
+
+ if ( !create_policy_hnd( p, hnd, 0, elog ) ) {
+ TALLOC_FREE(elog);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set the initial current_record pointer */
+
+ if ( !get_oldest_entry_hook( elog ) ) {
+ DEBUG(3,("elog_open: Successfully opened eventlog but can't "
+ "get any information on internal records!\n"));
+ }
+
+ elog->current_record = elog->oldest_entry;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static NTSTATUS elog_close( struct pipes_struct *p, struct policy_handle *hnd )
+{
+ if ( !( close_policy_hnd( p, hnd ) ) ) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ *******************************************************************/
+
+static int elog_size( EVENTLOG_INFO *info )
+{
+ if ( !info || !info->etdb ) {
+ DEBUG(0,("elog_size: Invalid info* structure!\n"));
+ return 0;
+ }
+
+ return elog_tdb_size( ELOG_TDB_CTX(info->etdb), NULL, NULL );
+}
+
+/********************************************************************
+ note that this can only be called AFTER the table is constructed,
+ since it uses the table to find the tdb handle
+ ********************************************************************/
+
+static bool sync_eventlog_params(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ EVENTLOG_INFO *info)
+{
+ struct dcerpc_binding_handle *h = NULL;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ uint32_t uiMaxSize = 0;
+ uint32_t uiRetention = 0;
+ char *path = NULL;
+ NTSTATUS status;
+ WERROR wresult = WERR_OK;
+ char *elogname = info->logname;
+ TALLOC_CTX *ctx;
+ bool ret = false;
+
+ ctx = talloc_stackframe();
+ if (ctx == NULL) {
+ return false;
+ }
+
+ DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
+
+ if ( !info->etdb ) {
+ DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
+ goto done;
+ }
+ /* set reasonable defaults. 512Kb on size and 1 week on time */
+
+ uiMaxSize = 0x80000;
+ uiRetention = 604800;
+
+ /* the general idea is to internally open the registry
+ key and retrieve the values. That way we can continue
+ to use the same fetch/store api that we use in
+ srv_reg_nt.c */
+ path = talloc_asprintf(ctx, "%s\\%s", TOP_LEVEL_EVENTLOG_KEY, elogname);
+ if (!path) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_int_hklm_openkey(ctx,
+ get_session_info_system(),
+ msg_ctx,
+ &h,
+ path,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd,
+ &wresult);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(4,("sync_eventlog_params: Failed to open key [%s] (%s)\n",
+ path, nt_errstr(status)));
+ goto done;
+ }
+ if ( !W_ERROR_IS_OK( wresult ) ) {
+ DEBUG( 4,
+ ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
+ path, win_errstr( wresult ) ) );
+ goto done;
+ }
+
+ status = dcerpc_winreg_query_dword(ctx,
+ h,
+ &key_hnd,
+ "Retention",
+ &uiRetention,
+ &wresult);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(4, ("Failed to query value \"Retention\": %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(wresult)) {
+ DEBUG(4, ("Failed to query value \"Retention\": %s\n",
+ win_errstr(wresult)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_query_dword(ctx,
+ h,
+ &key_hnd,
+ "MaxSize",
+ &uiMaxSize,
+ &wresult);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(4, ("Failed to query value \"Retention\": %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(wresult)) {
+ DEBUG(4, ("Failed to query value \"MaxSize\": %s\n",
+ win_errstr(wresult)));
+ goto done;
+ }
+
+ tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_MAXSIZE, uiMaxSize );
+ tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_RETENTION, uiRetention );
+
+ ret = true;
+
+done:
+ if (h != NULL) {
+ WERROR ignore;
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, ctx, &key_hnd, &ignore);
+ }
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ dcerpc_winreg_CloseKey(h, ctx, &hive_hnd, &ignore);
+ }
+ }
+
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/********************************************************************
+ _eventlog_OpenEventLogW
+ ********************************************************************/
+
+NTSTATUS _eventlog_OpenEventLogW(struct pipes_struct *p,
+ struct eventlog_OpenEventLogW *r)
+{
+ EVENTLOG_INFO *info;
+ NTSTATUS result;
+
+ DEBUG( 10,("_eventlog_OpenEventLogW: Server [%s], Log [%s]\n",
+ r->in.servername->string, r->in.logname->string ));
+
+ /* according to MSDN, if the logfile cannot be found, we should
+ default to the "Application" log */
+
+ if ( !NT_STATUS_IS_OK( result = elog_open( p, r->in.logname->string, r->out.handle )) )
+ return result;
+
+ if ( !(info = find_eventlog_info_by_hnd( p, r->out.handle )) ) {
+ DEBUG(0,("_eventlog_OpenEventLogW: eventlog (%s) opened but unable to find handle!\n",
+ r->in.logname->string ));
+ elog_close( p, r->out.handle );
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("_eventlog_OpenEventLogW: Size [%d]\n", elog_size( info )));
+
+ if (!sync_eventlog_params(p->mem_ctx,
+ p->msg_ctx,
+ info)) {
+ elog_close(p, r->out.handle);
+ return NT_STATUS_EVENTLOG_FILE_CORRUPT;
+ }
+ prune_eventlog( ELOG_TDB_CTX(info->etdb) );
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ _eventlog_ClearEventLogW
+ This call still needs some work
+ ********************************************************************/
+/** The windows client seems to be doing something funny with the file name
+ A call like
+ ClearEventLog(handle, "backup_file")
+ on the client side will result in the backup file name looking like this on the
+ server side:
+ \??\${CWD of client}\backup_file
+ If an absolute path gets specified, such as
+ ClearEventLog(handle, "C:\\temp\\backup_file")
+ then it is still mangled by the client into this:
+ \??\C:\temp\backup_file
+ when it is on the wire.
+ I'm not sure where the \?? is coming from, or why the ${CWD} of the client process
+ would be added in given that the backup file gets written on the server side. */
+
+NTSTATUS _eventlog_ClearEventLogW(struct pipes_struct *p,
+ struct eventlog_ClearEventLogW *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+
+ if ( !info )
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (r->in.backupfile && r->in.backupfile->string) {
+
+ DEBUG(8,( "_eventlog_ClearEventLogW: Using [%s] as the backup "
+ "file name for log [%s].\n",
+ r->in.backupfile->string, info->logname ) );
+ }
+
+ /* check for WRITE access to the file */
+
+ if ( !(info->access_granted & SEC_FILE_WRITE_DATA) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Force a close and reopen */
+
+ elog_close_tdb( info->etdb, True );
+ become_root();
+ info->etdb = elog_open_tdb( info->logname, True, False );
+ unbecome_root();
+
+ if ( !info->etdb )
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ _eventlog_CloseEventLog
+ ********************************************************************/
+
+NTSTATUS _eventlog_CloseEventLog(struct pipes_struct * p,
+ struct eventlog_CloseEventLog *r)
+{
+ NTSTATUS status;
+
+ status = elog_close( p, r->in.handle );
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ _eventlog_ReadEventLogW
+ ********************************************************************/
+
+NTSTATUS _eventlog_ReadEventLogW(struct pipes_struct *p,
+ struct eventlog_ReadEventLogW *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+ int bytes_left, record_number;
+ uint32_t elog_read_type, elog_read_dir;
+
+ if (!info) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ info->flags = r->in.flags;
+ bytes_left = r->in.number_of_bytes;
+
+ if (!info->etdb) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* check for valid flags. Can't use the sequential and seek flags together */
+
+ elog_read_type = r->in.flags & (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ);
+ elog_read_dir = r->in.flags & (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ);
+
+ if (r->in.flags == 0 ||
+ elog_read_type == (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ) ||
+ elog_read_dir == (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ))
+ {
+ DEBUG(3,("_eventlog_ReadEventLogW: "
+ "Invalid flags [0x%08x] for ReadEventLog\n",
+ r->in.flags));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* a sequential read should ignore the offset */
+
+ if (elog_read_type & EVENTLOG_SEQUENTIAL_READ) {
+ record_number = info->current_record;
+ } else {
+ record_number = r->in.offset;
+ }
+
+ if (r->in.number_of_bytes == 0) {
+ struct EVENTLOGRECORD *e;
+ e = evlog_pull_record(p->mem_ctx, ELOG_TDB_CTX(info->etdb),
+ record_number);
+ if (!e) {
+ return NT_STATUS_END_OF_FILE;
+ }
+ *r->out.real_size = e->Length;
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ while (bytes_left > 0) {
+
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct EVENTLOGRECORD *e;
+
+ e = evlog_pull_record(p->mem_ctx, ELOG_TDB_CTX(info->etdb),
+ record_number);
+ if (!e) {
+ break;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, e,
+ (ndr_push_flags_fn_t)ndr_push_EVENTLOGRECORD);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(EVENTLOGRECORD, e);
+ }
+
+ if (blob.length > r->in.number_of_bytes) {
+ *r->out.real_size = blob.length;
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (*r->out.sent_size + blob.length > r->in.number_of_bytes) {
+ break;
+ }
+
+ bytes_left -= blob.length;
+
+ if (info->flags & EVENTLOG_FORWARDS_READ) {
+ record_number++;
+ } else {
+ record_number--;
+ }
+
+ /* update the eventlog record pointer */
+
+ info->current_record = record_number;
+
+ memcpy(&r->out.data[*(r->out.sent_size)],
+ blob.data, blob.length);
+ *(r->out.sent_size) += blob.length;
+ }
+
+ if (r->in.offset == 0 && record_number == 0 && *r->out.sent_size == 0) {
+ return NT_STATUS_END_OF_FILE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ _eventlog_GetOldestRecord
+ ********************************************************************/
+
+NTSTATUS _eventlog_GetOldestRecord(struct pipes_struct *p,
+ struct eventlog_GetOldestRecord *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+
+ if (info == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if ( !( get_oldest_entry_hook( info ) ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ *r->out.oldest_entry = info->oldest_entry;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+_eventlog_GetNumRecords
+ ********************************************************************/
+
+NTSTATUS _eventlog_GetNumRecords(struct pipes_struct *p,
+ struct eventlog_GetNumRecords *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+
+ if (info == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if ( !( get_num_records_hook( info ) ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ *r->out.number = info->num_records;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _eventlog_BackupEventLogW(struct pipes_struct *p, struct eventlog_BackupEventLogW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/********************************************************************
+_eventlog_GetLogInformation
+ ********************************************************************/
+
+NTSTATUS _eventlog_GetLogInformation(struct pipes_struct *p,
+ struct eventlog_GetLogInformation *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd(p, r->in.handle);
+ struct EVENTLOG_FULL_INFORMATION f;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+
+ if (!info) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (r->in.level != 0) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ *r->out.bytes_needed = 4;
+
+ if (r->in.buf_size < 4) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* FIXME: this should be retrieved from the handle */
+ f.full = false;
+
+ ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &f,
+ (ndr_push_flags_fn_t)ndr_push_EVENTLOG_FULL_INFORMATION);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(EVENTLOG_FULL_INFORMATION, &f);
+ }
+
+ memcpy(r->out.buffer, blob.data, 4);
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+_eventlog_FlushEventLog
+ ********************************************************************/
+
+NTSTATUS _eventlog_FlushEventLog(struct pipes_struct *p,
+ struct eventlog_FlushEventLog *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd(p, r->in.handle);
+ if (!info) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static NTSTATUS evlog_report_to_record(TALLOC_CTX *mem_ctx,
+ const struct eventlog_ReportEventW *r,
+ const char *logname,
+ struct EVENTLOGRECORD *e)
+{
+ uint32_t i;
+ ZERO_STRUCTP(e);
+
+ e->TimeGenerated = r->in.timestamp;
+ e->TimeWritten = time(NULL);
+ e->EventID = r->in.event_id;
+ e->EventType = r->in.event_type;
+ e->NumStrings = r->in.num_of_strings;
+ e->EventCategory = r->in.event_category;
+ e->ReservedFlags = r->in.flags;
+ e->DataLength = r->in.data_size;
+ e->SourceName = talloc_strdup(mem_ctx, logname);
+ NT_STATUS_HAVE_NO_MEMORY(e->SourceName);
+ if (r->in.servername->string) {
+ e->Computername = r->in.servername->string;
+ } else {
+ e->Computername = talloc_strdup(mem_ctx, "");
+ NT_STATUS_HAVE_NO_MEMORY(e->Computername);
+ }
+ if (r->in.user_sid) {
+ e->UserSid = *r->in.user_sid;
+ }
+ e->Strings = talloc_array(mem_ctx, const char *, e->NumStrings);
+ NT_STATUS_HAVE_NO_MEMORY(e->Strings);
+
+ for (i=0; i < e->NumStrings; i++) {
+ e->Strings[i] = talloc_strdup(e->Strings,
+ r->in.strings[i]->string);
+ NT_STATUS_HAVE_NO_MEMORY(e->Strings[i]);
+ }
+ e->Data = r->in.data;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+_eventlog_ReportEventW
+ ********************************************************************/
+
+NTSTATUS _eventlog_ReportEventW(struct pipes_struct *p,
+ struct eventlog_ReportEventW *r)
+{
+ NTSTATUS status;
+ struct EVENTLOGRECORD record;
+
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd(p, r->in.handle);
+ if (!info) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = evlog_report_to_record(p->mem_ctx, r, info->logname, &record);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = evlog_push_record(p->mem_ctx,
+ ELOG_TDB_CTX(info->etdb),
+ &record,
+ r->out.record_number);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+NTSTATUS _eventlog_DeregisterEventSource(struct pipes_struct *p,
+ struct eventlog_DeregisterEventSource *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ChangeNotify(struct pipes_struct *p,
+ struct eventlog_ChangeNotify *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterEventSourceW(struct pipes_struct *p,
+ struct eventlog_RegisterEventSourceW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenBackupEventLogW(struct pipes_struct *p,
+ struct eventlog_OpenBackupEventLogW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ClearEventLogA(struct pipes_struct *p,
+ struct eventlog_ClearEventLogA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_BackupEventLogA(struct pipes_struct *p,
+ struct eventlog_BackupEventLogA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenEventLogA(struct pipes_struct *p,
+ struct eventlog_OpenEventLogA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterEventSourceA(struct pipes_struct *p,
+ struct eventlog_RegisterEventSourceA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenBackupEventLogA(struct pipes_struct *p,
+ struct eventlog_OpenBackupEventLogA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReadEventLogA(struct pipes_struct *p,
+ struct eventlog_ReadEventLogA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReportEventA(struct pipes_struct *p,
+ struct eventlog_ReportEventA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterClusterSvc(struct pipes_struct *p,
+ struct eventlog_RegisterClusterSvc *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_DeregisterClusterSvc(struct pipes_struct *p,
+ struct eventlog_DeregisterClusterSvc *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_WriteClusterEvents(struct pipes_struct *p,
+ struct eventlog_WriteClusterEvents *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReportEventAndSourceW(struct pipes_struct *p,
+ struct eventlog_ReportEventAndSourceW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS eventlog__op_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+#define DCESRV_INTERFACE_EVENTLOG_INIT_SERVER \
+ eventlog_init_server
+
+static NTSTATUS eventlog_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ bool ok;
+
+ ok = eventlog_init_winreg(msg_ctx);
+ if (!ok) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return eventlog__op_init_server(dce_ctx, ep_server);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_eventlog_scompat.c"
diff --git a/source3/rpc_server/eventlog/srv_eventlog_reg.c b/source3/rpc_server/eventlog/srv_eventlog_reg.c
new file mode 100644
index 0000000..513dd0f
--- /dev/null
+++ b/source3/rpc_server/eventlog/srv_eventlog_reg.c
@@ -0,0 +1,267 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Eventlog RPC server keys initialization
+ *
+ * Copyright (c) 2005 Marcin Krzysztof Porwit
+ * Copyright (c) 2005 Brian Moran
+ * Copyright (c) 2005 Gerald (Jerry) Carter
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "rpc_client/cli_winreg_int.h"
+#include "rpc_client/cli_winreg.h"
+#include "rpc_server/eventlog/srv_eventlog_reg.h"
+#include "auth.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+#define TOP_LEVEL_EVENTLOG_KEY "SYSTEM\\CurrentControlSet\\Services\\Eventlog"
+
+bool eventlog_init_winreg(struct messaging_context *msg_ctx)
+{
+ struct dcerpc_binding_handle *h = NULL;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ uint32_t uiMaxSize = 0x00080000;
+ uint32_t uiRetention = 0x93A80;
+ const char **elogs = lp_eventlog_list();
+ const char **subkeys = NULL;
+ uint32_t num_subkeys = 0;
+ uint32_t i;
+ char *key = NULL;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+ bool ok = false;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return false;
+ }
+
+ DEBUG(3, ("Initialise the eventlog registry keys if needed.\n"));
+
+ key = talloc_strdup(tmp_ctx, TOP_LEVEL_EVENTLOG_KEY);
+
+ status = dcerpc_winreg_int_hklm_openkey(tmp_ctx,
+ get_session_info_system(),
+ msg_ctx,
+ &h,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("eventlog_init_winreg: Could not open %s - %s\n",
+ key, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("eventlog_init_winreg: Could not open %s - %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_enum_keys(tmp_ctx,
+ h,
+ &key_hnd,
+ &num_subkeys,
+ &subkeys,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("eventlog_init_winreg: Could enum keys at %s - %s\n",
+ key, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("eventlog_init_winreg: Could enum keys at %s - %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
+ }
+
+ /* create subkeys if they don't exist */
+ while (elogs && *elogs) {
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+ char *evt_tdb = NULL;
+ struct winreg_String wkey;
+ struct winreg_String wkeyclass;
+ bool skip = false;
+
+ for (i = 0; i < num_subkeys; i++) {
+ if (strequal(subkeys[i], *elogs)) {
+ skip = true;
+ }
+ }
+
+ if (skip) {
+ elogs++;
+ continue;
+ }
+
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(wkey);
+
+ wkey.name = talloc_asprintf(tmp_ctx, "%s\\%s", key, *elogs);
+ if (wkey.name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+
+ status = dcerpc_winreg_CreateKey(h,
+ tmp_ctx,
+ &hive_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ &key_hnd,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("eventlog_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("eventlog_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ h,
+ &key_hnd,
+ "MaxSize",
+ uiMaxSize,
+ &result);
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ h,
+ &key_hnd,
+ "Retention",
+ uiRetention,
+ &result);
+
+ status = dcerpc_winreg_set_sz(tmp_ctx,
+ h,
+ &key_hnd,
+ "PrimaryModule",
+ *elogs,
+ &result);
+
+ evt_tdb = talloc_asprintf(tmp_ctx,
+ "%%SystemRoot%%\\system32\\config\\%s.tdb",
+ *elogs);
+ if (evt_tdb == NULL) {
+ goto done;
+ }
+ status = dcerpc_winreg_set_expand_sz(tmp_ctx,
+ h,
+ &key_hnd,
+ "File",
+ evt_tdb,
+ &result);
+ TALLOC_FREE(evt_tdb);
+
+ status = dcerpc_winreg_add_multi_sz(tmp_ctx,
+ h,
+ &key_hnd,
+ "Sources",
+ *elogs,
+ &result);
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
+ }
+
+ /* sub-subkeys */
+ {
+ uint32_t uiCategoryCount = 0x00000007;
+
+ wkey.name = talloc_asprintf(tmp_ctx,
+ "%s\\%s",
+ wkey.name, *elogs);
+ if (wkey.name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dcerpc_winreg_CreateKey(h,
+ tmp_ctx,
+ &hive_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ &key_hnd,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("eventlog_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("eventlog_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(tmp_ctx,
+ h,
+ &key_hnd,
+ "CategoryCount",
+ uiCategoryCount,
+ &result);
+
+ status = dcerpc_winreg_set_expand_sz(tmp_ctx,
+ h,
+ &key_hnd,
+ "CategoryMessageFile",
+ "%SystemRoot%\\system32\\eventlog.dll",
+ &result);
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
+ }
+ }
+
+ elogs++;
+ } /* loop */
+
+ ok = true;
+done:
+ TALLOC_FREE(tmp_ctx);
+ return ok;
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/eventlog/srv_eventlog_reg.h b/source3/rpc_server/eventlog/srv_eventlog_reg.h
new file mode 100644
index 0000000..02c2792
--- /dev/null
+++ b/source3/rpc_server/eventlog/srv_eventlog_reg.h
@@ -0,0 +1,29 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * WINREG client routines
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SRV_EVENTLOG_REG_H
+#define SRV_EVENTLOG_REG_H
+
+bool eventlog_init_winreg(struct messaging_context *msg_ctx);
+
+#endif /* SRV_EVENTLOG_REG_H */
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/fss/srv_fss_agent.c b/source3/rpc_server/fss/srv_fss_agent.c
new file mode 100644
index 0000000..4de600f
--- /dev/null
+++ b/source3/rpc_server/fss/srv_fss_agent.c
@@ -0,0 +1,1776 @@
+/*
+ * File Server Remote VSS Protocol (FSRVP) server
+ *
+ * Copyright (C) David Disseldorp 2012-2015
+ *
+ * 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 "ntdomain.h"
+#include "include/messages.h"
+#include "serverid.h"
+#include "include/auth.h"
+#include "../libcli/security/security.h"
+#include "../libcli/util/hresult.h"
+#include "../lib/smbconf/smbconf.h"
+#include "smbd/proto.h"
+#include "lib/smbconf/smbconf_init.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_fsrvp_scompat.h"
+#include "librpc/gen_ndr/ndr_fsrvp.h"
+#include "rpc_server/rpc_server.h"
+#include "srv_fss_private.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static struct fss_global fss_global;
+
+/* errmap NTSTATUS->fsrvp */
+static const struct {
+ NTSTATUS status;
+ uint32_t fsrvp_err;
+} ntstatus_to_fsrvp_map[] = {
+ {NT_STATUS_INVALID_SERVER_STATE, FSRVP_E_BAD_STATE},
+ {NT_STATUS_INVALID_DISPOSITION, FSRVP_E_SHADOW_COPY_SET_IN_PROGRESS},
+ {NT_STATUS_NOT_SUPPORTED, FSRVP_E_NOT_SUPPORTED},
+ {NT_STATUS_IO_TIMEOUT, FSRVP_E_WAIT_TIMEOUT},
+ {NT_STATUS_CANT_WAIT, FSRVP_E_WAIT_FAILED},
+ {NT_STATUS_OBJECTID_EXISTS, FSRVP_E_OBJECT_ALREADY_EXISTS},
+ {NT_STATUS_OBJECTID_NOT_FOUND, FSRVP_E_OBJECT_NOT_FOUND},
+ {NT_STATUS_OBJECT_NAME_INVALID, FSRVP_E_BAD_ID},
+};
+
+/* errmap NTSTATUS->hresult */
+static const struct {
+ NTSTATUS status;
+ HRESULT hres;
+} ntstatus_to_hres_map[] = {
+ {NT_STATUS_ACCESS_DENIED, HRES_E_ACCESSDENIED},
+ {NT_STATUS_INVALID_PARAMETER, HRES_E_INVALIDARG},
+ {NT_STATUS_NO_MEMORY, HRES_E_OUTOFMEMORY},
+};
+
+static uint32_t fss_ntstatus_map(NTSTATUS status)
+{
+ size_t i;
+
+ if (NT_STATUS_IS_OK(status))
+ return 0;
+
+ /* check fsrvp specific errors first */
+ for (i = 0; i < ARRAY_SIZE(ntstatus_to_fsrvp_map); i++) {
+ if (NT_STATUS_EQUAL(status, ntstatus_to_fsrvp_map[i].status)) {
+ return ntstatus_to_fsrvp_map[i].fsrvp_err;
+ }
+ }
+ /* fall-back to generic hresult values */
+ for (i = 0; i < ARRAY_SIZE(ntstatus_to_hres_map); i++) {
+ if (NT_STATUS_EQUAL(status, ntstatus_to_hres_map[i].status)) {
+ return HRES_ERROR_V(ntstatus_to_hres_map[i].hres);
+ }
+ }
+
+ return HRES_ERROR_V(HRES_E_FAIL);
+}
+
+static NTSTATUS fss_unc_parse(TALLOC_CTX *mem_ctx,
+ const char *unc,
+ char **_server,
+ char **_share)
+{
+ char *s;
+ char *server;
+ char *share;
+
+ if (unc == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ s = strstr_m(unc, "\\\\");
+ if (s == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ server = talloc_strdup(mem_ctx, s + 2);
+ if (server == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ s = strchr_m(server, '\\');
+ if ((s == NULL) || (s == server)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *s = '\0';
+ share = s + 1;
+
+ s = strchr_m(share, '\\');
+ if (s != NULL) {
+ /* diskshadow.exe adds a trailing '\' to the share-name */
+ *s = '\0';
+ }
+ if (strlen(share) == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (_server != NULL) {
+ *_server = server;
+ }
+ if (_share != NULL) {
+ *_share = share;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_conn_create_tos(struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ int snum,
+ struct connection_struct **conn_out);
+
+/* test if system path exists */
+static bool snap_path_exists(TALLOC_CTX *ctx, struct messaging_context *msg_ctx,
+ struct fss_sc *sc)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ SMB_STRUCT_STAT st;
+ struct connection_struct *conn = NULL;
+ struct smb_filename *smb_fname = NULL;
+ char *service = NULL;
+ char *share;
+ int snum;
+ int ret;
+ NTSTATUS status;
+ bool result = false;
+
+ ZERO_STRUCT(st);
+
+ if ((sc->smaps_count == 0) || (sc->sc_path == NULL)) {
+ goto out;
+ }
+
+ share = sc->smaps->share_name;
+ snum = find_service(frame, share, &service);
+
+ if ((snum == -1) || (service == NULL)) {
+ goto out;
+ }
+
+ status = fss_conn_create_tos(msg_ctx, NULL, snum, &conn);
+ if(!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ smb_fname = synthetic_smb_fname(service,
+ sc->sc_path,
+ NULL,
+ NULL,
+ 0,
+ 0);
+ if (smb_fname == NULL) {
+ goto out;
+ }
+
+ ret = SMB_VFS_STAT(conn, smb_fname);
+ if ((ret == -1) && (errno == ENOENT)) {
+ goto out;
+ }
+ result = true;
+out:
+ TALLOC_FREE(frame);
+ return result;
+}
+
+static NTSTATUS sc_smap_unexpose(struct messaging_context *msg_ctx,
+ struct fss_sc_smap *sc_smap, bool delete_all);
+
+static NTSTATUS fss_prune_stale(struct messaging_context *msg_ctx,
+ const char *db_path)
+{
+ struct fss_sc_set *sc_sets;
+ uint32_t sc_sets_count = 0;
+ struct fss_sc_set *sc_set;
+ struct fss_sc_smap *prunable_sc_smaps = NULL;
+ bool is_modified = false;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ if (!ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* work with temporary state for simple cleanup on failure */
+ become_root();
+ status = fss_state_retrieve(ctx, &sc_sets, &sc_sets_count, db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to retrieve fss server state: %s\n",
+ nt_errstr(status)));
+ goto out;
+ }
+
+ /* walk the cache and pick up any entries to be deleted */
+ sc_set = sc_sets;
+ DEBUG(10, ("pruning shared shadow copies\n"));
+ while (sc_set) {
+ struct fss_sc *sc;
+ struct fss_sc_set *sc_set_next = sc_set->next;
+ char *set_id = GUID_string(ctx, &sc_set->id);
+ if (set_id == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ DEBUGADD(10, ("\tprocessing shadow set id %s\n", set_id));
+ sc = sc_set->scs;
+ while (sc) {
+ struct fss_sc_smap *sc_smap;
+ struct fss_sc *sc_next = sc->next;
+ DEBUGADD(10, ("\tprocessing shadow copy path %s\n",
+ sc->sc_path));
+ if (snap_path_exists(ctx, msg_ctx, sc)) {
+ sc = sc_next;
+ continue;
+ }
+
+ /* move missing snapshot state to purge list */
+ sc_smap = sc->smaps;
+ while (sc_smap != NULL) {
+ struct fss_sc_smap *smap_next = sc_smap->next;
+ DLIST_REMOVE(sc->smaps, sc_smap);
+ DLIST_ADD_END(prunable_sc_smaps, sc_smap);
+ sc->smaps_count--;
+ sc_smap = smap_next;
+ }
+
+ DLIST_REMOVE(sc_set->scs, sc);
+ sc_set->scs_count--;
+ is_modified = true;
+ sc = sc_next;
+ }
+ if (sc_set->scs_count == 0) {
+ DLIST_REMOVE(sc_sets, sc_set);
+ sc_sets_count--;
+ }
+ sc_set = sc_set_next;
+ }
+
+ if (is_modified) {
+ /* unexpose all shares in a single transaction */
+ status = sc_smap_unexpose(msg_ctx, prunable_sc_smaps, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* exit without storing updated state */
+ goto out;
+ }
+
+ become_root();
+ status = fss_state_store(ctx, sc_sets, sc_sets_count, db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("pruning failed to store fss server state: %s\n",
+ nt_errstr(status)));
+ goto out;
+ }
+ }
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(ctx);
+ return status;
+}
+
+static NTSTATUS fss_conn_create_tos(struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ int snum,
+ struct connection_struct **conn_out)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct conn_struct_tos *c = NULL;
+ NTSTATUS status;
+
+ status = create_conn_struct_tos(msg_ctx,
+ snum,
+ lp_path(talloc_tos(), lp_sub, snum),
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("failed to create conn for vfs: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = set_conn_force_user_group(c->conn, snum);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("failed set force user / group\n"));
+ TALLOC_FREE(c);
+ return status;
+ }
+
+ *conn_out = c->conn;
+ return NT_STATUS_OK;
+}
+
+static struct fss_sc_set *sc_set_lookup(struct fss_sc_set *sc_set_head,
+ struct GUID *sc_set_id)
+{
+
+ struct fss_sc_set *sc_set;
+ char *guid_str;
+
+ for (sc_set = sc_set_head; sc_set; sc_set = sc_set->next) {
+ if (GUID_equal(&sc_set->id, sc_set_id)) {
+ return sc_set;
+ }
+ }
+ guid_str = GUID_string(sc_set_head, sc_set_id);
+ DEBUG(4, ("shadow copy set with GUID %s not found\n",
+ guid_str ? guid_str : "NO MEM"));
+ talloc_free(guid_str);
+
+ return NULL;
+}
+
+static struct fss_sc *sc_lookup(struct fss_sc *sc_head, struct GUID *sc_id)
+{
+
+ struct fss_sc *sc;
+ char *guid_str;
+
+ for (sc = sc_head; sc; sc = sc->next) {
+ if (GUID_equal(&sc->id, sc_id)) {
+ return sc;
+ }
+ }
+ guid_str = GUID_string(sc_head, sc_id);
+ DEBUG(4, ("shadow copy with GUID %s not found\n",
+ guid_str ? guid_str : "NO MEM"));
+ talloc_free(guid_str);
+
+ return NULL;
+}
+
+static struct fss_sc *sc_lookup_volname(struct fss_sc *sc_head,
+ const char *volname)
+{
+ struct fss_sc *sc;
+
+ for (sc = sc_head; sc; sc = sc->next) {
+ if (!strcmp(sc->volume_name, volname)) {
+ return sc;
+ }
+ }
+ DEBUG(4, ("shadow copy with base volume %s not found\n", volname));
+ return NULL;
+}
+
+/* lookup is case-insensitive */
+static struct fss_sc_smap *sc_smap_lookup(struct fss_sc_smap *smaps_head,
+ const char *share)
+{
+ struct fss_sc_smap *sc_smap;
+ for (sc_smap = smaps_head; sc_smap; sc_smap = sc_smap->next) {
+ if (!strcasecmp_m(sc_smap->share_name, share)) {
+ return sc_smap;
+ }
+ }
+ DEBUG(4, ("shadow copy share mapping for %s not found\n", share));
+ return NULL;
+}
+
+static void srv_fssa_cleanup(void)
+{
+ talloc_free(fss_global.db_path);
+ talloc_free(fss_global.mem_ctx);
+ ZERO_STRUCT(fss_global);
+}
+
+static NTSTATUS srv_fssa_start(struct messaging_context *msg_ctx)
+{
+ NTSTATUS status;
+ fss_global.mem_ctx = talloc_named_const(NULL, 0,
+ "parent fss rpc server ctx");
+ if (fss_global.mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fss_global.db_path = lock_path(talloc_tos(), FSS_DB_NAME);
+ if (fss_global.db_path == NULL) {
+ talloc_free(fss_global.mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fss_global.min_vers = FSRVP_RPC_VERSION_1;
+ fss_global.max_vers = FSRVP_RPC_VERSION_1;
+ /*
+ * The server MUST populate the GlobalShadowCopySetTable with the
+ * ShadowCopySet entries read from the configuration store.
+ */
+ if (lp_parm_bool(GLOBAL_SECTION_SNUM, "fss", "prune stale", false)) {
+ fss_prune_stale(msg_ctx, fss_global.db_path);
+ }
+ become_root();
+ status = fss_state_retrieve(fss_global.mem_ctx, &fss_global.sc_sets,
+ &fss_global.sc_sets_count,
+ fss_global.db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to retrieve fss server state: %s\n",
+ nt_errstr(status)));
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Determine whether to process an FSRVP operation from connected user @p.
+ * Windows checks for Administrators or Backup Operators group membership. We
+ * also allow for the SEC_PRIV_BACKUP privilege.
+ */
+static bool fss_permitted(struct pipes_struct *p)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+
+ if (session_info->unix_token->uid == sec_initial_uid()) {
+ DEBUG(6, ("Granting FSRVP op, user started smbd\n"));
+ return true;
+ }
+
+ if (nt_token_check_sid(&global_sid_Builtin_Administrators,
+ session_info->security_token)) {
+ DEBUG(6, ("Granting FSRVP op, administrators group member\n"));
+ return true;
+ }
+ if (nt_token_check_sid(&global_sid_Builtin_Backup_Operators,
+ session_info->security_token)) {
+ DEBUG(6, ("Granting FSRVP op, backup operators group member\n"));
+ return true;
+ }
+ if (security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_BACKUP)) {
+ DEBUG(6, ("Granting FSRVP op, backup privilege present\n"));
+ return true;
+ }
+
+ DEBUG(2, ("FSRVP operation blocked due to lack of backup privilege "
+ "or Administrators/Backup Operators group membership\n"));
+
+ return false;
+}
+
+static void fss_seq_tout_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct GUID *sc_set_id = NULL;
+ struct fss_sc_set *sc_set;
+
+ /*
+ * MS-FSRVP: 3.1.5 Timer Events
+ * Message Sequence Timer elapses: When the Message Sequence Timer
+ * elapses, the server MUST delete the ShadowCopySet in the
+ * GlobalShadowCopySetTable where ShadowCopySet.Status is not equal to
+ * "Recovered", ContextSet MUST be set to FALSE, and the ShadowCopySet
+ * object MUST be freed.
+ */
+ DEBUG(2, ("FSRVP msg seq timeout fired\n"));
+
+ if (private_data == NULL) {
+ DEBUG(4, ("timeout without sc_set\n"));
+ goto out_init_ctx;
+ }
+
+ sc_set_id = talloc_get_type_abort(private_data, struct GUID);
+ sc_set = sc_set_lookup(fss_global.sc_sets, sc_set_id);
+ if (sc_set == NULL) {
+ DEBUG(0, ("timeout for unknown sc_set\n"));
+ goto out_init_ctx;
+ } else if ((sc_set->state == FSS_SC_EXPOSED)
+ || (sc_set->state == FSS_SC_RECOVERED)) {
+ DEBUG(2, ("timeout for finished sc_set %s\n", sc_set->id_str));
+ goto out_init_ctx;
+ }
+ DEBUG(2, ("cleaning up sc_set %s\n", sc_set->id_str));
+ SMB_ASSERT(fss_global.sc_sets_count > 0);
+ DLIST_REMOVE(fss_global.sc_sets, sc_set);
+ fss_global.sc_sets_count--;
+ talloc_free(sc_set);
+
+out_init_ctx:
+ fss_global.ctx_set = false;
+ fss_global.seq_tmr = NULL;
+ talloc_free(sc_set_id);
+}
+
+static void fss_seq_tout_set(TALLOC_CTX *mem_ctx,
+ uint32_t timeout_s,
+ struct fss_sc_set *sc_set,
+ struct tevent_timer **tmr_out)
+{
+ struct tevent_timer *tmr;
+ struct GUID *sc_set_id = NULL;
+ uint32_t tout;
+
+ /* allow changes to timeout for testing/debugging purposes */
+ tout = lp_parm_int(GLOBAL_SECTION_SNUM, "fss",
+ "sequence timeout", timeout_s);
+ if (tout == 0) {
+ DEBUG(2, ("FSRVP message sequence timeout disabled\n"));
+ *tmr_out = NULL;
+ return;
+ }
+
+ if (sc_set) {
+ /* don't use talloc_memdup(), need explicit type for callback */
+ sc_set_id = talloc(mem_ctx, struct GUID);
+ if (sc_set_id == NULL) {
+ smb_panic("no memory");
+ }
+ memcpy(sc_set_id, &sc_set->id, sizeof(*sc_set_id));
+ }
+
+ tmr = tevent_add_timer(global_event_context(),
+ mem_ctx,
+ timeval_current_ofs(tout, 0),
+ fss_seq_tout_handler, sc_set_id);
+ if (tmr == NULL) {
+ talloc_free(sc_set_id);
+ smb_panic("no memory");
+ }
+
+ *tmr_out = tmr;
+}
+
+uint32_t _fss_GetSupportedVersion(struct pipes_struct *p,
+ struct fss_GetSupportedVersion *r)
+{
+ if (!fss_permitted(p)) {
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ *r->out.MinVersion = fss_global.min_vers;
+ *r->out.MaxVersion = fss_global.max_vers;
+
+ return 0;
+}
+
+uint32_t _fss_SetContext(struct pipes_struct *p,
+ struct fss_SetContext *r)
+{
+ if (!fss_permitted(p)) {
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ /* ATTR_AUTO_RECOVERY flag can be applied to any */
+ switch (r->in.Context & (~ATTR_AUTO_RECOVERY)) {
+ case FSRVP_CTX_BACKUP:
+ DEBUG(6, ("fss ctx set backup\n"));
+ break;
+ case FSRVP_CTX_FILE_SHARE_BACKUP:
+ DEBUG(6, ("fss ctx set file share backup\n"));
+ break;
+ case FSRVP_CTX_NAS_ROLLBACK:
+ DEBUG(6, ("fss ctx set nas rollback\n"));
+ break;
+ case FSRVP_CTX_APP_ROLLBACK:
+ DEBUG(6, ("fss ctx set app rollback\n"));
+ break;
+ default:
+ DEBUG(0, ("invalid fss ctx set value: 0x%x\n", r->in.Context));
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ break; /* not reached */
+ }
+
+ fss_global.ctx_set = true;
+ fss_global.cur_ctx = r->in.Context;
+
+ TALLOC_FREE(fss_global.seq_tmr); /* kill timer if running */
+ fss_seq_tout_set(fss_global.mem_ctx, 180, NULL, &fss_global.seq_tmr);
+
+ fss_global.cur_ctx = r->in.Context;
+
+ return 0;
+}
+
+static bool sc_set_active(struct fss_sc_set *sc_set_head)
+{
+
+ struct fss_sc_set *sc_set;
+
+ for (sc_set = sc_set_head; sc_set; sc_set = sc_set->next) {
+ if ((sc_set->state != FSS_SC_EXPOSED)
+ && (sc_set->state != FSS_SC_RECOVERED)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+uint32_t _fss_StartShadowCopySet(struct pipes_struct *p,
+ struct fss_StartShadowCopySet *r)
+{
+ struct fss_sc_set *sc_set;
+ uint32_t ret;
+
+ if (!fss_permitted(p)) {
+ ret = HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ goto err_out;
+ }
+
+ if (!fss_global.ctx_set) {
+ DEBUG(3, ("invalid sequence: start sc set requested without "
+ "prior context set\n"));
+ ret = FSRVP_E_BAD_STATE;
+ goto err_out;
+ }
+
+ /*
+ * At any given time, Windows servers allow only one shadow copy set to
+ * be going through the creation process.
+ */
+ if (sc_set_active(fss_global.sc_sets)) {
+ DEBUG(3, ("StartShadowCopySet called while in progress\n"));
+ ret = FSRVP_E_SHADOW_COPY_SET_IN_PROGRESS;
+ goto err_out;
+ }
+
+ /* stop msg seq timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+
+ sc_set = talloc_zero(fss_global.mem_ctx, struct fss_sc_set);
+ if (sc_set == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_tmr_restart;
+ }
+
+ sc_set->id = GUID_random(); /* Windows servers ignore client ids */
+ sc_set->id_str = GUID_string(sc_set, &sc_set->id);
+ if (sc_set->id_str == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_sc_set_free;
+ }
+ sc_set->state = FSS_SC_STARTED;
+ sc_set->context = fss_global.cur_ctx;
+ DLIST_ADD_END(fss_global.sc_sets, sc_set);
+ fss_global.sc_sets_count++;
+ DEBUG(6, ("%s: shadow-copy set %u added\n",
+ sc_set->id_str, fss_global.sc_sets_count));
+
+ /* start msg seq timer */
+ fss_seq_tout_set(fss_global.mem_ctx, 180, sc_set, &fss_global.seq_tmr);
+
+ r->out.pShadowCopySetId = &sc_set->id;
+
+ return 0;
+
+err_sc_set_free:
+ talloc_free(sc_set);
+err_tmr_restart:
+ fss_seq_tout_set(fss_global.mem_ctx, 180, NULL, &fss_global.seq_tmr);
+err_out:
+ return ret;
+}
+
+static uint32_t map_share_name(struct fss_sc_smap *sc_smap,
+ const struct fss_sc *sc)
+{
+ bool hidden_base = false;
+
+ if (*(sc_smap->share_name + strlen(sc_smap->share_name) - 1) == '$') {
+ /*
+ * If MappedShare.ShareName ends with a $ character (meaning
+ * that the share is hidden), then the exposed share name will
+ * have the $ suffix appended.
+ * FIXME: turns out Windows doesn't do this, contrary to docs
+ */
+ hidden_base = true;
+ }
+
+ sc_smap->sc_share_name = talloc_asprintf(sc_smap, "%s@{%s}%s",
+ sc_smap->share_name,
+ sc->id_str,
+ hidden_base ? "$" : "");
+ if (sc_smap->sc_share_name == NULL) {
+ return HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ }
+
+ return 0;
+}
+
+static uint32_t map_share_comment(struct fss_sc_smap *sc_smap,
+ const struct fss_sc *sc)
+{
+ char *time_str;
+
+ time_str = http_timestring(sc_smap, sc->create_ts);
+ if (time_str == NULL) {
+ return HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ }
+
+ sc_smap->sc_share_comment = talloc_asprintf(sc_smap, "Shadow copy of %s taken %s",
+ sc_smap->share_name, time_str);
+ if (sc_smap->sc_share_comment == NULL) {
+ return HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ }
+
+ return 0;
+}
+
+uint32_t _fss_AddToShadowCopySet(struct pipes_struct *p,
+ struct fss_AddToShadowCopySet *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ uint32_t ret;
+ struct fss_sc_set *sc_set;
+ struct fss_sc *sc;
+ struct fss_sc_smap *sc_smap;
+ int snum;
+ char *service;
+ char *base_vol;
+ char *share;
+ char *path_name;
+ struct connection_struct *conn;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ if (!fss_permitted(p)) {
+ ret = HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ goto err_tmp_free;
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ ret = HRES_ERROR_V(HRES_E_INVALIDARG);
+ goto err_tmp_free;
+ }
+
+ status = fss_unc_parse(frame, r->in.ShareName, NULL, &share);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = fss_ntstatus_map(status);
+ goto err_tmp_free;
+ }
+
+ snum = find_service(frame, share, &service);
+ if ((snum == -1) || (service == NULL)) {
+ DEBUG(0, ("share at %s not found\n", r->in.ShareName));
+ ret = HRES_ERROR_V(HRES_E_INVALIDARG);
+ goto err_tmp_free;
+ }
+
+ path_name = lp_path(frame, lp_sub, snum);
+ if (path_name == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_tmp_free;
+ }
+
+ status = fss_conn_create_tos(p->msg_ctx, session_info, snum, &conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ goto err_tmp_free;
+ }
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ ret = HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ goto err_tmp_free;
+ }
+
+ status = SMB_VFS_SNAP_CHECK_PATH(conn, frame, path_name, &base_vol);
+ unbecome_user_without_service();
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = FSRVP_E_NOT_SUPPORTED;
+ goto err_tmp_free;
+ }
+
+ if ((sc_set->state != FSS_SC_STARTED)
+ && (sc_set->state != FSS_SC_ADDED)) {
+ ret = FSRVP_E_BAD_STATE;
+ goto err_tmp_free;
+ }
+
+ /* stop msg seq timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+
+ /*
+ * server MUST look up the ShadowCopy in ShadowCopySet.ShadowCopyList
+ * where ShadowCopy.VolumeName matches the file store on which the
+ * share identified by ShareName is hosted. If an entry is found, the
+ * server MUST fail the call with FSRVP_E_OBJECT_ALREADY_EXISTS.
+ * If no entry is found, the server MUST create a new ShadowCopy
+ * object
+ * XXX Windows appears to allow multiple mappings for the same vol!
+ */
+ sc = sc_lookup_volname(sc_set->scs, base_vol);
+ if (sc != NULL) {
+ ret = FSRVP_E_OBJECT_ALREADY_EXISTS;
+ goto err_tmr_restart;
+ }
+
+ sc = talloc_zero(sc_set, struct fss_sc);
+ if (sc == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_tmr_restart;
+ }
+ talloc_steal(sc, base_vol);
+ sc->volume_name = base_vol;
+ sc->sc_set = sc_set;
+ sc->create_ts = time(NULL);
+
+ sc->id = GUID_random(); /* Windows servers ignore client ids */
+ sc->id_str = GUID_string(sc, &sc->id);
+ if (sc->id_str == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_sc_free;
+ }
+
+ sc_smap = talloc_zero(sc, struct fss_sc_smap);
+ if (sc_smap == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_sc_free;
+ }
+
+ talloc_steal(sc_smap, service);
+ sc_smap->share_name = service;
+ sc_smap->is_exposed = false;
+ /*
+ * generate the sc_smap share name now. It is a unique identifier for
+ * the smap used as a tdb key for state storage.
+ */
+ ret = map_share_name(sc_smap, sc);
+ if (ret) {
+ goto err_sc_free;
+ }
+
+ /* add share map to shadow-copy */
+ DLIST_ADD_END(sc->smaps, sc_smap);
+ sc->smaps_count++;
+ /* add shadow-copy to shadow-copy set */
+ DLIST_ADD_END(sc_set->scs, sc);
+ sc_set->scs_count++;
+ DEBUG(4, ("added volume %s to shadow copy set with GUID %s\n",
+ sc->volume_name, sc_set->id_str));
+
+ /* start the Message Sequence Timer with timeout of 1800 seconds */
+ fss_seq_tout_set(fss_global.mem_ctx, 1800, sc_set, &fss_global.seq_tmr);
+
+ sc_set->state = FSS_SC_ADDED;
+ r->out.pShadowCopyId = &sc->id;
+
+ TALLOC_FREE(frame);
+ return 0;
+
+err_sc_free:
+ talloc_free(sc);
+err_tmr_restart:
+ fss_seq_tout_set(fss_global.mem_ctx, 180, sc_set, &fss_global.seq_tmr);
+err_tmp_free:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static NTSTATUS commit_sc_with_conn(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ struct fss_sc *sc,
+ char **base_path,
+ char **snap_path)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ bool rw;
+ struct connection_struct *conn;
+ int snum;
+ char *service;
+
+ snum = find_service(frame, sc->smaps->share_name, &service);
+ if ((snum == -1) || (service == NULL)) {
+ DEBUG(0, ("share at %s not found\n", sc->smaps->share_name));
+ TALLOC_FREE(frame);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = fss_conn_create_tos(msg_ctx, session_info, snum, &conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ rw = ((sc->sc_set->context & ATTR_AUTO_RECOVERY) == ATTR_AUTO_RECOVERY);
+ status = SMB_VFS_SNAP_CREATE(conn, mem_ctx,
+ sc->volume_name,
+ &sc->create_ts, rw,
+ base_path, snap_path);
+ unbecome_user_without_service();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("snap create failed: %s\n", nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+uint32_t _fss_CommitShadowCopySet(struct pipes_struct *p,
+ struct fss_CommitShadowCopySet *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct fss_sc_set *sc_set;
+ struct fss_sc *sc;
+ uint32_t commit_count;
+ NTSTATUS status;
+ NTSTATUS saved_status;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!fss_permitted(p)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err_tmp_free;
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto err_tmp_free;
+ }
+
+ if (sc_set->state != FSS_SC_ADDED) {
+ status = NT_STATUS_INVALID_SERVER_STATE;
+ goto err_tmp_free;
+ }
+
+ /* stop Message Sequence Timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+ sc_set->state = FSS_SC_CREATING;
+ commit_count = 0;
+ saved_status = NT_STATUS_OK;
+ for (sc = sc_set->scs; sc; sc = sc->next) {
+ char *base_path;
+ char *snap_path;
+ status = commit_sc_with_conn(frame, global_event_context(),
+ p->msg_ctx, session_info, sc,
+ &base_path, &snap_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("snap create failed for shadow copy of "
+ "%s\n", sc->volume_name));
+ /* dispatch all scs in set, but retain last error */
+ saved_status = status;
+ continue;
+ }
+ /* XXX set timeout r->in.TimeOutInMilliseconds */
+ commit_count++;
+ DEBUG(10, ("good snap create %d\n",
+ commit_count));
+ sc->sc_path = talloc_steal(sc, snap_path);
+ }
+ if (!NT_STATUS_IS_OK(saved_status)) {
+ status = saved_status;
+ goto err_state_revert;
+ }
+
+ sc_set->state = FSS_SC_COMMITED;
+ become_root();
+ status = fss_state_store(fss_global.mem_ctx, fss_global.sc_sets,
+ fss_global.sc_sets_count,
+ fss_global.db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to store fss server state: %s\n",
+ nt_errstr(status)));
+ }
+
+ fss_seq_tout_set(fss_global.mem_ctx, 180, sc_set,
+ &fss_global.seq_tmr);
+ TALLOC_FREE(frame);
+ return 0;
+
+err_state_revert:
+ sc_set->state = FSS_SC_ADDED;
+ fss_seq_tout_set(fss_global.mem_ctx, 180, sc_set,
+ &fss_global.seq_tmr);
+err_tmp_free:
+ TALLOC_FREE(frame);
+ return fss_ntstatus_map(status);
+}
+
+static sbcErr fss_conf_get_share_def(struct smbconf_ctx *fconf_ctx,
+ struct smbconf_ctx *rconf_ctx,
+ TALLOC_CTX *mem_ctx,
+ char *share,
+ struct smbconf_service **service_def)
+{
+ sbcErr cerr;
+ struct smbconf_service *def;
+
+ *service_def = NULL;
+ cerr = smbconf_get_share(fconf_ctx, mem_ctx, share, &def);
+ if (SBC_ERROR_IS_OK(cerr)) {
+ *service_def = def;
+ return SBC_ERR_OK;
+ }
+
+ cerr = smbconf_get_share(rconf_ctx, mem_ctx, share, &def);
+ if (SBC_ERROR_IS_OK(cerr)) {
+ *service_def = def;
+ return SBC_ERR_OK;
+ }
+ return cerr;
+}
+
+/*
+ * Expose a new share using libsmbconf, cloning the existing configuration
+ * from the base share. The base share may be defined in either the registry
+ * or smb.conf.
+ * XXX this is called as root
+ */
+static uint32_t fss_sc_expose(struct smbconf_ctx *fconf_ctx,
+ struct smbconf_ctx *rconf_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct fss_sc *sc)
+{
+ struct fss_sc_smap *sc_smap;
+ uint32_t err = 0;
+
+ for (sc_smap = sc->smaps; sc_smap; sc_smap = sc_smap->next) {
+ sbcErr cerr;
+ struct smbconf_service *base_service = NULL;
+ struct security_descriptor *sd;
+ size_t sd_size;
+
+ cerr = fss_conf_get_share_def(fconf_ctx, rconf_ctx, mem_ctx,
+ sc_smap->share_name, &base_service);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed to get base share %s definition: "
+ "%s\n", sc_smap->share_name,
+ sbcErrorString(cerr)));
+ err = HRES_ERROR_V(HRES_E_FAIL);
+ break;
+ }
+
+ /* smap share name already defined when added */
+ err = map_share_comment(sc_smap, sc);
+ if (err) {
+ DEBUG(0, ("failed to map share comment\n"));
+ break;
+ }
+
+ base_service->name = sc_smap->sc_share_name;
+
+ cerr = smbconf_create_set_share(rconf_ctx, base_service);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed to create share %s: %s\n",
+ base_service->name, sbcErrorString(cerr)));
+ err = HRES_ERROR_V(HRES_E_FAIL);
+ break;
+ }
+ cerr = smbconf_set_parameter(rconf_ctx, sc_smap->sc_share_name,
+ "path", sc->sc_path);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed to set path param: %s\n",
+ sbcErrorString(cerr)));
+ err = HRES_ERROR_V(HRES_E_FAIL);
+ break;
+ }
+ if (sc_smap->sc_share_comment != NULL) {
+ cerr = smbconf_set_parameter(rconf_ctx,
+ sc_smap->sc_share_name,
+ "comment",
+ sc_smap->sc_share_comment);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed to set comment param: %s\n",
+ sbcErrorString(cerr)));
+ err = HRES_ERROR_V(HRES_E_FAIL);
+ break;
+ }
+ }
+ talloc_free(base_service);
+
+ /*
+ * Obtain the base share SD, which also needs to be cloned.
+ * Share SDs are stored in share_info.tdb, so are not covered by
+ * the registry transaction.
+ * The base share SD should be cloned at the time of exposure,
+ * rather than when the snapshot is taken. This matches Windows
+ * Server 2012 behaviour.
+ */
+ sd = get_share_security(mem_ctx, sc_smap->share_name, &sd_size);
+ if (sd == NULL) {
+ DEBUG(2, ("no share SD to clone for %s snapshot\n",
+ sc_smap->share_name));
+ } else {
+ NTSTATUS status;
+ status = set_share_security(sc_smap->sc_share_name, sd);
+ TALLOC_FREE(sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("failed to set %s share SD\n",
+ sc_smap->sc_share_name));
+ err = HRES_ERROR_V(HRES_E_FAIL);
+ break;
+ }
+ }
+ }
+
+ return err;
+}
+
+uint32_t _fss_ExposeShadowCopySet(struct pipes_struct *p,
+ struct fss_ExposeShadowCopySet *r)
+{
+ NTSTATUS status;
+ struct fss_sc_set *sc_set;
+ struct fss_sc *sc;
+ uint32_t ret;
+ struct smbconf_ctx *fconf_ctx;
+ struct smbconf_ctx *rconf_ctx;
+ sbcErr cerr;
+ char *fconf_path;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!fss_permitted(p)) {
+ ret = HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ goto err_out;
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ ret = HRES_ERROR_V(HRES_E_INVALIDARG);
+ goto err_out;
+ }
+
+ if (sc_set->state != FSS_SC_COMMITED) {
+ ret = FSRVP_E_BAD_STATE;
+ goto err_out;
+ }
+
+ /* stop message sequence timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+
+ /*
+ * Prepare to clone the base share definition for the snapshot share.
+ * Create both registry and file conf contexts, as the base share
+ * definition may be located in either. The snapshot share definition
+ * is always written to the registry.
+ */
+ cerr = smbconf_init(frame, &rconf_ctx, "registry");
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed registry smbconf init: %s\n",
+ sbcErrorString(cerr)));
+ ret = HRES_ERROR_V(HRES_E_FAIL);
+ goto err_tmr_restart;
+ }
+ fconf_path = talloc_asprintf(frame, "file:%s", get_dyn_CONFIGFILE());
+ if (fconf_path == NULL) {
+ ret = HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ goto err_tmr_restart;
+ }
+ cerr = smbconf_init(frame, &fconf_ctx, fconf_path);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed %s smbconf init: %s\n",
+ fconf_path, sbcErrorString(cerr)));
+ ret = HRES_ERROR_V(HRES_E_FAIL);
+ goto err_tmr_restart;
+ }
+
+ /* registry IO must be done as root */
+ become_root();
+ cerr = smbconf_transaction_start(rconf_ctx);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("error starting transaction: %s\n",
+ sbcErrorString(cerr)));
+ ret = HRES_ERROR_V(HRES_E_FAIL);
+ unbecome_root();
+ goto err_tmr_restart;
+ }
+
+ for (sc = sc_set->scs; sc; sc = sc->next) {
+ ret = fss_sc_expose(fconf_ctx, rconf_ctx, frame, sc);
+ if (ret) {
+ DEBUG(0,("failed to expose shadow copy of %s\n",
+ sc->volume_name));
+ goto err_cancel;
+ }
+ }
+
+ cerr = smbconf_transaction_commit(rconf_ctx);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("error committing transaction: %s\n",
+ sbcErrorString(cerr)));
+ ret = HRES_ERROR_V(HRES_E_FAIL);
+ goto err_cancel;
+ }
+ unbecome_root();
+
+ messaging_send_all(p->msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0);
+ for (sc = sc_set->scs; sc; sc = sc->next) {
+ struct fss_sc_smap *sm;
+ for (sm = sc->smaps; sm; sm = sm->next)
+ sm->is_exposed = true;
+ }
+ sc_set->state = FSS_SC_EXPOSED;
+ become_root();
+ status = fss_state_store(fss_global.mem_ctx, fss_global.sc_sets,
+ fss_global.sc_sets_count, fss_global.db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to store fss server state: %s\n",
+ nt_errstr(status)));
+ }
+ /* start message sequence timer */
+ fss_seq_tout_set(fss_global.mem_ctx, 180, sc_set, &fss_global.seq_tmr);
+ TALLOC_FREE(frame);
+ return 0;
+
+err_cancel:
+ smbconf_transaction_cancel(rconf_ctx);
+ unbecome_root();
+err_tmr_restart:
+ fss_seq_tout_set(fss_global.mem_ctx, 180, sc_set, &fss_global.seq_tmr);
+err_out:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+uint32_t _fss_RecoveryCompleteShadowCopySet(struct pipes_struct *p,
+ struct fss_RecoveryCompleteShadowCopySet *r)
+{
+ NTSTATUS status;
+ struct fss_sc_set *sc_set;
+
+ if (!fss_permitted(p)) {
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ if (sc_set->state != FSS_SC_EXPOSED) {
+ return FSRVP_E_BAD_STATE;
+ }
+
+ /* stop msg sequence timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+
+ if (sc_set->context & ATTR_NO_AUTO_RECOVERY) {
+ /* TODO set read-only */
+ }
+
+ sc_set->state = FSS_SC_RECOVERED;
+ fss_global.cur_ctx = 0;
+ fss_global.ctx_set = false;
+
+ become_root();
+ status = fss_state_store(fss_global.mem_ctx, fss_global.sc_sets,
+ fss_global.sc_sets_count, fss_global.db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to store fss server state: %s\n",
+ nt_errstr(status)));
+ }
+
+ return 0;
+}
+
+uint32_t _fss_AbortShadowCopySet(struct pipes_struct *p,
+ struct fss_AbortShadowCopySet *r)
+{
+ NTSTATUS status;
+ struct fss_sc_set *sc_set;
+
+ if (!fss_permitted(p)) {
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ DEBUG(6, ("%s: aborting shadow-copy set\n", sc_set->id_str));
+
+ if ((sc_set->state == FSS_SC_COMMITED)
+ || (sc_set->state == FSS_SC_EXPOSED)
+ || (sc_set->state == FSS_SC_RECOVERED)) {
+ return 0;
+ }
+
+ if (sc_set->state == FSS_SC_CREATING) {
+ return FSRVP_E_BAD_STATE;
+ }
+
+ DLIST_REMOVE(fss_global.sc_sets, sc_set);
+ talloc_free(sc_set);
+ fss_global.sc_sets_count--;
+ become_root();
+ status = fss_state_store(fss_global.mem_ctx, fss_global.sc_sets,
+ fss_global.sc_sets_count, fss_global.db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to store fss server state: %s\n",
+ nt_errstr(status)));
+ }
+
+ return 0;
+}
+
+uint32_t _fss_IsPathSupported(struct pipes_struct *p,
+ struct fss_IsPathSupported *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ int snum;
+ char *service;
+ char *base_vol;
+ NTSTATUS status;
+ struct connection_struct *conn;
+ char *share;
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ if (!fss_permitted(p)) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ status = fss_unc_parse(frame, r->in.ShareName, NULL, &share);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return fss_ntstatus_map(status);
+ }
+
+ snum = find_service(frame, share, &service);
+ if ((snum == -1) || (service == NULL)) {
+ DEBUG(0, ("share at %s not found\n", r->in.ShareName));
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ status = fss_conn_create_tos(p->msg_ctx, session_info, snum, &conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+ status = SMB_VFS_SNAP_CHECK_PATH(conn, frame,
+ lp_path(frame, lp_sub, snum),
+ &base_vol);
+ unbecome_user_without_service();
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return FSRVP_E_NOT_SUPPORTED;
+ }
+
+ *r->out.OwnerMachineName = lp_netbios_name();
+ *r->out.SupportedByThisProvider = 1;
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+uint32_t _fss_IsPathShadowCopied(struct pipes_struct *p,
+ struct fss_IsPathShadowCopied *r)
+{
+ if (!fss_permitted(p)) {
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ /* not yet supported */
+ return FSRVP_E_NOT_SUPPORTED;
+}
+
+uint32_t _fss_GetShareMapping(struct pipes_struct *p,
+ struct fss_GetShareMapping *r)
+{
+ NTSTATUS status;
+ struct fss_sc_set *sc_set;
+ struct fss_sc *sc;
+ struct fss_sc_smap *sc_smap;
+ char *share;
+ struct fssagent_share_mapping_1 *sm_out;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!fss_permitted(p)) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ /*
+ * If ShadowCopySet.Status is not "Exposed", the server SHOULD<9> fail
+ * the call with FSRVP_E_BAD_STATE.
+ * <9> If ShadowCopySet.Status is "Started", "Added",
+ * "CreationInProgress", or "Committed", Windows Server 2012 FSRVP
+ * servers return an error value of 0x80042311.
+ */
+ if ((sc_set->state == FSS_SC_STARTED)
+ || (sc_set->state == FSS_SC_ADDED)
+ || (sc_set->state == FSS_SC_CREATING)
+ || (sc_set->state == FSS_SC_COMMITED)) {
+ TALLOC_FREE(frame);
+ return 0x80042311; /* documented magic value */
+ }
+
+ sc = sc_lookup(sc_set->scs, &r->in.ShadowCopyId);
+ if (sc == NULL) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ status = fss_unc_parse(frame, r->in.ShareName, NULL, &share);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return fss_ntstatus_map(status);
+ }
+
+ sc_smap = sc_smap_lookup(sc->smaps, share);
+ if (sc_smap == NULL) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ if (r->in.Level != 1) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ sm_out = talloc_zero(p->mem_ctx, struct fssagent_share_mapping_1);
+ if (sm_out == NULL) {
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ }
+ sm_out->ShadowCopySetId = sc_set->id;
+ sm_out->ShadowCopyId = sc->id;
+ sm_out->ShareNameUNC = talloc_asprintf(sm_out, "\\\\%s\\%s",
+ lp_netbios_name(),
+ sc_smap->share_name);
+ if (sm_out->ShareNameUNC == NULL) {
+ talloc_free(sm_out);
+ TALLOC_FREE(frame);
+ return HRES_ERROR_V(HRES_E_OUTOFMEMORY);
+ }
+ sm_out->ShadowCopyShareName = sc_smap->sc_share_name;
+ unix_to_nt_time(&sm_out->tstamp, sc->create_ts);
+ r->out.ShareMapping->ShareMapping1 = sm_out;
+ TALLOC_FREE(frame);
+
+ /* reset msg sequence timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+ fss_seq_tout_set(fss_global.mem_ctx, 1800, sc_set, &fss_global.seq_tmr);
+
+ return 0;
+}
+
+static NTSTATUS sc_smap_unexpose(struct messaging_context *msg_ctx,
+ struct fss_sc_smap *sc_smap, bool delete_all)
+{
+ NTSTATUS ret;
+ struct smbconf_ctx *conf_ctx;
+ sbcErr cerr;
+ bool is_modified = false;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ cerr = smbconf_init(frame, &conf_ctx, "registry");
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("failed registry smbconf init: %s\n",
+ sbcErrorString(cerr)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto err_tmp;
+ }
+
+ /* registry IO must be done as root */
+ become_root();
+
+ cerr = smbconf_transaction_start(conf_ctx);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("error starting transaction: %s\n",
+ sbcErrorString(cerr)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto err_conf;
+ }
+
+ while (sc_smap) {
+ struct fss_sc_smap *sc_map_next = sc_smap->next;
+ if (!smbconf_share_exists(conf_ctx, sc_smap->sc_share_name)) {
+ DEBUG(2, ("no such share: %s\n", sc_smap->sc_share_name));
+ if (!delete_all) {
+ ret = NT_STATUS_OK;
+ goto err_cancel;
+ }
+ sc_smap = sc_map_next;
+ continue;
+ }
+
+ cerr = smbconf_delete_share(conf_ctx, sc_smap->sc_share_name);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("error deleting share: %s\n",
+ sbcErrorString(cerr)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto err_cancel;
+ }
+ is_modified = true;
+ sc_smap->is_exposed = false;
+ if (delete_all) {
+ sc_smap = sc_map_next;
+ } else {
+ sc_smap = NULL; /* only process single sc_map entry */
+ }
+ }
+ if (is_modified) {
+ cerr = smbconf_transaction_commit(conf_ctx);
+ if (!SBC_ERROR_IS_OK(cerr)) {
+ DEBUG(0, ("error committing transaction: %s\n",
+ sbcErrorString(cerr)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto err_cancel;
+ }
+ messaging_send_all(msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0);
+ } else {
+ ret = NT_STATUS_OK;
+ goto err_cancel;
+ }
+ ret = NT_STATUS_OK;
+
+err_conf:
+ talloc_free(conf_ctx);
+ unbecome_root();
+err_tmp:
+ TALLOC_FREE(frame);
+ return ret;
+
+err_cancel:
+ smbconf_transaction_cancel(conf_ctx);
+ talloc_free(conf_ctx);
+ unbecome_root();
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+uint32_t _fss_DeleteShareMapping(struct pipes_struct *p,
+ struct fss_DeleteShareMapping *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct fss_sc_set *sc_set;
+ struct fss_sc *sc;
+ struct fss_sc_smap *sc_smap;
+ char *share;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct connection_struct *conn;
+ int snum;
+ char *service;
+
+ if (!fss_permitted(p)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err_tmp_free;
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ /* docs say HRES_E_INVALIDARG */
+ status = NT_STATUS_OBJECTID_NOT_FOUND;
+ goto err_tmp_free;
+ }
+
+ if ((sc_set->state != FSS_SC_EXPOSED)
+ && (sc_set->state != FSS_SC_RECOVERED)) {
+ status = NT_STATUS_INVALID_SERVER_STATE;
+ goto err_tmp_free;
+ }
+
+ sc = sc_lookup(sc_set->scs, &r->in.ShadowCopyId);
+ if (sc == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto err_tmp_free;
+ }
+
+ status = fss_unc_parse(frame, r->in.ShareName, NULL, &share);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_tmp_free;
+ }
+
+ sc_smap = sc_smap_lookup(sc->smaps, share);
+ if (sc_smap == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto err_tmp_free;
+ }
+
+ status = sc_smap_unexpose(p->msg_ctx, sc_smap, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("failed to remove share %s: %s\n",
+ sc_smap->sc_share_name, nt_errstr(status)));
+ goto err_tmp_free;
+ }
+
+ messaging_send_all(p->msg_ctx, MSG_SMB_FORCE_TDIS,
+ sc_smap->sc_share_name,
+ strlen(sc_smap->sc_share_name) + 1);
+
+ if (sc->smaps_count > 1) {
+ /* do not delete the underlying snapshot - still in use */
+ status = NT_STATUS_OK;
+ goto err_tmp_free;
+ }
+
+ snum = find_service(frame, sc_smap->share_name, &service);
+ if ((snum == -1) || (service == NULL)) {
+ DEBUG(0, ("share at %s not found\n", sc_smap->share_name));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_tmp_free;
+ }
+
+ status = fss_conn_create_tos(p->msg_ctx, session_info, snum, &conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_tmp_free;
+ }
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err_tmp_free;
+ }
+
+ status = SMB_VFS_SNAP_DELETE(conn, frame, sc->volume_name,
+ sc->sc_path);
+ unbecome_user_without_service();
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_tmp_free;
+ }
+
+ /* XXX set timeout r->in.TimeOutInMilliseconds */
+ DEBUG(6, ("good snap delete\n"));
+ DLIST_REMOVE(sc->smaps, sc_smap);
+ sc->smaps_count--;
+ talloc_free(sc_smap);
+ if (sc->smaps_count == 0) {
+ DLIST_REMOVE(sc_set->scs, sc);
+ sc_set->scs_count--;
+ talloc_free(sc);
+
+ if (sc_set->scs_count == 0) {
+ DLIST_REMOVE(fss_global.sc_sets, sc_set);
+ fss_global.sc_sets_count--;
+ talloc_free(sc_set);
+ }
+ }
+
+ become_root();
+ status = fss_state_store(fss_global.mem_ctx, fss_global.sc_sets,
+ fss_global.sc_sets_count, fss_global.db_path);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to store fss server state: %s\n",
+ nt_errstr(status)));
+ }
+
+ status = NT_STATUS_OK;
+err_tmp_free:
+ TALLOC_FREE(frame);
+ return fss_ntstatus_map(status);
+}
+
+uint32_t _fss_PrepareShadowCopySet(struct pipes_struct *p,
+ struct fss_PrepareShadowCopySet *r)
+{
+ struct fss_sc_set *sc_set;
+
+ if (!fss_permitted(p)) {
+ return HRES_ERROR_V(HRES_E_ACCESSDENIED);
+ }
+
+ sc_set = sc_set_lookup(fss_global.sc_sets, &r->in.ShadowCopySetId);
+ if (sc_set == NULL) {
+ return HRES_ERROR_V(HRES_E_INVALIDARG);
+ }
+
+ if (sc_set->state != FSS_SC_ADDED) {
+ return FSRVP_E_BAD_STATE;
+ }
+
+ /* stop msg sequence timer */
+ TALLOC_FREE(fss_global.seq_tmr);
+
+ /*
+ * Windows Server "8" Beta takes ~60s here, presumably flushing
+ * everything to disk. We may want to do something similar.
+ */
+
+ /* start msg sequence timer, 1800 on success */
+ fss_seq_tout_set(fss_global.mem_ctx, 1800, sc_set, &fss_global.seq_tmr);
+
+ return 0;
+}
+
+static NTSTATUS FileServerVssAgent__op_init_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+static NTSTATUS FileServerVssAgent__op_shutdown_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+#define DCESRV_INTERFACE_FILESERVERVSSAGENT_INIT_SERVER \
+ fileservervssagent_init_server
+
+#define DCESRV_INTERFACE_FILESERVERVSSAGENT_SHUTDOWN_SERVER \
+ fileservervssagent_shutdown_server
+
+static NTSTATUS fileservervssagent_shutdown_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ srv_fssa_cleanup();
+ return FileServerVssAgent__op_shutdown_server(dce_ctx, ep_server);
+}
+
+static NTSTATUS fileservervssagent_init_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ NTSTATUS status;
+ struct messaging_context *msg_ctx = global_messaging_context();
+
+ status = srv_fssa_start(msg_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return FileServerVssAgent__op_init_server(dce_ctx, ep_server);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_fsrvp_scompat.c"
diff --git a/source3/rpc_server/fss/srv_fss_private.h b/source3/rpc_server/fss/srv_fss_private.h
new file mode 100644
index 0000000..4db9f98
--- /dev/null
+++ b/source3/rpc_server/fss/srv_fss_private.h
@@ -0,0 +1,92 @@
+/*
+ * File Server Remote VSS Protocol (FSRVP) server state
+ *
+ * Copyright (C) David Disseldorp 2012-2015
+ *
+ * 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 _SRV_FSS_PRIVATE_H_
+#define _SRV_FSS_PRIVATE_H_
+
+#define FSS_DB_NAME "srv_fss.tdb"
+
+struct fss_sc_smap {
+ struct fss_sc_smap *next, *prev;
+ char *share_name; /* name of the base file share */
+ char *sc_share_name; /* share exposing the shadow copy */
+ char *sc_share_comment;
+ bool is_exposed; /* whether shadow copy is exposed */
+};
+
+struct fss_sc {
+ struct fss_sc *next, *prev;
+ struct GUID id; /* GUID of the shadow copy */
+ char *id_str;
+ char *volume_name; /* name uniquely identifying on the
+ * server object store on which this
+ * shadow copy is created. */
+ char *sc_path; /* path exposing the shadow copy */
+ time_t create_ts; /* timestamp of client initiation */
+ struct fss_sc_smap *smaps; /* shares mapped to this shadow copy */
+ uint32_t smaps_count;
+ struct fss_sc_set *sc_set; /* parent shadow copy set */
+};
+
+/*
+ * 3.1.1.2: Per ShadowCopySet
+ * The status of the shadow copy set. This MUST be one of "Started", "Added",
+ * "CreationInProgress", "Committed", "Exposed", or "Recovered".
+ */
+enum fss_sc_state {
+ FSS_SC_STARTED,
+ FSS_SC_ADDED,
+ FSS_SC_CREATING,
+ FSS_SC_COMMITED,
+ FSS_SC_EXPOSED,
+ FSS_SC_RECOVERED,
+};
+struct fss_sc_set {
+ struct fss_sc_set *next, *prev;
+ struct GUID id; /* GUID of the shadow copy set. */
+ char *id_str;
+ enum fss_sc_state state; /* status of the shadow copy set */
+ uint32_t context; /* attributes used for set creation */
+ struct fss_sc *scs; /* list of ShadowCopy objects */
+ uint32_t scs_count;
+};
+
+struct fss_global {
+ TALLOC_CTX *mem_ctx; /* parent mem ctx for sc sets */
+ char *db_path;
+ uint32_t min_vers;
+ uint32_t max_vers;
+ bool ctx_set; /* whether client has set context */
+ uint32_t cur_ctx;
+ struct fss_sc_set *sc_sets;
+ uint32_t sc_sets_count;
+ struct tevent_timer *seq_tmr; /* time to wait between client reqs */
+};
+
+NTSTATUS fss_state_store(TALLOC_CTX *mem_ctx,
+ struct fss_sc_set *sc_sets,
+ uint32_t sc_sets_count,
+ const char *db_path);
+
+NTSTATUS fss_state_retrieve(TALLOC_CTX *mem_ctx,
+ struct fss_sc_set **sc_sets,
+ uint32_t *sc_sets_count,
+ const char *db_path);
+
+#endif /*_SRV_FSS_PRIVATE_H_ */
diff --git a/source3/rpc_server/fss/srv_fss_state.c b/source3/rpc_server/fss/srv_fss_state.c
new file mode 100644
index 0000000..8597c36
--- /dev/null
+++ b/source3/rpc_server/fss/srv_fss_state.c
@@ -0,0 +1,698 @@
+/*
+ * File Server Remote VSS Protocol (FSRVP) persistent server state
+ *
+ * Copyright (C) David Disseldorp 2012-2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "source3/include/includes.h"
+#include <fcntl.h>
+#include "source3/include/util_tdb.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_fsrvp_state.h"
+#include "srv_fss_private.h"
+
+#define FSS_DB_KEY_VERSION "db_version"
+#define FSS_DB_KEY_CONTEXT "context"
+#define FSS_DB_KEY_SC_SET_COUNT "sc_set_count"
+#define FSS_DB_KEY_PFX_SC_SET "sc_set/"
+#define FSS_DB_KEY_PFX_SC "sc/"
+#define FSS_DB_KEY_PFX_SMAP "smap/"
+
+static NTSTATUS fss_state_smap_store(TALLOC_CTX *mem_ctx,
+ struct db_context *db,
+ const char *sc_key_str,
+ struct fss_sc_smap *smap)
+{
+ NTSTATUS status;
+ TDB_DATA val;
+ const char *smap_key_str;
+ struct fsrvp_state_smap smap_state;
+ enum ndr_err_code ndr_ret;
+ DATA_BLOB smap_state_blob;
+
+ /* becomes sc_set/@sc_set_id/sc/@sc_id/smap/@sc_share_name */
+ smap_key_str = talloc_asprintf(mem_ctx, "%s/%s%s", sc_key_str,
+ FSS_DB_KEY_PFX_SMAP,
+ smap->sc_share_name);
+ if (smap_key_str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smap_state.share_name = smap->share_name;
+ smap_state.sc_share_name = smap->sc_share_name;
+ /* @smap->sc_share_comment may be null if not exposed. */
+ if (smap->sc_share_comment != NULL) {
+ smap_state.sc_share_comment = smap->sc_share_comment;
+ } else {
+ smap_state.sc_share_comment = "";
+ }
+ smap_state.is_exposed = smap->is_exposed;
+
+ ndr_ret = ndr_push_struct_blob(&smap_state_blob, mem_ctx,
+ &smap_state,
+ (ndr_push_flags_fn_t)ndr_push_fsrvp_state_smap);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ val.dsize = smap_state_blob.length;
+ val.dptr = smap_state_blob.data;
+
+ status = dbwrap_store(db, string_term_tdb_data(smap_key_str), val, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_state_sc_store(TALLOC_CTX *mem_ctx,
+ struct db_context *db,
+ const char *sc_set_key_str,
+ struct fss_sc *sc)
+{
+ NTSTATUS status;
+ TDB_DATA val;
+ const char *sc_key_str;
+ struct fsrvp_state_sc sc_state;
+ struct fss_sc_smap *smap;
+ enum ndr_err_code ndr_ret;
+ DATA_BLOB sc_state_blob;
+
+ /* becomes sc_set/@sc_set.id/sc/@sc_id */
+ sc_key_str = talloc_asprintf(mem_ctx, "%s/%s%s", sc_set_key_str,
+ FSS_DB_KEY_PFX_SC, sc->id_str);
+ if (sc_key_str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sc_state.id_str = sc->id_str;
+ sc_state.volume_name = sc->volume_name;
+ /* @sc->sc_path may be null if not committed, store empty str */
+ sc_state.sc_path = (sc->sc_path ? sc->sc_path : "");
+ sc_state.create_ts = sc->create_ts;
+ sc_state.smaps_count = sc->smaps_count;
+
+ ndr_ret = ndr_push_struct_blob(&sc_state_blob, mem_ctx,
+ &sc_state,
+ (ndr_push_flags_fn_t)ndr_push_fsrvp_state_sc);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ val.dsize = sc_state_blob.length;
+ val.dptr = sc_state_blob.data;
+
+ status = dbwrap_store(db, string_term_tdb_data(sc_key_str), val, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (smap = sc->smaps; smap; smap = smap->next) {
+ status = fss_state_smap_store(mem_ctx, db, sc_key_str, smap);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_state_sc_set_store(TALLOC_CTX *mem_ctx,
+ struct db_context *db,
+ struct fss_sc_set *sc_set)
+{
+ NTSTATUS status;
+ TDB_DATA val;
+ const char *sc_set_key_str;
+ struct fss_sc *sc;
+ struct fsrvp_state_sc_set sc_set_state;
+ DATA_BLOB sc_set_state_blob;
+ enum ndr_err_code ndr_ret;
+
+ sc_set_key_str = talloc_asprintf(mem_ctx, "%s%s",
+ FSS_DB_KEY_PFX_SC_SET,
+ sc_set->id_str);
+ if (sc_set_key_str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sc_set_state.id_str = sc_set->id_str;
+ sc_set_state.state = sc_set->state;
+ sc_set_state.context = sc_set->context;
+ sc_set_state.scs_count = sc_set->scs_count;
+
+ ndr_ret = ndr_push_struct_blob(&sc_set_state_blob, mem_ctx,
+ &sc_set_state,
+ (ndr_push_flags_fn_t)ndr_push_fsrvp_state_sc_set);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ val.dsize = sc_set_state_blob.length;
+ val.dptr = sc_set_state_blob.data;
+
+ status = dbwrap_store(db, string_term_tdb_data(sc_set_key_str), val, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (sc = sc_set->scs; sc; sc = sc->next) {
+ status = fss_state_sc_store(mem_ctx, db, sc_set_key_str, sc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * write out the current fsrvp server state to a TDB. This clears any content
+ * currently written to the TDB.
+ */
+_PRIVATE_ NTSTATUS fss_state_store(TALLOC_CTX *mem_ctx,
+ struct fss_sc_set *sc_sets,
+ uint32_t sc_sets_count,
+ const char *db_path)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct db_context *db;
+ NTSTATUS status;
+ int ret;
+ struct fss_sc_set *sc_set;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ db = db_open(tmp_ctx, db_path, 0, TDB_DEFAULT, O_RDWR | O_CREAT,
+ 0600, DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ DEBUG(0, ("Failed to open fss state database %s\n", db_path));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err_ctx_free;
+ }
+
+ ret = dbwrap_wipe(db);
+ if (ret != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_db_free;
+ }
+
+ status = dbwrap_store_int32_bystring(db, FSS_DB_KEY_VERSION,
+ FSRVP_STATE_DB_VERSION);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_db_free;
+ }
+
+ ret = dbwrap_transaction_start(db);
+ if (ret != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_db_free;
+ }
+
+ status = dbwrap_store_int32_bystring(db, FSS_DB_KEY_SC_SET_COUNT,
+ sc_sets_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_trans_cancel;
+ }
+
+ for (sc_set = sc_sets; sc_set; sc_set = sc_set->next) {
+ status = fss_state_sc_set_store(tmp_ctx, db, sc_set);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_trans_cancel;
+ }
+ }
+
+ ret = dbwrap_transaction_commit(db);
+ if (ret != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_trans_cancel;
+ }
+
+ talloc_free(db);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+
+err_trans_cancel:
+ dbwrap_transaction_cancel(db);
+err_db_free:
+ talloc_free(db);
+err_ctx_free:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+static NTSTATUS fss_state_smap_retrieve(TALLOC_CTX *mem_ctx,
+ TDB_DATA *key,
+ TDB_DATA *val,
+ struct fss_sc_smap **smap_out)
+{
+ struct fss_sc_smap *smap;
+ struct fsrvp_state_smap smap_state;
+ DATA_BLOB smap_state_blob;
+ enum ndr_err_code ndr_ret;
+
+ smap_state_blob.length = val->dsize;
+ smap_state_blob.data = val->dptr;
+
+ ndr_ret = ndr_pull_struct_blob(&smap_state_blob, mem_ctx, &smap_state,
+ (ndr_pull_flags_fn_t)ndr_pull_fsrvp_state_smap);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ smap = talloc_zero(mem_ctx, struct fss_sc_smap);
+ if (smap == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smap->share_name = talloc_strdup(smap, smap_state.share_name);
+ if (smap->share_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* store the full path so that the hierarchy can be rebuilt */
+ smap->sc_share_name = talloc_strdup(smap, (char *)key->dptr);
+ if (smap->sc_share_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* sc_share_comment may be empty, keep null in such a case */
+ if (strlen(smap_state.sc_share_comment) > 0) {
+ smap->sc_share_comment = talloc_strdup(smap,
+ smap_state.sc_share_comment);
+ if (smap->sc_share_comment == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ smap->is_exposed = smap_state.is_exposed;
+
+ *smap_out = smap;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_state_sc_retrieve(TALLOC_CTX *mem_ctx,
+ TDB_DATA *key,
+ TDB_DATA *val,
+ struct fss_sc **sc_out)
+{
+ struct fss_sc *sc;
+ struct fsrvp_state_sc sc_state;
+ DATA_BLOB sc_state_blob;
+ enum ndr_err_code ndr_ret;
+
+ sc_state_blob.length = val->dsize;
+ sc_state_blob.data = val->dptr;
+
+ ndr_ret = ndr_pull_struct_blob(&sc_state_blob, mem_ctx, &sc_state,
+ (ndr_pull_flags_fn_t)ndr_pull_fsrvp_state_sc);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ sc = talloc_zero(mem_ctx, struct fss_sc);
+ if (sc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* store the full path so that the hierarchy can be rebuilt */
+ sc->id_str = talloc_strdup(sc, (char *)key->dptr);
+ if (sc->id_str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sc->volume_name = talloc_strdup(sc, sc_state.volume_name);
+ if (sc->volume_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* sc_path may be empty, keep null in such a case */
+ if (strlen(sc_state.sc_path) > 0) {
+ sc->sc_path = talloc_strdup(sc, sc_state.sc_path);
+ if (sc->sc_path == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ sc->create_ts = sc_state.create_ts;
+ sc->smaps_count = sc_state.smaps_count;
+
+ *sc_out = sc;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fss_state_sc_set_retrieve(TALLOC_CTX *mem_ctx,
+ TDB_DATA *key,
+ TDB_DATA *val,
+ struct fss_sc_set **sc_set_out)
+{
+ struct fss_sc_set *sc_set;
+ struct fsrvp_state_sc_set sc_set_state;
+ DATA_BLOB sc_set_state_blob;
+ enum ndr_err_code ndr_ret;
+
+ sc_set_state_blob.length = val->dsize;
+ sc_set_state_blob.data = val->dptr;
+
+ ndr_ret = ndr_pull_struct_blob(&sc_set_state_blob, mem_ctx,
+ &sc_set_state,
+ (ndr_pull_flags_fn_t)ndr_pull_fsrvp_state_sc_set);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ sc_set = talloc_zero(mem_ctx, struct fss_sc_set);
+ if (sc_set == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* store the full path so that the hierarchy can be rebuilt */
+ sc_set->id_str = talloc_strdup(sc_set, (char *)key->dptr);
+ if (sc_set->id_str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sc_set->state = sc_set_state.state;
+ sc_set->context = sc_set_state.context;
+ sc_set->scs_count = sc_set_state.scs_count;
+
+ *sc_set_out = sc_set;
+ return NT_STATUS_OK;
+}
+
+struct fss_traverse_state {
+ TALLOC_CTX *mem_ctx;
+ struct fss_sc_smap *smaps;
+ uint32_t smaps_count;
+ struct fss_sc *scs;
+ uint32_t scs_count;
+ struct fss_sc_set *sc_sets;
+ uint32_t sc_sets_count;
+ NTSTATUS (*smap_retrieve)(TALLOC_CTX *mem_ctx,
+ TDB_DATA *key,
+ TDB_DATA *val,
+ struct fss_sc_smap **smap_out);
+ NTSTATUS (*sc_retrieve)(TALLOC_CTX *mem_ctx,
+ TDB_DATA *key,
+ TDB_DATA *val,
+ struct fss_sc **sc_out);
+ NTSTATUS (*sc_set_retrieve)(TALLOC_CTX *mem_ctx,
+ TDB_DATA *key,
+ TDB_DATA *val,
+ struct fss_sc_set **sc_set_out);
+};
+
+static int fss_state_retrieve_traverse(struct db_record *rec,
+ void *private_data)
+{
+ NTSTATUS status;
+ struct fss_traverse_state *trv_state
+ = (struct fss_traverse_state *)private_data;
+ TDB_DATA key = dbwrap_record_get_key(rec);
+ TDB_DATA val = dbwrap_record_get_value(rec);
+
+ /* order of checking is important here */
+ if (strstr((char *)key.dptr, FSS_DB_KEY_PFX_SMAP) != NULL) {
+ struct fss_sc_smap *smap;
+ status = trv_state->smap_retrieve(trv_state->mem_ctx,
+ &key, &val, &smap);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ DLIST_ADD_END(trv_state->smaps, smap);
+ trv_state->smaps_count++;
+ } else if (strstr((char *)key.dptr, FSS_DB_KEY_PFX_SC) != NULL) {
+ struct fss_sc *sc;
+ status = trv_state->sc_retrieve(trv_state->mem_ctx,
+ &key, &val, &sc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ DLIST_ADD_END(trv_state->scs, sc);
+ trv_state->scs_count++;
+ } else if (strstr((char *)key.dptr, FSS_DB_KEY_PFX_SC_SET) != NULL) {
+ struct fss_sc_set *sc_set;
+ status = trv_state->sc_set_retrieve(trv_state->mem_ctx,
+ &key, &val, &sc_set);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ DLIST_ADD_END(trv_state->sc_sets, sc_set);
+ trv_state->sc_sets_count++;
+ } else {
+ /* global context and db vers */
+ DEBUG(4, ("Ignoring fss srv db entry with key %s\n", key.dptr));
+ }
+
+ return 0;
+}
+
+static bool fss_state_smap_is_child(struct fss_sc *sc,
+ struct fss_sc_smap *smap)
+{
+ return (strstr(smap->sc_share_name, sc->id_str) != NULL);
+}
+
+static NTSTATUS fss_state_hierarchize_smaps(struct fss_traverse_state *trv_state,
+ struct fss_sc *sc)
+{
+ struct fss_sc_smap *smap;
+ struct fss_sc_smap *smap_n;
+ uint32_t smaps_moved = 0;
+
+ for (smap = trv_state->smaps; smap; smap = smap_n) {
+ smap_n = smap->next;
+ if (!fss_state_smap_is_child(sc, smap))
+ continue;
+
+ /* smap mem should be owned by parent sc */
+ talloc_steal(sc, smap);
+ DLIST_REMOVE(trv_state->smaps, smap);
+ trv_state->smaps_count--;
+ DLIST_ADD_END(sc->smaps, smap);
+ smaps_moved++;
+
+ /* last component of the tdb key path is the sc share name */
+ SMB_ASSERT(strrchr(smap->sc_share_name, '/') != NULL);
+ smap->sc_share_name = strrchr(smap->sc_share_name, '/') + 1;
+ }
+
+ if (sc->smaps_count != smaps_moved) {
+ DEBUG(0, ("Inconsistent smaps_count, expected %u, moved %u\n",
+ sc->smaps_count, smaps_moved));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static bool fss_state_sc_is_child(struct fss_sc_set *sc_set,
+ struct fss_sc *sc)
+{
+ return (strstr(sc->id_str, sc_set->id_str) != NULL);
+}
+
+static NTSTATUS fss_state_hierarchize_scs(struct fss_traverse_state *trv_state,
+ struct fss_sc_set *sc_set)
+{
+ NTSTATUS status;
+ struct fss_sc *sc;
+ struct fss_sc *sc_n;
+ uint32_t scs_moved = 0;
+
+ for (sc = trv_state->scs; sc; sc = sc_n) {
+ sc_n = sc->next;
+ if (!fss_state_sc_is_child(sc_set, sc))
+ continue;
+
+ /* sc mem should be owned by parent sc_set */
+ talloc_steal(sc_set, sc);
+ DLIST_REMOVE(trv_state->scs, sc);
+ trv_state->scs_count--;
+ DLIST_ADD_END(sc_set->scs, sc);
+ scs_moved++;
+
+ sc->sc_set = sc_set;
+
+ /* last component of the tdb key path is the sc GUID str */
+ SMB_ASSERT(strrchr(sc->id_str, '/') != NULL);
+ sc->id_str = strrchr(sc->id_str, '/') + 1;
+
+ status = GUID_from_string(sc->id_str, &sc->id);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+
+ status = fss_state_hierarchize_smaps(trv_state, sc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+ }
+
+ if (sc_set->scs_count != scs_moved) {
+ DEBUG(0, ("Inconsistent scs_count, expected %u, moved %u\n",
+ sc_set->scs_count, scs_moved));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_out;
+ }
+
+ return NT_STATUS_OK;
+
+err_out:
+ return status;
+}
+
+static NTSTATUS fss_state_hierarchize(struct fss_traverse_state *trv_state,
+ struct fss_sc_set **sc_sets,
+ uint32_t *sc_sets_count)
+{
+ NTSTATUS status;
+ struct fss_sc_set *sc_set;
+ struct fss_sc_set *sc_set_n;
+ uint32_t i = 0;
+
+ *sc_sets = NULL;
+ for (sc_set = trv_state->sc_sets; sc_set; sc_set = sc_set_n) {
+ sc_set_n = sc_set->next;
+ /* sc_set mem already owned by trv_state->mem_ctx */
+ DLIST_REMOVE(trv_state->sc_sets, sc_set);
+ trv_state->sc_sets_count--;
+ DLIST_ADD_END(*sc_sets, sc_set);
+ i++;
+
+ /* last component of the tdb key path is the sc_set GUID str */
+ SMB_ASSERT(strrchr(sc_set->id_str, '/') != NULL);
+ sc_set->id_str = strrchr(sc_set->id_str, '/') + 1;
+
+ status = GUID_from_string(sc_set->id_str, &sc_set->id);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+
+ status = fss_state_hierarchize_scs(trv_state, sc_set);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+ }
+ *sc_sets_count = i;
+ return NT_STATUS_OK;
+
+err_out:
+ return status;
+}
+
+_PRIVATE_ NTSTATUS fss_state_retrieve(TALLOC_CTX *mem_ctx,
+ struct fss_sc_set **sc_sets,
+ uint32_t *sc_sets_count,
+ const char *db_path)
+{
+ struct db_context *db;
+ NTSTATUS status;
+ struct fss_traverse_state trv_state;
+ int err;
+ int rec_count;
+ int vers;
+ *sc_sets = NULL;
+ *sc_sets_count = 0;
+
+ memset(&trv_state, 0, sizeof(trv_state));
+ trv_state.mem_ctx = talloc_new(mem_ctx);
+ if (trv_state.mem_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err_out;
+ }
+
+ /* set callbacks for unmarshalling on-disk structures */
+ trv_state.smap_retrieve = fss_state_smap_retrieve;
+ trv_state.sc_retrieve = fss_state_sc_retrieve;
+ trv_state.sc_set_retrieve = fss_state_sc_set_retrieve;
+
+ db = db_open(trv_state.mem_ctx, db_path, 0, TDB_DEFAULT,
+ O_RDONLY, 0600, DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ err = errno;
+ if ((db == NULL) && (err == ENOENT)) {
+ DEBUG(4, ("fss state TDB does not exist for retrieval\n"));
+ status = NT_STATUS_OK;
+ goto err_ts_free;
+ } else if (db == NULL) {
+ DEBUG(0, ("Failed to open fss state TDB: %s\n",
+ strerror(err)));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto err_ts_free;
+ }
+
+ status = dbwrap_fetch_int32_bystring(db, FSS_DB_KEY_VERSION,
+ &vers);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("failed to fetch version from fss state tdb: %s\n",
+ nt_errstr(status)));
+ goto err_db_free;
+ } else if (vers != FSRVP_STATE_DB_VERSION) {
+ DEBUG(0, ("Unsupported fss tdb version %d, expected %d\n",
+ vers, FSRVP_STATE_DB_VERSION));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_db_free;
+ }
+
+ status = dbwrap_traverse_read(db,
+ fss_state_retrieve_traverse,
+ &trv_state,
+ &rec_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_db_free;
+ }
+
+ status = fss_state_hierarchize(&trv_state, sc_sets, sc_sets_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to form fss state hierarchy\n"));
+ goto err_db_free;
+ }
+
+ /* check whether anything was left without a parent */
+ if (trv_state.sc_sets_count != 0) {
+ DEBUG(0, ("%d shadow copy set orphans in %s tdb\n",
+ trv_state.sc_sets_count, db_path));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_db_free;
+ }
+ if (trv_state.scs_count != 0) {
+ DEBUG(0, ("%d shadow copy orphans in %s tdb\n",
+ trv_state.scs_count, db_path));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_db_free;
+ }
+ if (trv_state.smaps_count != 0) {
+ DEBUG(0, ("%d share map orphans in %s tdb\n",
+ trv_state.smaps_count, db_path));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto err_db_free;
+ }
+ talloc_free(db);
+
+ return NT_STATUS_OK;
+
+err_db_free:
+ talloc_free(db);
+err_ts_free:
+ talloc_free(trv_state.mem_ctx);
+err_out:
+ return status;
+}
diff --git a/source3/rpc_server/initshutdown/srv_initshutdown_nt.c b/source3/rpc_server/initshutdown/srv_initshutdown_nt.c
new file mode 100644
index 0000000..da32cdf
--- /dev/null
+++ b/source3/rpc_server/initshutdown/srv_initshutdown_nt.c
@@ -0,0 +1,84 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997.
+ * Copyright (C) Gerald Carter 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/>.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/gen_ndr/ndr_initshutdown.h"
+#include "librpc/gen_ndr/ndr_initshutdown_scompat.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "librpc/gen_ndr/ndr_winreg_scompat.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+
+/*******************************************************************
+ ********************************************************************/
+WERROR _initshutdown_Init(struct pipes_struct *p, struct initshutdown_Init *r)
+{
+ struct winreg_InitiateSystemShutdownEx s;
+
+ s.in.hostname = r->in.hostname;
+ s.in.message = r->in.message;
+ s.in.timeout = r->in.timeout;
+ s.in.force_apps = r->in.force_apps;
+ s.in.do_reboot = r->in.do_reboot;
+ s.in.reason = 0;
+
+ /* thunk down to _winreg_InitiateSystemShutdownEx()
+ (just returns a status) */
+
+ return _winreg_InitiateSystemShutdownEx( p, &s );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _initshutdown_InitEx(struct pipes_struct *p, struct initshutdown_InitEx *r)
+{
+ struct winreg_InitiateSystemShutdownEx s;
+ s.in.hostname = r->in.hostname;
+ s.in.message = r->in.message;
+ s.in.timeout = r->in.timeout;
+ s.in.force_apps = r->in.force_apps;
+ s.in.do_reboot = r->in.do_reboot;
+ s.in.reason = r->in.reason;
+
+ return _winreg_InitiateSystemShutdownEx( p, &s);
+}
+
+
+
+
+/*******************************************************************
+ reg_abort_shutdwon
+ ********************************************************************/
+
+WERROR _initshutdown_Abort(struct pipes_struct *p, struct initshutdown_Abort *r)
+{
+ struct winreg_AbortSystemShutdown s;
+ s.in.server = r->in.server;
+ return _winreg_AbortSystemShutdown( p, &s );
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_initshutdown_scompat.c"
diff --git a/source3/rpc_server/lsa/srv_lsa_nt.c b/source3/rpc_server/lsa/srv_lsa_nt.c
new file mode 100644
index 0000000..e2078c6
--- /dev/null
+++ b/source3/rpc_server/lsa/srv_lsa_nt.c
@@ -0,0 +1,5235 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Jeremy Allison 2001, 2006.
+ * Copyright (C) Rafal Szczesniak 2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002,
+ * Copyright (C) Simo Sorce 2003.
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ * Copyright (C) Volker Lendecke 2005.
+ * Copyright (C) Guenther Deschner 2008.
+ * Copyright (C) Andrew Bartlett 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This is the implementation of the lsa server code. */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_scompat.h"
+#include "secrets.h"
+#include "../librpc/gen_ndr/netlogon.h"
+#include "rpc_client/init_lsa.h"
+#include "../libcli/security/security.h"
+#include "../libcli/security/dom_sid.h"
+#include "../librpc/gen_ndr/drsblobs.h"
+#include "../librpc/gen_ndr/ndr_drsblobs.h"
+#include "../libcli/security/dom_sid.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "passdb.h"
+#include "auth.h"
+#include "lib/privileges.h"
+#include "rpc_server/srv_access_check.h"
+#include "../librpc/gen_ndr/ndr_wkssvc.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../libcli/lsarpc/util_lsarpc.h"
+#include "lsa.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/rpc/dcerpc_helper.h"
+#include "lib/param/loadparm.h"
+#include "source3/lib/substitute.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_LOOKUP_SIDS 0x5000 /* 20480 */
+
+enum lsa_handle_type {
+ LSA_HANDLE_POLICY_TYPE = 1,
+ LSA_HANDLE_ACCOUNT_TYPE = 2,
+ LSA_HANDLE_TRUST_TYPE = 3,
+ LSA_HANDLE_SECRET_TYPE = 4};
+
+struct lsa_info {
+ struct dom_sid sid;
+ const char *name;
+ uint32_t access;
+ enum lsa_handle_type type;
+ struct security_descriptor *sd;
+};
+
+const struct generic_mapping lsa_account_mapping = {
+ LSA_ACCOUNT_READ,
+ LSA_ACCOUNT_WRITE,
+ LSA_ACCOUNT_EXECUTE,
+ LSA_ACCOUNT_ALL_ACCESS
+};
+
+const struct generic_mapping lsa_policy_mapping = {
+ LSA_POLICY_READ,
+ LSA_POLICY_WRITE,
+ LSA_POLICY_EXECUTE,
+ LSA_POLICY_ALL_ACCESS
+};
+
+const struct generic_mapping lsa_secret_mapping = {
+ LSA_SECRET_READ,
+ LSA_SECRET_WRITE,
+ LSA_SECRET_EXECUTE,
+ LSA_SECRET_ALL_ACCESS
+};
+
+const struct generic_mapping lsa_trusted_domain_mapping = {
+ LSA_TRUSTED_DOMAIN_READ,
+ LSA_TRUSTED_DOMAIN_WRITE,
+ LSA_TRUSTED_DOMAIN_EXECUTE,
+ LSA_TRUSTED_DOMAIN_ALL_ACCESS
+};
+
+/***************************************************************************
+ initialize a lsa_DomainInfo structure.
+ ***************************************************************************/
+
+static void init_dom_query_3(struct lsa_DomainInfo *r,
+ const char *name,
+ struct dom_sid *sid)
+{
+ init_lsa_StringLarge(&r->name, name);
+ r->sid = sid;
+}
+
+/***************************************************************************
+ initialize a lsa_DomainInfo structure.
+ ***************************************************************************/
+
+static void init_dom_query_5(struct lsa_DomainInfo *r,
+ const char *name,
+ struct dom_sid *sid)
+{
+ init_lsa_StringLarge(&r->name, name);
+ r->sid = sid;
+}
+
+/***************************************************************************
+ lookup_lsa_rids. Must be called as root for lookup_name to work.
+ ***************************************************************************/
+
+static NTSTATUS lookup_lsa_rids(TALLOC_CTX *mem_ctx,
+ struct lsa_RefDomainList *ref,
+ struct lsa_TranslatedSid *prid,
+ uint32_t num_entries,
+ struct lsa_String *name,
+ int flags,
+ uint32_t *pmapped_count)
+{
+ uint32_t mapped_count, i;
+
+ SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+ mapped_count = 0;
+ *pmapped_count = 0;
+
+ for (i = 0; i < num_entries; i++) {
+ struct dom_sid sid;
+ uint32_t rid;
+ int dom_idx;
+ const char *full_name;
+ const char *domain;
+ enum lsa_SidType type;
+
+ /* Split name into domain and user component */
+
+ /* follow w2k8 behavior and return the builtin domain when no
+ * input has been passed in */
+
+ if (name[i].string) {
+ full_name = name[i].string;
+ } else {
+ full_name = "BUILTIN";
+ }
+
+ DEBUG(5, ("lookup_lsa_rids: looking up name %s\n", full_name));
+
+ if (!lookup_name(mem_ctx, full_name, flags, &domain, NULL,
+ &sid, &type)) {
+ type = SID_NAME_UNKNOWN;
+ }
+
+ switch (type) {
+ case SID_NAME_USER:
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_DOMAIN:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ DEBUG(5, ("init_lsa_rids: %s found\n", full_name));
+ /* Leave these unchanged */
+ break;
+ default:
+ /* Don't hand out anything but the list above */
+ DEBUG(5, ("init_lsa_rids: %s not found\n", full_name));
+ type = SID_NAME_UNKNOWN;
+ break;
+ }
+
+ rid = 0;
+ dom_idx = -1;
+
+ if (type != SID_NAME_UNKNOWN) {
+ if (type == SID_NAME_DOMAIN) {
+ rid = (uint32_t)-1;
+ } else {
+ sid_split_rid(&sid, &rid);
+ }
+ dom_idx = init_lsa_ref_domain_list(mem_ctx, ref, domain, &sid);
+ mapped_count++;
+ }
+
+ prid[i].sid_type = type;
+ prid[i].rid = rid;
+ prid[i].sid_index = dom_idx;
+ }
+
+ *pmapped_count = mapped_count;
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ lookup_lsa_sids. Must be called as root for lookup_name to work.
+ ***************************************************************************/
+
+static NTSTATUS lookup_lsa_sids(TALLOC_CTX *mem_ctx,
+ struct lsa_RefDomainList *ref,
+ struct lsa_TranslatedSid3 *trans_sids,
+ uint32_t num_entries,
+ struct lsa_String *name,
+ int flags,
+ uint32_t *pmapped_count)
+{
+ uint32_t mapped_count, i;
+
+ SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+ mapped_count = 0;
+ *pmapped_count = 0;
+
+ for (i = 0; i < num_entries; i++) {
+ struct dom_sid sid;
+ uint32_t rid;
+ int dom_idx;
+ const char *full_name;
+ const char *domain;
+ enum lsa_SidType type;
+
+ ZERO_STRUCT(sid);
+
+ /* Split name into domain and user component */
+
+ full_name = name[i].string;
+ if (full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5, ("lookup_lsa_sids: looking up name %s\n", full_name));
+
+ if (!lookup_name(mem_ctx, full_name, flags, &domain, NULL,
+ &sid, &type)) {
+ type = SID_NAME_UNKNOWN;
+ }
+
+ switch (type) {
+ case SID_NAME_USER:
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_DOMAIN:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ DEBUG(5, ("lookup_lsa_sids: %s found\n", full_name));
+ /* Leave these unchanged */
+ break;
+ default:
+ /* Don't hand out anything but the list above */
+ DEBUG(5, ("lookup_lsa_sids: %s not found\n", full_name));
+ type = SID_NAME_UNKNOWN;
+ break;
+ }
+
+ rid = 0;
+ dom_idx = -1;
+
+ if (type != SID_NAME_UNKNOWN) {
+ struct dom_sid domain_sid;
+ sid_copy(&domain_sid, &sid);
+ sid_split_rid(&domain_sid, &rid);
+ dom_idx = init_lsa_ref_domain_list(mem_ctx, ref, domain, &domain_sid);
+ mapped_count++;
+ }
+
+ /* Initialize the lsa_TranslatedSid3 return. */
+ trans_sids[i].sid_type = type;
+ trans_sids[i].sid = dom_sid_dup(mem_ctx, &sid);
+ trans_sids[i].sid_index = dom_idx;
+ }
+
+ *pmapped_count = mapped_count;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS make_lsa_object_sd(TALLOC_CTX *mem_ctx, struct security_descriptor **sd, size_t *sd_size,
+ const struct generic_mapping *map,
+ struct dom_sid *sid, uint32_t sid_access)
+{
+ struct dom_sid adm_sid;
+ struct security_ace ace[5];
+ size_t i = 0;
+
+ struct security_acl *psa = NULL;
+
+ /* READ|EXECUTE access for Everyone */
+
+ init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ map->generic_execute | map->generic_read, 0);
+
+ /* Add Full Access 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */
+
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Account_Operators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0);
+
+ /* Add Full Access for Domain Admins */
+ sid_compose(&adm_sid, get_global_sam_sid(), DOMAIN_RID_ADMINS);
+ init_sec_ace(&ace[i++], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ map->generic_all, 0);
+
+ /* If we have a sid, give it some special access */
+
+ if (sid) {
+ init_sec_ace(&ace[i++], sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ sid_access, 0);
+ }
+
+ if((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, i, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if((*sd = make_sec_desc(mem_ctx, SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE, &adm_sid, NULL, NULL,
+ psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+static NTSTATUS create_lsa_policy_handle(TALLOC_CTX *mem_ctx,
+ struct pipes_struct *p,
+ enum lsa_handle_type type,
+ uint32_t acc_granted,
+ struct dom_sid *sid,
+ const char *name,
+ const struct security_descriptor *sd,
+ struct policy_handle *handle)
+{
+ struct lsa_info *info;
+
+ ZERO_STRUCTP(handle);
+
+ info = talloc_zero(mem_ctx, struct lsa_info);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->type = type;
+ info->access = acc_granted;
+
+ if (sid) {
+ sid_copy(&info->sid, sid);
+ }
+
+ info->name = talloc_strdup(info, name);
+
+ if (sd != NULL) {
+ info->sd = security_descriptor_copy(info, sd);
+ if (info->sd == NULL) {
+ talloc_free(info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!create_policy_hnd(p, handle, type, info)) {
+ talloc_free(info);
+ ZERO_STRUCTP(handle);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_OpenPolicy2
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenPolicy2(struct pipes_struct *p,
+ struct lsa_OpenPolicy2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd = NULL;
+ size_t sd_size;
+ uint32_t des_access = r->in.access_mask;
+ uint32_t acc_granted;
+ NTSTATUS status;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_policy_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size, &lsa_policy_mapping,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, des_access,
+ &acc_granted, "_lsa_OpenPolicy2" );
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_POLICY_TYPE,
+ acc_granted,
+ get_global_sam_sid(),
+ NULL,
+ psd,
+ r->out.handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_OpenPolicy
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenPolicy(struct pipes_struct *p,
+ struct lsa_OpenPolicy *r)
+{
+ struct lsa_OpenPolicy2 o;
+
+ /* _lsa_OpenPolicy2 will check if this is a NCACN_NP connection */
+
+ o.in.system_name = NULL; /* should be ignored */
+ o.in.attr = r->in.attr;
+ o.in.access_mask = r->in.access_mask;
+
+ o.out.handle = r->out.handle;
+
+ return _lsa_OpenPolicy2(p, &o);
+}
+
+/***************************************************************************
+ _lsa_EnumTrustDom - this needs fixing to do more than return NULL ! JRA.
+ ufff, done :) mimir
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumTrustDom(struct pipes_struct *p,
+ struct lsa_EnumTrustDom *r)
+{
+ struct lsa_info *info;
+ uint32_t i, count;
+ struct trustdom_info **domains;
+ struct lsa_DomainInfo *entries;
+ NTSTATUS nt_status;
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &nt_status);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+ if (!(info->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ become_root();
+ nt_status = pdb_enum_trusteddoms(p->mem_ctx, &count, &domains);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ entries = talloc_zero_array(p->mem_ctx, struct lsa_DomainInfo, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<count; i++) {
+ init_lsa_StringLarge(&entries[i].name, domains[i]->name);
+ entries[i].sid = &domains[i]->sid;
+ }
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+ TALLOC_FREE(entries);
+ 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;
+
+ 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;
+ }
+
+ /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST
+ * always be larger than the previous input resume handle, in
+ * particular when hitting the last query it is vital to set the
+ * resume handle correctly to avoid infinite client loops, as
+ * seen e.g. with Windows XP SP3 when resume handle is 0 and
+ * status is NT_STATUS_OK - gd */
+
+ *r->out.resume_handle = (uint32_t)-1;
+
+ return NT_STATUS_OK;
+}
+
+#define LSA_AUDIT_NUM_CATEGORIES_NT4 7
+#define LSA_AUDIT_NUM_CATEGORIES_WIN2K 9
+#define LSA_AUDIT_NUM_CATEGORIES LSA_AUDIT_NUM_CATEGORIES_NT4
+
+/***************************************************************************
+ _lsa_QueryInfoPolicy
+ ***************************************************************************/
+
+NTSTATUS _lsa_QueryInfoPolicy(struct pipes_struct *p,
+ struct lsa_QueryInfoPolicy *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct lsa_info *handle;
+ struct dom_sid domain_sid;
+ const char *name;
+ struct dom_sid *sid = NULL;
+ union lsa_PolicyInformation *info = NULL;
+ uint32_t acc_required = 0;
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (r->in.level) {
+ case LSA_POLICY_INFO_AUDIT_LOG:
+ case LSA_POLICY_INFO_AUDIT_EVENTS:
+ acc_required = LSA_POLICY_VIEW_AUDIT_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_DOMAIN:
+ acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_PD:
+ acc_required = LSA_POLICY_GET_PRIVATE_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_ROLE:
+ case LSA_POLICY_INFO_REPLICA:
+ acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_QUOTA:
+ acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_MOD:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ /* according to MS-LSAD 3.1.4.4.3 */
+ return NT_STATUS_INVALID_PARAMETER;
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ acc_required = LSA_POLICY_VIEW_AUDIT_INFORMATION;
+ break;
+ case LSA_POLICY_INFO_DNS:
+ case LSA_POLICY_INFO_DNS_INT:
+ case LSA_POLICY_INFO_L_ACCOUNT_DOMAIN:
+ acc_required = LSA_POLICY_VIEW_LOCAL_INFORMATION;
+ break;
+ default:
+ break;
+ }
+
+ if (!(handle->access & acc_required)) {
+ /* return NT_STATUS_ACCESS_DENIED; */
+ }
+
+ info = talloc_zero(p->mem_ctx, union lsa_PolicyInformation);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ /* according to MS-LSAD 3.1.4.4.3 */
+ case LSA_POLICY_INFO_MOD:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ return NT_STATUS_INVALID_PARAMETER;
+ case LSA_POLICY_INFO_AUDIT_LOG:
+ info->audit_log.percent_full = 0;
+ info->audit_log.maximum_log_size = 0;
+ info->audit_log.retention_time = 0;
+ info->audit_log.shutdown_in_progress = 0;
+ info->audit_log.time_to_shutdown = 0;
+ info->audit_log.next_audit_record = 0;
+ status = NT_STATUS_OK;
+ break;
+ case LSA_POLICY_INFO_PD:
+ info->pd.name.string = NULL;
+ status = NT_STATUS_OK;
+ break;
+ case LSA_POLICY_INFO_REPLICA:
+ info->replica.source.string = NULL;
+ info->replica.account.string = NULL;
+ status = NT_STATUS_OK;
+ break;
+ case LSA_POLICY_INFO_QUOTA:
+ info->quota.paged_pool = 0;
+ info->quota.non_paged_pool = 0;
+ info->quota.min_wss = 0;
+ info->quota.max_wss = 0;
+ info->quota.pagefile = 0;
+ info->quota.unknown = 0;
+ status = NT_STATUS_OK;
+ break;
+ case LSA_POLICY_INFO_AUDIT_EVENTS:
+ {
+
+ uint32_t policy_def = LSA_AUDIT_POLICY_ALL;
+
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_VIEW_AUDIT_INFORMATION)) {
+ DEBUG(10,("_lsa_QueryInfoPolicy: insufficient access rights\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* fake info: We audit everything. ;) */
+
+ info->audit_events.auditing_mode = true;
+ info->audit_events.count = LSA_AUDIT_NUM_CATEGORIES;
+ info->audit_events.settings = talloc_zero_array(p->mem_ctx,
+ enum lsa_PolicyAuditPolicy,
+ info->audit_events.count);
+ if (!info->audit_events.settings) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_ACCOUNT_MANAGEMENT] = policy_def;
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_FILE_AND_OBJECT_ACCESS] = policy_def;
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_LOGON] = policy_def;
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_PROCCESS_TRACKING] = policy_def;
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_SECURITY_POLICY_CHANGES] = policy_def;
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_SYSTEM] = policy_def;
+ info->audit_events.settings[LSA_AUDIT_CATEGORY_USE_OF_USER_RIGHTS] = policy_def;
+
+ break;
+ }
+ case LSA_POLICY_INFO_DOMAIN:
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyPrimaryDomainInformation. */
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ case ROLE_IPA_DC:
+ name = get_global_sam_name();
+ sid = dom_sid_dup(p->mem_ctx, get_global_sam_sid());
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ name = lp_workgroup();
+ /* We need to return the Domain SID here. */
+ if (secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) {
+ sid = dom_sid_dup(p->mem_ctx, &domain_sid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ break;
+ case ROLE_STANDALONE:
+ name = lp_workgroup();
+ sid = NULL;
+ break;
+ default:
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ init_dom_query_3(&info->domain, name, sid);
+ break;
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Request PolicyAccountDomainInformation. */
+ name = get_global_sam_name();
+ sid = get_global_sam_sid();
+
+ init_dom_query_5(&info->account_domain, name, sid);
+ break;
+ case LSA_POLICY_INFO_ROLE:
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_BDC:
+ /*
+ * only a BDC is a backup controller
+ * of the domain, it controls.
+ */
+ info->role.role = LSA_ROLE_BACKUP;
+ break;
+ default:
+ /*
+ * any other role is a primary
+ * of the domain, it controls.
+ */
+ info->role.role = LSA_ROLE_PRIMARY;
+ break;
+ }
+ break;
+ case LSA_POLICY_INFO_DNS:
+ case LSA_POLICY_INFO_DNS_INT: {
+ struct pdb_domain_info *dominfo;
+
+ if ((pdb_capabilities() & PDB_CAP_ADS) == 0) {
+ DEBUG(10, ("Not replying to LSA_POLICY_INFO_DNS "
+ "without ADS passdb backend\n"));
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ dominfo = pdb_get_domain_info(info);
+ if (dominfo == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ break;
+ }
+
+ init_lsa_StringLarge(&info->dns.name,
+ dominfo->name);
+ init_lsa_StringLarge(&info->dns.dns_domain,
+ dominfo->dns_domain);
+ init_lsa_StringLarge(&info->dns.dns_forest,
+ dominfo->dns_forest);
+ info->dns.domain_guid = dominfo->guid;
+ info->dns.sid = &dominfo->sid;
+ break;
+ }
+ default:
+ DEBUG(0,("_lsa_QueryInfoPolicy: unknown info level in Lsa Query: %d\n",
+ r->in.level));
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ *r->out.info = info;
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_QueryInfoPolicy2
+ ***************************************************************************/
+
+NTSTATUS _lsa_QueryInfoPolicy2(struct pipes_struct *p,
+ struct lsa_QueryInfoPolicy2 *r2)
+{
+ struct lsa_QueryInfoPolicy r;
+
+ if ((pdb_capabilities() & PDB_CAP_ADS) == 0) {
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ ZERO_STRUCT(r);
+ r.in.handle = r2->in.handle;
+ r.in.level = r2->in.level;
+ r.out.info = r2->out.info;
+
+ return _lsa_QueryInfoPolicy(p, &r);
+}
+
+/***************************************************************************
+ _lsa_lookup_sids_internal
+ ***************************************************************************/
+
+static NTSTATUS _lsa_lookup_sids_internal(struct pipes_struct *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level, /* input */
+ int num_sids, /* input */
+ struct lsa_SidPtr *sid, /* input */
+ struct lsa_RefDomainList **pp_ref, /* input/output */
+ struct lsa_TranslatedName2 **pp_names,/* input/output */
+ uint32_t *pp_mapped_count) /* input/output */
+{
+ NTSTATUS status;
+ int i;
+ const struct dom_sid **sids = NULL;
+ struct lsa_RefDomainList *ref = NULL;
+ uint32_t mapped_count = 0;
+ struct lsa_dom_info *dom_infos = NULL;
+ struct lsa_name_info *name_infos = NULL;
+ struct lsa_TranslatedName2 *names = NULL;
+
+ *pp_mapped_count = 0;
+ *pp_names = NULL;
+ *pp_ref = NULL;
+
+ if (num_sids == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sids = talloc_array(p->mem_ctx, const struct dom_sid *, num_sids);
+ ref = talloc_zero(p->mem_ctx, struct lsa_RefDomainList);
+
+ if (sids == NULL || ref == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ sids[i] = sid[i].sid;
+ }
+
+ status = lookup_sids(p->mem_ctx, num_sids, sids, level,
+ &dom_infos, &name_infos);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ names = talloc_array(p->mem_ctx, struct lsa_TranslatedName2, num_sids);
+ if (names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<LSA_REF_DOMAIN_LIST_MULTIPLIER; i++) {
+
+ if (!dom_infos[i].valid) {
+ break;
+ }
+
+ if (init_lsa_ref_domain_list(mem_ctx, ref,
+ dom_infos[i].name,
+ &dom_infos[i].sid) != i) {
+ DEBUG(0, ("Domain %s mentioned twice??\n",
+ dom_infos[i].name));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ for (i=0; i<num_sids; i++) {
+ struct lsa_name_info *name = &name_infos[i];
+
+ if (name->type == SID_NAME_UNKNOWN) {
+ name->dom_idx = -1;
+ /* Unknown sids should return the string
+ * representation of the SID. Windows 2003 behaves
+ * rather erratic here, in many cases it returns the
+ * RID as 8 bytes hex, in others it returns the full
+ * SID. We (Jerry/VL) could not figure out which the
+ * hard cases are, so leave it with the SID. */
+ name->name = dom_sid_string(p->mem_ctx, sids[i]);
+ if (name->name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ mapped_count += 1;
+ }
+
+ names[i].sid_type = name->type;
+ names[i].name.string = name->name;
+ names[i].sid_index = name->dom_idx;
+ names[i].unknown = 0;
+ }
+
+ status = NT_STATUS_NONE_MAPPED;
+ if (mapped_count > 0) {
+ status = (mapped_count < num_sids) ?
+ STATUS_SOME_UNMAPPED : NT_STATUS_OK;
+ }
+
+ DEBUG(10, ("num_sids %d, mapped_count %d, status %s\n",
+ num_sids, mapped_count, nt_errstr(status)));
+
+ *pp_mapped_count = mapped_count;
+ *pp_names = names;
+ *pp_ref = ref;
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_LookupSids
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupSids(struct pipes_struct *p,
+ struct lsa_LookupSids *r)
+{
+ NTSTATUS status;
+ struct lsa_info *handle;
+ int num_sids = r->in.sids->num_sids;
+ uint32_t mapped_count = 0;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedName *names_out = NULL;
+ struct lsa_TranslatedName2 *names = NULL;
+ int i;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((r->in.level < 1) || (r->in.level > 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (num_sids > MAX_LOOKUP_SIDS) {
+ DEBUG(5,("_lsa_LookupSids: limit of %d exceeded, requested %d\n",
+ MAX_LOOKUP_SIDS, num_sids));
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = _lsa_lookup_sids_internal(p,
+ p->mem_ctx,
+ r->in.level,
+ num_sids,
+ r->in.sids->sids,
+ &domains,
+ &names,
+ &mapped_count);
+
+ /* Only return here when there is a real error.
+ NT_STATUS_NONE_MAPPED is a special case as it indicates that none of
+ the requested sids could be resolved. Older versions of XP (pre SP3)
+ rely that we return with the string representations of those SIDs in
+ that case. If we don't, XP crashes - Guenther
+ */
+
+ if (NT_STATUS_IS_ERR(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ return status;
+ }
+
+ /* Convert from lsa_TranslatedName2 to lsa_TranslatedName */
+ names_out = talloc_array(p->mem_ctx, struct lsa_TranslatedName,
+ num_sids);
+ if (!names_out) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ names_out[i].sid_type = names[i].sid_type;
+ names_out[i].name = names[i].name;
+ names_out[i].sid_index = names[i].sid_index;
+ }
+
+ *r->out.domains = domains;
+ r->out.names->count = num_sids;
+ r->out.names->names = names_out;
+ *r->out.count = mapped_count;
+
+ return status;
+}
+
+static NTSTATUS _lsa_LookupSids_common(struct pipes_struct *p,
+ struct lsa_LookupSids2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ NTSTATUS status;
+ struct lsa_info *handle;
+ int num_sids = r->in.sids->num_sids;
+ uint32_t mapped_count = 0;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedName2 *names = NULL;
+ bool check_policy = true;
+
+ switch (dce_call->pkt.u.request.opnum) {
+ case NDR_LSA_LOOKUPSIDS3:
+ check_policy = false;
+ break;
+ case NDR_LSA_LOOKUPSIDS2:
+ default:
+ check_policy = true;
+ }
+
+ if ((r->in.level < 1) || (r->in.level > 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (check_policy) {
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (num_sids > MAX_LOOKUP_SIDS) {
+ DEBUG(5,("_lsa_LookupSids2: limit of %d exceeded, requested %d\n",
+ MAX_LOOKUP_SIDS, num_sids));
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = _lsa_lookup_sids_internal(p,
+ p->mem_ctx,
+ r->in.level,
+ num_sids,
+ r->in.sids->sids,
+ &domains,
+ &names,
+ &mapped_count);
+
+ *r->out.domains = domains;
+ r->out.names->count = num_sids;
+ r->out.names->names = names;
+ *r->out.count = mapped_count;
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_LookupSids2
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupSids2(struct pipes_struct *p,
+ struct lsa_LookupSids2 *r)
+{
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return _lsa_LookupSids_common(p, r);
+}
+
+/***************************************************************************
+ _lsa_LookupSids3
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupSids3(struct pipes_struct *p,
+ struct lsa_LookupSids3 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+ struct lsa_LookupSids2 q;
+
+ if (p->transport != NCACN_IP_TCP) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ dcesrv_call_auth_info(dce_call, &auth_type, &auth_level);
+
+ /* No policy handle on this call. Restrict to crypto connections. */
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL ||
+ auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
+ DEBUG(1, ("_lsa_LookupSids3: The client %s is not using "
+ "a secure connection over netlogon\n",
+ get_remote_machine_name() ));
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ q.in.handle = NULL;
+ q.in.sids = r->in.sids;
+ q.in.level = r->in.level;
+ q.in.lookup_options = r->in.lookup_options;
+ q.in.client_revision = r->in.client_revision;
+ q.in.names = r->in.names;
+ q.in.count = r->in.count;
+
+ q.out.domains = r->out.domains;
+ q.out.names = r->out.names;
+ q.out.count = r->out.count;
+
+ return _lsa_LookupSids_common(p, &q);
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+static int lsa_lookup_level_to_flags(enum lsa_LookupNamesLevel level)
+{
+ int flags;
+
+ switch (level) {
+ case LSA_LOOKUP_NAMES_ALL: /* 1 */
+ flags = LOOKUP_NAME_ALL;
+ break;
+ case LSA_LOOKUP_NAMES_DOMAINS_ONLY: /* 2 */
+ flags = LOOKUP_NAME_DOMAIN|LOOKUP_NAME_REMOTE|LOOKUP_NAME_ISOLATED;
+ break;
+ case LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY: /* 3 */
+ flags = LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED;
+ break;
+ case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY: /* 4 */
+ case LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY: /* 5 */
+ case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2: /* 6 */
+ case LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC: /* 7 */
+ default:
+ flags = LOOKUP_NAME_NONE;
+ break;
+ }
+
+ return flags;
+}
+
+/***************************************************************************
+ _lsa_LookupNames
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames(struct pipes_struct *p,
+ struct lsa_LookupNames *r)
+{
+ NTSTATUS status = NT_STATUS_NONE_MAPPED;
+ struct lsa_info *handle;
+ struct lsa_String *names = r->in.names;
+ uint32_t num_entries = r->in.num_names;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedSid *rids = NULL;
+ uint32_t mapped_count = 0;
+ int flags = 0;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (num_entries > MAX_LOOKUP_SIDS) {
+ num_entries = MAX_LOOKUP_SIDS;
+ DEBUG(5,("_lsa_LookupNames: truncating name lookup list to %d\n",
+ num_entries));
+ }
+
+ flags = lsa_lookup_level_to_flags(r->in.level);
+
+ domains = talloc_zero(p->mem_ctx, struct lsa_RefDomainList);
+ if (!domains) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (num_entries) {
+ rids = talloc_zero_array(p->mem_ctx, struct lsa_TranslatedSid,
+ num_entries);
+ if (!rids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ rids = NULL;
+ }
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* set up the LSA Lookup RIDs response */
+ become_root(); /* lookup_name can require root privs */
+ status = lookup_lsa_rids(p->mem_ctx, domains, rids, num_entries,
+ names, flags, &mapped_count);
+ unbecome_root();
+
+done:
+
+ if (NT_STATUS_IS_OK(status) && (num_entries != 0) ) {
+ if (mapped_count == 0) {
+ status = NT_STATUS_NONE_MAPPED;
+ } else if (mapped_count != num_entries) {
+ status = STATUS_SOME_UNMAPPED;
+ }
+ }
+
+ *r->out.count = mapped_count;
+ *r->out.domains = domains;
+ r->out.sids->sids = rids;
+ r->out.sids->count = num_entries;
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_LookupNames2
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames2(struct pipes_struct *p,
+ struct lsa_LookupNames2 *r)
+{
+ NTSTATUS status;
+ struct lsa_LookupNames q;
+ struct lsa_TransSidArray2 *sid_array2 = r->in.sids;
+ struct lsa_TransSidArray *sid_array = NULL;
+ uint32_t i;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sid_array = talloc_zero(p->mem_ctx, struct lsa_TransSidArray);
+ if (!sid_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ q.in.handle = r->in.handle;
+ q.in.num_names = r->in.num_names;
+ q.in.names = r->in.names;
+ q.in.level = r->in.level;
+ q.in.sids = sid_array;
+ q.in.count = r->in.count;
+ /* we do not know what this is for */
+ /* = r->in.unknown1; */
+ /* = r->in.unknown2; */
+
+ q.out.domains = r->out.domains;
+ q.out.sids = sid_array;
+ q.out.count = r->out.count;
+
+ status = _lsa_LookupNames(p, &q);
+
+ sid_array2->count = sid_array->count;
+ sid_array2->sids = talloc_array(p->mem_ctx, struct lsa_TranslatedSid2, sid_array->count);
+ if (!sid_array2->sids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<sid_array->count; i++) {
+ sid_array2->sids[i].sid_type = sid_array->sids[i].sid_type;
+ sid_array2->sids[i].rid = sid_array->sids[i].rid;
+ sid_array2->sids[i].sid_index = sid_array->sids[i].sid_index;
+ sid_array2->sids[i].unknown = 0;
+ }
+
+ r->out.sids = sid_array2;
+
+ return status;
+}
+
+static NTSTATUS _lsa_LookupNames_common(struct pipes_struct *p,
+ struct lsa_LookupNames3 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ NTSTATUS status;
+ struct lsa_info *handle;
+ struct lsa_String *names = r->in.names;
+ uint32_t num_entries = r->in.num_names;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedSid3 *trans_sids = NULL;
+ uint32_t mapped_count = 0;
+ int flags = 0;
+ bool check_policy = true;
+
+ switch (dce_call->pkt.u.request.opnum) {
+ case NDR_LSA_LOOKUPNAMES4:
+ check_policy = false;
+ break;
+ case NDR_LSA_LOOKUPNAMES3:
+ default:
+ check_policy = true;
+ }
+
+ if (num_entries > MAX_LOOKUP_SIDS) {
+ num_entries = MAX_LOOKUP_SIDS;
+ DEBUG(5,("_lsa_LookupNames3: truncating name lookup list to %d\n", num_entries));
+ }
+
+ flags = lsa_lookup_level_to_flags(r->in.level);
+
+ domains = talloc_zero(p->mem_ctx, struct lsa_RefDomainList);
+ if (!domains) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (num_entries) {
+ trans_sids = talloc_zero_array(p->mem_ctx, struct lsa_TranslatedSid3,
+ num_entries);
+ if (!trans_sids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ trans_sids = NULL;
+ }
+
+ if (check_policy) {
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_LOOKUP_NAMES)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ }
+
+ /* set up the LSA Lookup SIDs response */
+ become_root(); /* lookup_name can require root privs */
+ status = lookup_lsa_sids(p->mem_ctx, domains, trans_sids, num_entries,
+ names, flags, &mapped_count);
+ unbecome_root();
+
+done:
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (mapped_count == 0) {
+ status = NT_STATUS_NONE_MAPPED;
+ } else if (mapped_count != num_entries) {
+ status = STATUS_SOME_UNMAPPED;
+ }
+ }
+
+ *r->out.count = mapped_count;
+ *r->out.domains = domains;
+ r->out.sids->sids = trans_sids;
+ r->out.sids->count = num_entries;
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_LookupNames3
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames3(struct pipes_struct *p,
+ struct lsa_LookupNames3 *r)
+{
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return _lsa_LookupNames_common(p, r);
+}
+
+/***************************************************************************
+ _lsa_LookupNames4
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames4(struct pipes_struct *p,
+ struct lsa_LookupNames4 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+ struct lsa_LookupNames3 q;
+
+ if (p->transport != NCACN_IP_TCP) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ dcesrv_call_auth_info(dce_call, &auth_type, &auth_level);
+
+ /* No policy handle on this call. Restrict to crypto connections. */
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL ||
+ auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
+ DEBUG(1, ("_lsa_LookupNames4: The client %s is not using "
+ "a secure connection over netlogon\n",
+ get_remote_machine_name()));
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ q.in.handle = NULL;
+ q.in.num_names = r->in.num_names;
+ q.in.names = r->in.names;
+ q.in.level = r->in.level;
+ q.in.lookup_options = r->in.lookup_options;
+ q.in.client_revision = r->in.client_revision;
+ q.in.sids = r->in.sids;
+ q.in.count = r->in.count;
+
+ q.out.domains = r->out.domains;
+ q.out.sids = r->out.sids;
+ q.out.count = r->out.count;
+
+ return _lsa_LookupNames_common(p, &q);
+}
+
+/***************************************************************************
+ _lsa_close. Also weird - needs to check if lsa handle is correct. JRA.
+ ***************************************************************************/
+
+NTSTATUS _lsa_Close(struct pipes_struct *p, struct lsa_Close *r)
+{
+ NTSTATUS status;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ DCESRV_HANDLE_ANY,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ close_policy_hnd(p, r->in.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+static NTSTATUS lsa_lookup_trusted_domain_by_sid(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ struct trustdom_info **info)
+{
+ NTSTATUS status;
+ uint32_t num_domains = 0;
+ struct trustdom_info **domains = NULL;
+ int i;
+
+ status = pdb_enum_trusteddoms(mem_ctx, &num_domains, &domains);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (i=0; i < num_domains; i++) {
+ if (dom_sid_equal(&domains[i]->sid, sid)) {
+ break;
+ }
+ }
+
+ if (i == num_domains) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *info = domains[i];
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+static NTSTATUS lsa_lookup_trusted_domain_by_name(TALLOC_CTX *mem_ctx,
+ const char *netbios_domain_name,
+ struct trustdom_info **info_p)
+{
+ NTSTATUS status;
+ struct trustdom_info *info;
+ struct pdb_trusted_domain *td;
+
+ status = pdb_get_trusted_domain(mem_ctx, netbios_domain_name, &td);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info = talloc(mem_ctx, struct trustdom_info);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->name = talloc_strdup(info, netbios_domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->name);
+
+ sid_copy(&info->sid, &td->security_identifier);
+
+ *info_p = info;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_OpenSecret
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenSecret(struct pipes_struct *p,
+ struct lsa_OpenSecret *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd;
+ NTSTATUS status;
+ uint32_t acc_granted;
+
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &r->in.access_mask);
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&r->in.access_mask, &lsa_secret_mapping);
+
+ status = pdb_get_secret(p->mem_ctx, r->in.name.string,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &psd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0,
+ r->in.access_mask,
+ &acc_granted, "_lsa_OpenSecret");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_SECRET_TYPE,
+ acc_granted,
+ NULL,
+ r->in.name.string,
+ psd,
+ r->out.sec_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_OpenTrustedDomain_base
+ ***************************************************************************/
+
+static NTSTATUS _lsa_OpenTrustedDomain_base(struct pipes_struct *p,
+ uint32_t access_mask,
+ struct trustdom_info *info,
+ struct policy_handle *handle)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd = NULL;
+ size_t sd_size;
+ uint32_t acc_granted;
+ NTSTATUS status;
+
+ /* des_access is for the account here, not the policy
+ * handle - so don't check against policy handle. */
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &access_mask);
+
+ /* map the generic bits to the lsa account ones */
+ se_map_generic(&access_mask, &lsa_trusted_domain_mapping);
+
+ /* get the generic lsa account SD until we store it */
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_trusted_domain_mapping,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0,
+ access_mask, &acc_granted,
+ "_lsa_OpenTrustedDomain");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_TRUST_TYPE,
+ acc_granted,
+ &info->sid,
+ info->name,
+ psd,
+ handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_OpenTrustedDomain
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenTrustedDomain(struct pipes_struct *p,
+ struct lsa_OpenTrustedDomain *r)
+{
+ struct trustdom_info *info = NULL;
+ NTSTATUS status;
+
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = lsa_lookup_trusted_domain_by_sid(p->mem_ctx,
+ r->in.sid,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return _lsa_OpenTrustedDomain_base(p, r->in.access_mask, info,
+ r->out.trustdom_handle);
+}
+
+/***************************************************************************
+ _lsa_OpenTrustedDomainByName
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenTrustedDomainByName(struct pipes_struct *p,
+ struct lsa_OpenTrustedDomainByName *r)
+{
+ struct trustdom_info *info = NULL;
+ NTSTATUS status;
+
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = lsa_lookup_trusted_domain_by_name(p->mem_ctx,
+ r->in.name.string,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return _lsa_OpenTrustedDomain_base(p, r->in.access_mask, info,
+ r->out.trustdom_handle);
+}
+
+static NTSTATUS get_trustdom_auth_blob(struct pipes_struct *p,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *auth_blob,
+ struct trustDomainPasswords *auth_struct)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ enum ndr_err_code ndr_err;
+ DATA_BLOB lsession_key;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t my_session_key;
+ NTSTATUS status;
+ int rc;
+ bool encrypted;
+
+ encrypted = dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = session_extract_session_key(
+ session_info, &lsession_key, KEY_USE_16BYTES);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ my_session_key = (gnutls_datum_t) {
+ .data = lsession_key.data,
+ .size = lsession_key.length,
+ };
+
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &my_session_key,
+ NULL);
+ if (rc < 0) {
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ auth_blob->data,
+ auth_blob->length);
+ gnutls_cipher_deinit(cipher_hnd);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ ndr_err = ndr_pull_struct_blob(auth_blob, mem_ctx,
+ auth_struct,
+ (ndr_pull_flags_fn_t)ndr_pull_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ status = NT_STATUS_OK;
+out:
+ return status;
+}
+
+static NTSTATUS get_trustauth_inout_blob(TALLOC_CTX *mem_ctx,
+ struct trustAuthInOutBlob *iopw,
+ DATA_BLOB *trustauth_blob)
+{
+ enum ndr_err_code ndr_err;
+
+ if (iopw->current.count != iopw->count) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (iopw->previous.count > iopw->current.count) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (iopw->previous.count == 0) {
+ /*
+ * If the previous credentials are not present
+ * we need to make a copy.
+ */
+ iopw->previous = iopw->current;
+ }
+
+ if (iopw->previous.count < iopw->current.count) {
+ struct AuthenticationInformationArray *c = &iopw->current;
+ struct AuthenticationInformationArray *p = &iopw->previous;
+
+ /*
+ * The previous array needs to have the same size
+ * as the current one.
+ *
+ * We may have to fill with TRUST_AUTH_TYPE_NONE
+ * elements.
+ */
+ p->array = talloc_realloc(mem_ctx, p->array,
+ struct AuthenticationInformation,
+ c->count);
+ if (p->array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (p->count < c->count) {
+ struct AuthenticationInformation *a =
+ &p->array[p->count++];
+
+ *a = (struct AuthenticationInformation) {
+ .LastUpdateTime = p->array[0].LastUpdateTime,
+ .AuthType = TRUST_AUTH_TYPE_NONE,
+ };
+ }
+ }
+
+ ndr_err = ndr_push_struct_blob(trustauth_blob, mem_ctx,
+ iopw,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_CreateTrustedDomainEx2
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateTrustedDomainEx2(struct pipes_struct *p,
+ struct lsa_CreateTrustedDomainEx2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct lsa_info *policy;
+ NTSTATUS status;
+ uint32_t acc_granted;
+ struct security_descriptor *psd;
+ size_t sd_size;
+ struct pdb_trusted_domain td;
+ struct trustDomainPasswords auth_struct;
+ DATA_BLOB auth_blob;
+
+ if (!IS_DC) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ policy = find_policy_by_hnd(p,
+ r->in.policy_handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(policy->access & LSA_POLICY_TRUST_ADMIN)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (session_info->unix_token->uid != sec_initial_uid() &&
+ !nt_token_check_domain_rid(
+ session_info->security_token, DOMAIN_RID_ADMINS)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &r->in.access_mask);
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&r->in.access_mask, &lsa_account_mapping);
+
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_trusted_domain_mapping,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0,
+ r->in.access_mask, &acc_granted,
+ "_lsa_CreateTrustedDomainEx2");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(td);
+
+ td.domain_name = talloc_strdup(p->mem_ctx,
+ r->in.info->domain_name.string);
+ if (td.domain_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ td.netbios_name = talloc_strdup(p->mem_ctx,
+ r->in.info->netbios_name.string);
+ if (td.netbios_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid_copy(&td.security_identifier, r->in.info->sid);
+ td.trust_direction = r->in.info->trust_direction;
+ td.trust_type = r->in.info->trust_type;
+ td.trust_attributes = r->in.info->trust_attributes;
+
+ if (r->in.auth_info_internal->auth_blob.size != 0) {
+ auth_blob.length = r->in.auth_info_internal->auth_blob.size;
+ auth_blob.data = r->in.auth_info_internal->auth_blob.data;
+
+ status = get_trustdom_auth_blob(p, p->mem_ctx, &auth_blob, &auth_struct);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = get_trustauth_inout_blob(p->mem_ctx, &auth_struct.incoming, &td.trust_auth_incoming);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = get_trustauth_inout_blob(p->mem_ctx, &auth_struct.outgoing, &td.trust_auth_outgoing);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ td.trust_auth_incoming.data = NULL;
+ td.trust_auth_incoming.length = 0;
+ td.trust_auth_outgoing.data = NULL;
+ td.trust_auth_outgoing.length = 0;
+ }
+
+ status = pdb_set_trusted_domain(r->in.info->domain_name.string, &td);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_TRUST_TYPE,
+ acc_granted,
+ r->in.info->sid,
+ r->in.info->netbios_name.string,
+ psd,
+ r->out.trustdom_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ pdb_del_trusted_domain(r->in.info->netbios_name.string);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_CreateTrustedDomainEx
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateTrustedDomainEx(struct pipes_struct *p,
+ struct lsa_CreateTrustedDomainEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ _lsa_CreateTrustedDomain
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateTrustedDomain(struct pipes_struct *p,
+ struct lsa_CreateTrustedDomain *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ _lsa_DeleteTrustedDomain
+ ***************************************************************************/
+
+NTSTATUS _lsa_DeleteTrustedDomain(struct pipes_struct *p,
+ struct lsa_DeleteTrustedDomain *r)
+{
+ NTSTATUS status;
+ struct lsa_info *handle;
+ struct pdb_trusted_domain *td;
+
+ /* find the connection policy handle. */
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(handle->access & LSA_POLICY_TRUST_ADMIN)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pdb_get_trusted_domain_by_sid(p->mem_ctx, r->in.dom_sid, &td);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (td->netbios_name == NULL || *td->netbios_name == '\0') {
+ struct dom_sid_buf buf;
+ DEBUG(10, ("Missing netbios name for trusted domain %s.\n",
+ dom_sid_str_buf(r->in.dom_sid, &buf)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = pdb_del_trusted_domain(td->netbios_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_CloseTrustedDomainEx
+ ***************************************************************************/
+
+NTSTATUS _lsa_CloseTrustedDomainEx(struct pipes_struct *p,
+ struct lsa_CloseTrustedDomainEx *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ _lsa_QueryTrustedDomainInfo
+ ***************************************************************************/
+
+static NTSTATUS pdb_trusted_domain_2_info_ex(TALLOC_CTX *mem_ctx,
+ struct pdb_trusted_domain *td,
+ struct lsa_TrustDomainInfoInfoEx *info_ex)
+{
+ if (td->domain_name == NULL ||
+ td->netbios_name == NULL ||
+ is_null_sid(&td->security_identifier)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ info_ex->domain_name.string = talloc_strdup(mem_ctx, td->domain_name);
+ info_ex->netbios_name.string = talloc_strdup(mem_ctx, td->netbios_name);
+ info_ex->sid = dom_sid_dup(mem_ctx, &td->security_identifier);
+ if (info_ex->domain_name.string == NULL ||
+ info_ex->netbios_name.string == NULL ||
+ info_ex->sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info_ex->trust_direction = td->trust_direction;
+ info_ex->trust_type = td->trust_type;
+ info_ex->trust_attributes = td->trust_attributes;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _lsa_QueryTrustedDomainInfo(struct pipes_struct *p,
+ struct lsa_QueryTrustedDomainInfo *r)
+{
+ NTSTATUS status;
+ struct lsa_info *handle;
+ union lsa_TrustedDomainInfo *info;
+ struct pdb_trusted_domain *td;
+ uint32_t acc_required;
+
+ /* find the connection policy handle. */
+ handle = find_policy_by_hnd(p,
+ r->in.trustdom_handle,
+ LSA_HANDLE_TRUST_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (r->in.level) {
+ case LSA_TRUSTED_DOMAIN_INFO_NAME:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS:
+ acc_required = LSA_TRUSTED_QUERY_CONTROLLERS;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ acc_required = LSA_TRUSTED_QUERY_POSIX;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_PASSWORD:
+ acc_required = LSA_TRUSTED_QUERY_AUTH;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_BASIC:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO:
+ acc_required = LSA_TRUSTED_QUERY_AUTH;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME |
+ LSA_TRUSTED_QUERY_POSIX |
+ LSA_TRUSTED_QUERY_AUTH;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL:
+ acc_required = LSA_TRUSTED_QUERY_AUTH;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME |
+ LSA_TRUSTED_QUERY_POSIX |
+ LSA_TRUSTED_QUERY_AUTH;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL:
+ acc_required = LSA_TRUSTED_QUERY_DOMAIN_NAME |
+ LSA_TRUSTED_QUERY_POSIX |
+ LSA_TRUSTED_QUERY_AUTH;
+ break;
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES:
+ acc_required = LSA_TRUSTED_QUERY_POSIX;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(handle->access & acc_required)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pdb_get_trusted_domain_by_sid(p->mem_ctx, &handle->sid, &td);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info = talloc_zero(p->mem_ctx, union lsa_TrustedDomainInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case LSA_TRUSTED_DOMAIN_INFO_NAME:
+ init_lsa_StringLarge(&info->name.netbios_name, td->netbios_name);
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS:
+ return NT_STATUS_INVALID_PARAMETER;
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ info->posix_offset.posix_offset = *td->trust_posix_offset;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_PASSWORD:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ case LSA_TRUSTED_DOMAIN_INFO_BASIC:
+ return NT_STATUS_INVALID_PARAMETER;
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ status = pdb_trusted_domain_2_info_ex(info, td, &info->info_ex);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ status = pdb_trusted_domain_2_info_ex(info, td,
+ &info->full_info.info_ex);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ info->full_info.posix_offset.posix_offset = *td->trust_posix_offset;
+ status = auth_blob_2_auth_info(p->mem_ctx,
+ td->trust_auth_incoming,
+ td->trust_auth_outgoing,
+ &info->full_info.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL:
+ return NT_STATUS_INVALID_PARAMETER;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL:
+ info->full_info2_internal.posix_offset.posix_offset = *td->trust_posix_offset;
+ status = auth_blob_2_auth_info(p->mem_ctx,
+ td->trust_auth_incoming,
+ td->trust_auth_outgoing,
+ &info->full_info2_internal.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES:
+ info->enc_types.enc_types = *td->supported_enc_type;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_QueryTrustedDomainInfoBySid
+ ***************************************************************************/
+
+NTSTATUS _lsa_QueryTrustedDomainInfoBySid(struct pipes_struct *p,
+ struct lsa_QueryTrustedDomainInfoBySid *r)
+{
+ NTSTATUS status;
+ struct policy_handle trustdom_handle;
+ struct lsa_OpenTrustedDomain o;
+ struct lsa_QueryTrustedDomainInfo q;
+ struct lsa_Close c;
+
+ o.in.handle = r->in.handle;
+ o.in.sid = r->in.dom_sid;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.trustdom_handle = &trustdom_handle;
+
+ status = _lsa_OpenTrustedDomain(p, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = r->in.level;
+ q.out.info = r->out.info;
+
+ status = _lsa_QueryTrustedDomainInfo(p, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &trustdom_handle;
+
+ return _lsa_Close(p, &c);
+}
+
+/***************************************************************************
+ _lsa_QueryTrustedDomainInfoByName
+ ***************************************************************************/
+
+NTSTATUS _lsa_QueryTrustedDomainInfoByName(struct pipes_struct *p,
+ struct lsa_QueryTrustedDomainInfoByName *r)
+{
+ NTSTATUS status;
+ struct policy_handle trustdom_handle;
+ struct lsa_OpenTrustedDomainByName o;
+ struct lsa_QueryTrustedDomainInfo q;
+ struct lsa_Close c;
+
+ o.in.handle = r->in.handle;
+ o.in.name.string = r->in.trusted_domain->string;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.trustdom_handle = &trustdom_handle;
+
+ status = _lsa_OpenTrustedDomainByName(p, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return status;
+ }
+
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = r->in.level;
+ q.out.info = r->out.info;
+
+ status = _lsa_QueryTrustedDomainInfo(p, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &trustdom_handle;
+
+ return _lsa_Close(p, &c);
+}
+
+/***************************************************************************
+ _lsa_CreateSecret
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateSecret(struct pipes_struct *p,
+ struct lsa_CreateSecret *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ struct lsa_info *handle;
+ uint32_t acc_granted;
+ struct security_descriptor *psd;
+ size_t sd_size;
+
+ /* find the connection policy handle. */
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+
+ if (!(handle->access & LSA_POLICY_CREATE_SECRET)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &r->in.access_mask);
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&r->in.access_mask, &lsa_secret_mapping);
+
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_secret_mapping,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0,
+ r->in.access_mask,
+ &acc_granted, "_lsa_CreateSecret");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strlen(r->in.name.string) > 128) {
+ return NT_STATUS_NAME_TOO_LONG;
+ }
+
+ status = pdb_get_secret(p->mem_ctx, r->in.name.string,
+ NULL, NULL, NULL, NULL, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pdb_set_secret(r->in.name.string, NULL, NULL, psd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_SECRET_TYPE,
+ acc_granted,
+ NULL,
+ r->in.name.string,
+ psd,
+ r->out.sec_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_SetSecret
+ ***************************************************************************/
+
+NTSTATUS _lsa_SetSecret(struct pipes_struct *p,
+ struct lsa_SetSecret *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ struct lsa_info *info = NULL;
+ DATA_BLOB blob_new, blob_old;
+ DATA_BLOB cleartext_blob_new = data_blob_null;
+ DATA_BLOB cleartext_blob_old = data_blob_null;
+ DATA_BLOB *cleartext_blob_new_p = NULL;
+ DATA_BLOB *cleartext_blob_old_p = NULL;
+ DATA_BLOB session_key;
+
+ info = find_policy_by_hnd(p,
+ r->in.sec_handle,
+ LSA_HANDLE_SECRET_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_SECRET_SET_VALUE)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.new_val) {
+ blob_new = data_blob_const(r->in.new_val->data,
+ r->in.new_val->length);
+
+ status = sess_decrypt_blob(p->mem_ctx, &blob_new,
+ &session_key,
+ &cleartext_blob_new);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ cleartext_blob_new_p = &cleartext_blob_new;
+ }
+
+ if (r->in.old_val) {
+ blob_old = data_blob_const(r->in.old_val->data,
+ r->in.old_val->length);
+
+ status = sess_decrypt_blob(p->mem_ctx, &blob_old,
+ &session_key,
+ &cleartext_blob_old);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ cleartext_blob_old_p = &cleartext_blob_old;
+ }
+
+ status = pdb_set_secret(info->name, cleartext_blob_new_p, cleartext_blob_old_p, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(10,("_lsa_SetSecret: successfully set new secret\n"));
+ dump_data(10, cleartext_blob_new.data, cleartext_blob_new.length);
+ DEBUG(10,("_lsa_SetSecret: successfully set old secret\n"));
+ dump_data(10, cleartext_blob_old.data, cleartext_blob_old.length);
+#endif
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_QuerySecret
+ ***************************************************************************/
+
+NTSTATUS _lsa_QuerySecret(struct pipes_struct *p,
+ struct lsa_QuerySecret *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct lsa_info *info = NULL;
+ DATA_BLOB blob_new, blob_old;
+ DATA_BLOB blob_new_crypt, blob_old_crypt;
+ DATA_BLOB session_key;
+ NTTIME nttime_new, nttime_old;
+ NTSTATUS status;
+
+ info = find_policy_by_hnd(p,
+ r->in.sec_handle,
+ LSA_HANDLE_SECRET_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_SECRET_QUERY_VALUE)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pdb_get_secret(p->mem_ctx, info->name,
+ &blob_new, &nttime_new,
+ &blob_old, &nttime_old,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.new_val) {
+ if (blob_new.length) {
+ if (!r->out.new_val->buf) {
+ r->out.new_val->buf = talloc_zero(p->mem_ctx, struct lsa_DATA_BUF);
+ }
+ if (!r->out.new_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ blob_new_crypt = sess_encrypt_blob(p->mem_ctx, &blob_new,
+ &session_key);
+ if (!blob_new_crypt.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.new_val->buf->data = blob_new_crypt.data;
+ r->out.new_val->buf->length = blob_new_crypt.length;
+ r->out.new_val->buf->size = blob_new_crypt.length;
+ }
+ }
+
+ if (r->in.old_val) {
+ if (blob_old.length) {
+ if (!r->out.old_val->buf) {
+ r->out.old_val->buf = talloc_zero(p->mem_ctx, struct lsa_DATA_BUF);
+ }
+ if (!r->out.old_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ blob_old_crypt = sess_encrypt_blob(p->mem_ctx, &blob_old,
+ &session_key);
+ if (!blob_old_crypt.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.old_val->buf->data = blob_old_crypt.data;
+ r->out.old_val->buf->length = blob_old_crypt.length;
+ r->out.old_val->buf->size = blob_old_crypt.length;
+ }
+ }
+
+ if (r->out.new_mtime) {
+ *r->out.new_mtime = nttime_new;
+ }
+
+ if (r->out.old_mtime) {
+ *r->out.old_mtime = nttime_old;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_DeleteObject
+ ***************************************************************************/
+
+NTSTATUS _lsa_DeleteObject(struct pipes_struct *p,
+ struct lsa_DeleteObject *r)
+{
+ NTSTATUS status;
+ struct lsa_info *info = NULL;
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ DCESRV_HANDLE_ANY,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & SEC_STD_DELETE)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ switch (info->type) {
+ case LSA_HANDLE_ACCOUNT_TYPE:
+ status = privilege_delete_account(&info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("_lsa_DeleteObject: privilege_delete_account gave: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ break;
+ case LSA_HANDLE_TRUST_TYPE:
+ if (!pdb_del_trusteddom_pw(info->name)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ status = NT_STATUS_OK;
+ break;
+ case LSA_HANDLE_SECRET_TYPE:
+ status = pdb_delete_secret(info->name);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ close_policy_hnd(p, r->in.handle);
+ ZERO_STRUCTP(r->out.handle);
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_EnumPrivs
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumPrivs(struct pipes_struct *p,
+ struct lsa_EnumPrivs *r)
+{
+ struct lsa_info *handle;
+ uint32_t i;
+ uint32_t enum_context = *r->in.resume_handle;
+ int num_privs = num_privileges_in_short_list();
+ struct lsa_PrivEntry *entries = NULL;
+ NTSTATUS status;
+
+ /* remember that the enum_context starts at 0 and not 1 */
+
+ if ( enum_context >= num_privs )
+ return NT_STATUS_NO_MORE_ENTRIES;
+
+ DEBUG(10,("_lsa_EnumPrivs: enum_context:%d total entries:%d\n",
+ enum_context, num_privs));
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights
+ I don't know if it's the right one. not documented. */
+
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (num_privs) {
+ entries = talloc_zero_array(p->mem_ctx, struct lsa_PrivEntry, num_privs);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ entries = NULL;
+ }
+
+ for (i = 0; i < num_privs; i++) {
+ if( i < enum_context) {
+
+ init_lsa_StringLarge(&entries[i].name, NULL);
+
+ entries[i].luid.low = 0;
+ entries[i].luid.high = 0;
+ } else {
+
+ init_lsa_StringLarge(&entries[i].name, sec_privilege_name_from_index(i));
+
+ entries[i].luid.low = sec_privilege_from_index(i);
+ entries[i].luid.high = 0;
+ }
+ }
+
+ enum_context = num_privs;
+
+ *r->out.resume_handle = enum_context;
+ r->out.privs->count = num_privs;
+ r->out.privs->privs = entries;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_LookupPrivDisplayName
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupPrivDisplayName(struct pipes_struct *p,
+ struct lsa_LookupPrivDisplayName *r)
+{
+ struct lsa_info *handle;
+ const char *description;
+ struct lsa_StringLarge *lsa_name;
+ NTSTATUS status;
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ */
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ DEBUG(10,("_lsa_LookupPrivDisplayName: name = %s\n", r->in.name->string));
+
+ description = get_privilege_dispname(r->in.name->string);
+ if (!description) {
+ DEBUG(10,("_lsa_LookupPrivDisplayName: doesn't exist\n"));
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ DEBUG(10,("_lsa_LookupPrivDisplayName: display name = %s\n", description));
+
+ lsa_name = talloc_zero(p->mem_ctx, struct lsa_StringLarge);
+ if (!lsa_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ init_lsa_StringLarge(lsa_name, description);
+
+ *r->out.returned_language_id = r->in.language_id;
+ *r->out.disp_name = lsa_name;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_EnumAccounts
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumAccounts(struct pipes_struct *p,
+ struct lsa_EnumAccounts *r)
+{
+ struct lsa_info *handle;
+ struct dom_sid *sid_list;
+ int i, j, num_entries;
+ NTSTATUS status;
+ struct lsa_SidPtr *sids = NULL;
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_list = NULL;
+ num_entries = 0;
+
+ /* The only way we can currently find out all the SIDs that have been
+ privileged is to scan all privileges */
+
+ status = privilege_enumerate_accounts(&sid_list, &num_entries);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (*r->in.resume_handle >= num_entries) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ if (num_entries - *r->in.resume_handle) {
+ sids = talloc_zero_array(p->mem_ctx, struct lsa_SidPtr,
+ num_entries - *r->in.resume_handle);
+ if (!sids) {
+ talloc_free(sid_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = *r->in.resume_handle, j = 0; i < num_entries; i++, j++) {
+ sids[j].sid = dom_sid_dup(p->mem_ctx, &sid_list[i]);
+ if (!sids[j].sid) {
+ talloc_free(sid_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ talloc_free(sid_list);
+
+ *r->out.resume_handle = num_entries;
+ r->out.sids->num_sids = num_entries;
+ r->out.sids->sids = sids;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_GetUserName
+ ***************************************************************************/
+
+NTSTATUS _lsa_GetUserName(struct pipes_struct *p,
+ struct lsa_GetUserName *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const char *username, *domname;
+ struct lsa_String *account_name = NULL;
+ struct lsa_String *authority_name = NULL;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (r->in.account_name &&
+ *r->in.account_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.authority_name &&
+ *r->in.authority_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
+ /*
+ * I'm 99% sure this is not the right place to do this,
+ * global_sid_Anonymous should probably be put into the token
+ * instead of the guest id -- vl
+ */
+ if (!lookup_sid(p->mem_ctx, &global_sid_Anonymous,
+ &domname, &username, NULL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ username = session_info->unix_info->sanitized_username;
+ domname = session_info->info->domain_name;
+ }
+
+ account_name = talloc(p->mem_ctx, struct lsa_String);
+ if (!account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ init_lsa_String(account_name, username);
+
+ if (r->out.authority_name) {
+ authority_name = talloc(p->mem_ctx, struct lsa_String);
+ if (!authority_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ init_lsa_String(authority_name, domname);
+ }
+
+ *r->out.account_name = account_name;
+ if (r->out.authority_name) {
+ *r->out.authority_name = authority_name;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_CreateAccount
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateAccount(struct pipes_struct *p,
+ struct lsa_CreateAccount *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ struct lsa_info *handle;
+ uint32_t acc_granted;
+ struct security_descriptor *psd;
+ size_t sd_size;
+ uint32_t owner_access = (LSA_ACCOUNT_ALL_ACCESS &
+ ~(LSA_ACCOUNT_ADJUST_PRIVILEGES|
+ LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|
+ SEC_STD_DELETE));
+
+ /* find the connection policy handle. */
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+
+ if (!(handle->access & LSA_POLICY_CREATE_ACCOUNT)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &r->in.access_mask);
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&r->in.access_mask, &lsa_account_mapping);
+
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_account_mapping,
+ r->in.sid, owner_access);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, r->in.access_mask,
+ &acc_granted, "_lsa_CreateAccount");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ( is_privileged_sid( r->in.sid ) )
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ acc_granted,
+ r->in.sid,
+ NULL,
+ psd,
+ r->out.acct_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return privilege_create_account(r->in.sid);
+}
+
+/***************************************************************************
+ _lsa_OpenAccount
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenAccount(struct pipes_struct *p,
+ struct lsa_OpenAccount *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd = NULL;
+ size_t sd_size;
+ uint32_t des_access = r->in.access_mask;
+ uint32_t acc_granted;
+ uint32_t owner_access = (LSA_ACCOUNT_ALL_ACCESS &
+ ~(LSA_ACCOUNT_ADJUST_PRIVILEGES|
+ LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|
+ SEC_STD_DELETE));
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* des_access is for the account here, not the policy
+ * handle - so don't check against policy handle. */
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ /* map the generic bits to the lsa account ones */
+ se_map_generic(&des_access, &lsa_account_mapping);
+
+ /* get the generic lsa account SD until we store it */
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_account_mapping,
+ r->in.sid, owner_access);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0, des_access,
+ &acc_granted, "_lsa_OpenAccount" );
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* TODO: Fis the parsing routine before reenabling this check! */
+ #if 0
+ if (!lookup_sid(&handle->sid, dom_name, name, &type))
+ return NT_STATUS_ACCESS_DENIED;
+ #endif
+
+ status = create_lsa_policy_handle(p->mem_ctx, p,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ acc_granted,
+ r->in.sid,
+ NULL,
+ psd,
+ r->out.acct_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_EnumPrivsAccount
+ For a given SID, enumerate all the privilege this account has.
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumPrivsAccount(struct pipes_struct *p,
+ struct lsa_EnumPrivsAccount *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct lsa_info *info=NULL;
+ PRIVILEGE_SET *privileges;
+ struct lsa_PrivilegeSet *priv_set = NULL;
+ struct dom_sid_buf buf;
+
+ /* find the connection policy handle. */
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_ACCOUNT_VIEW))
+ return NT_STATUS_ACCESS_DENIED;
+
+ status = get_privileges_for_sid_as_set(p->mem_ctx, &privileges, &info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.privs = priv_set = talloc_zero(p->mem_ctx, struct lsa_PrivilegeSet);
+ if (!priv_set) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("_lsa_EnumPrivsAccount: %s has %d privileges\n",
+ dom_sid_str_buf(&info->sid, &buf),
+ privileges->count));
+
+ priv_set->count = privileges->count;
+ priv_set->unknown = 0;
+ priv_set->set = talloc_move(priv_set, &privileges->set);
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_GetSystemAccessAccount
+ ***************************************************************************/
+
+NTSTATUS _lsa_GetSystemAccessAccount(struct pipes_struct *p,
+ struct lsa_GetSystemAccessAccount *r)
+{
+ NTSTATUS status;
+ struct lsa_info *info = NULL;
+ struct lsa_EnumPrivsAccount e;
+ struct lsa_PrivilegeSet *privset;
+
+ /* find the connection policy handle. */
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_ACCOUNT_VIEW))
+ return NT_STATUS_ACCESS_DENIED;
+
+ privset = talloc_zero(p->mem_ctx, struct lsa_PrivilegeSet);
+ if (!privset) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ e.in.handle = r->in.handle;
+ e.out.privs = &privset;
+
+ status = _lsa_EnumPrivsAccount(p, &e);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("_lsa_GetSystemAccessAccount: "
+ "failed to call _lsa_EnumPrivsAccount(): %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* Samba4 would iterate over the privset to merge the policy mode bits,
+ * not sure samba3 can do the same here, so just return what we did in
+ * the past - gd */
+
+ /*
+ 0x01 -> Log on locally
+ 0x02 -> Access this computer from network
+ 0x04 -> Log on as a batch job
+ 0x10 -> Log on as a service
+
+ they can be ORed together
+ */
+
+ *r->out.access_mask = LSA_POLICY_MODE_INTERACTIVE |
+ LSA_POLICY_MODE_NETWORK;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ update the systemaccount information
+ ***************************************************************************/
+
+NTSTATUS _lsa_SetSystemAccessAccount(struct pipes_struct *p,
+ struct lsa_SetSystemAccessAccount *r)
+{
+ struct lsa_info *info=NULL;
+ NTSTATUS status;
+ GROUP_MAP *map;
+
+ /* find the connection policy handle. */
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ map = talloc_zero(p->mem_ctx, GROUP_MAP);
+ if (!map) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_getgrsid(map, info->sid)) {
+ TALLOC_FREE(map);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ status = pdb_update_group_mapping_entry(map);
+ TALLOC_FREE(map);
+ return status;
+}
+
+/***************************************************************************
+ _lsa_AddPrivilegesToAccount
+ For a given SID, add some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_AddPrivilegesToAccount(struct pipes_struct *p,
+ struct lsa_AddPrivilegesToAccount *r)
+{
+ struct lsa_info *info = NULL;
+ struct lsa_PrivilegeSet *set = NULL;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_ACCOUNT_ADJUST_PRIVILEGES)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ set = r->in.privs;
+
+ if ( !grant_privilege_set( &info->sid, set ) ) {
+ struct dom_sid_buf buf;
+ DEBUG(3,("_lsa_AddPrivilegesToAccount: grant_privilege_set(%s) failed!\n",
+ dom_sid_str_buf(&info->sid, &buf)));
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_RemovePrivilegesFromAccount
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_RemovePrivilegesFromAccount(struct pipes_struct *p,
+ struct lsa_RemovePrivilegesFromAccount *r)
+{
+ struct lsa_info *info = NULL;
+ struct lsa_PrivilegeSet *set = NULL;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_ACCOUNT_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_ACCOUNT_ADJUST_PRIVILEGES)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ set = r->in.privs;
+
+ if ( !revoke_privilege_set( &info->sid, set) ) {
+ struct dom_sid_buf buf;
+ DEBUG(3,("_lsa_RemovePrivilegesFromAccount: revoke_privilege(%s) failed!\n",
+ dom_sid_str_buf(&info->sid, &buf)));
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_LookupPrivName
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupPrivName(struct pipes_struct *p,
+ struct lsa_LookupPrivName *r)
+{
+ struct lsa_info *info = NULL;
+ const char *name;
+ struct lsa_StringLarge *lsa_name;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_POLICY_VIEW_LOCAL_INFORMATION)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (r->in.luid->high != 0) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ name = sec_privilege_name(r->in.luid->low);
+ if (!name) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ lsa_name = talloc_zero(p->mem_ctx, struct lsa_StringLarge);
+ if (!lsa_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lsa_name->string = talloc_strdup(lsa_name, name);
+ if (!lsa_name->string) {
+ TALLOC_FREE(lsa_name);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.name = lsa_name;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_QuerySecurity
+ ***************************************************************************/
+
+NTSTATUS _lsa_QuerySecurity(struct pipes_struct *p,
+ struct lsa_QuerySecurity *r)
+{
+ struct lsa_info *handle=NULL;
+ struct security_descriptor *psd = NULL;
+ size_t sd_size = 0;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ DCESRV_HANDLE_ANY,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (handle->type) {
+ case LSA_HANDLE_POLICY_TYPE:
+ case LSA_HANDLE_ACCOUNT_TYPE:
+ case LSA_HANDLE_TRUST_TYPE:
+ case LSA_HANDLE_SECRET_TYPE:
+ psd = handle->sd;
+ sd_size = ndr_size_security_descriptor(psd, 0);
+ status = NT_STATUS_OK;
+ break;
+ default:
+ status = NT_STATUS_INVALID_HANDLE;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.sdbuf = make_sec_desc_buf(p->mem_ctx, sd_size, psd);
+ if (!*r->out.sdbuf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_AddAccountRights
+ ***************************************************************************/
+
+NTSTATUS _lsa_AddAccountRights(struct pipes_struct *p,
+ struct lsa_AddAccountRights *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ int i = 0;
+ uint32_t acc_granted = 0;
+ struct security_descriptor *psd = NULL;
+ size_t sd_size;
+ struct dom_sid sid;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* get the generic lsa account SD for this SID until we store it */
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_account_mapping,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * From the MS DOCs. If the sid doesn't exist, ask for LSA_POLICY_CREATE_ACCOUNT
+ * on the policy handle. If it does, ask for
+ * LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|LSA_ACCOUNT_VIEW,
+ * on the account sid. We don't check here so just use the latter. JRA.
+ */
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0,
+ LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|LSA_ACCOUNT_VIEW,
+ &acc_granted, "_lsa_AddAccountRights" );
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* according to an NT4 PDC, you can add privileges to SIDs even without
+ call_lsa_create_account() first. And you can use any arbitrary SID. */
+
+ sid_copy( &sid, r->in.sid );
+
+ for ( i=0; i < r->in.rights->count; i++ ) {
+
+ const char *privname = r->in.rights->names[i].string;
+
+ /* only try to add non-null strings */
+
+ if ( !privname )
+ continue;
+
+ if ( !grant_privilege_by_name( &sid, privname ) ) {
+ DEBUG(2,("_lsa_AddAccountRights: Failed to add privilege [%s]\n",
+ privname ));
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_RemoveAccountRights
+ ***************************************************************************/
+
+NTSTATUS _lsa_RemoveAccountRights(struct pipes_struct *p,
+ struct lsa_RemoveAccountRights *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ int i = 0;
+ struct security_descriptor *psd = NULL;
+ size_t sd_size;
+ struct dom_sid sid;
+ const char *privname = NULL;
+ uint32_t acc_granted = 0;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ (void)find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* get the generic lsa account SD for this SID until we store it */
+ status = make_lsa_object_sd(p->mem_ctx, &psd, &sd_size,
+ &lsa_account_mapping,
+ NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * From the MS DOCs. We need
+ * LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|LSA_ACCOUNT_VIEW
+ * and DELETE on the account sid.
+ */
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID, 0,
+ LSA_ACCOUNT_ADJUST_PRIVILEGES|LSA_ACCOUNT_ADJUST_SYSTEM_ACCESS|
+ LSA_ACCOUNT_VIEW|SEC_STD_DELETE,
+ &acc_granted, "_lsa_RemoveAccountRights");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sid_copy( &sid, r->in.sid );
+
+ if ( r->in.remove_all ) {
+ if ( !revoke_all_privileges( &sid ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+ }
+
+ for ( i=0; i < r->in.rights->count; i++ ) {
+
+ privname = r->in.rights->names[i].string;
+
+ /* only try to add non-null strings */
+
+ if ( !privname )
+ continue;
+
+ if ( !revoke_privilege_by_name( &sid, privname ) ) {
+ DEBUG(2,("_lsa_RemoveAccountRights: Failed to revoke privilege [%s]\n",
+ privname ));
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static NTSTATUS init_lsa_right_set(TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *r,
+ PRIVILEGE_SET *privileges)
+{
+ uint32_t i;
+ const char *privname;
+ const char **privname_array = NULL;
+ size_t num_priv = 0;
+
+ for (i=0; i<privileges->count; i++) {
+ if (privileges->set[i].luid.high) {
+ continue;
+ }
+ privname = sec_privilege_name(privileges->set[i].luid.low);
+ if (privname) {
+ if (!add_string_to_array(mem_ctx, privname,
+ &privname_array, &num_priv)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ if (num_priv) {
+
+ r->names = talloc_zero_array(mem_ctx, struct lsa_StringLarge,
+ num_priv);
+ if (!r->names) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_priv; i++) {
+ init_lsa_StringLarge(&r->names[i], privname_array[i]);
+ }
+
+ r->count = num_priv;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_EnumAccountRights
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumAccountRights(struct pipes_struct *p,
+ struct lsa_EnumAccountRights *r)
+{
+ NTSTATUS status;
+ struct lsa_info *info = NULL;
+ PRIVILEGE_SET *privileges;
+ struct dom_sid_buf buf;
+
+ /* find the connection policy handle. */
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_ACCOUNT_VIEW)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* according to an NT4 PDC, you can add privileges to SIDs even without
+ call_lsa_create_account() first. And you can use any arbitrary SID. */
+
+ /* according to MS-LSAD 3.1.4.5.10 it is required to return
+ * NT_STATUS_OBJECT_NAME_NOT_FOUND if the account sid was not found in
+ * the lsa database */
+
+ status = get_privileges_for_sid_as_set(p->mem_ctx, &privileges, r->in.sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10,("_lsa_EnumAccountRights: %s has %d privileges\n",
+ dom_sid_str_buf(r->in.sid, &buf),
+ privileges->count));
+
+ status = init_lsa_right_set(p->mem_ctx, r->out.rights, privileges);
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_LookupPrivValue
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupPrivValue(struct pipes_struct *p,
+ struct lsa_LookupPrivValue *r)
+{
+ struct lsa_info *info = NULL;
+ const char *name = NULL;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_POLICY_LOOKUP_NAMES))
+ return NT_STATUS_ACCESS_DENIED;
+
+ name = r->in.name->string;
+
+ DEBUG(10,("_lsa_lookup_priv_value: name = %s\n", name));
+
+ r->out.luid->low = sec_privilege_id(name);
+ r->out.luid->high = 0;
+ if (r->out.luid->low == SEC_PRIV_INVALID) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_EnumAccountsWithUserRight
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumAccountsWithUserRight(struct pipes_struct *p,
+ struct lsa_EnumAccountsWithUserRight *r)
+{
+ NTSTATUS status;
+ struct lsa_info *info = NULL;
+ struct dom_sid *sids = NULL;
+ int num_sids = 0;
+ uint32_t i;
+ enum sec_privilege privilege;
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(info->access & LSA_POLICY_LOOKUP_NAMES)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!r->in.name || !r->in.name->string) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privilege = sec_privilege_id(r->in.name->string);
+ if (privilege == SEC_PRIV_INVALID) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ status = privilege_enum_sids(privilege, p->mem_ctx,
+ &sids, &num_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.sids->num_sids = num_sids;
+ r->out.sids->sids = talloc_array(p->mem_ctx, struct lsa_SidPtr,
+ r->out.sids->num_sids);
+
+ for (i=0; i < r->out.sids->num_sids; i++) {
+ r->out.sids->sids[i].sid = dom_sid_dup(r->out.sids->sids,
+ &sids[i]);
+ if (!r->out.sids->sids[i].sid) {
+ TALLOC_FREE(r->out.sids->sids);
+ r->out.sids->num_sids = 0;
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_Delete
+ ***************************************************************************/
+
+NTSTATUS _lsa_Delete(struct pipes_struct *p,
+ struct lsa_Delete *r)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+static NTSTATUS info_ex_2_pdb_trusted_domain(
+ struct lsa_TrustDomainInfoInfoEx *info_ex,
+ struct pdb_trusted_domain *td)
+{
+ if (info_ex->domain_name.string == NULL ||
+ info_ex->netbios_name.string == NULL ||
+ info_ex->sid == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ td->domain_name = talloc_strdup(td, info_ex->domain_name.string);
+ td->netbios_name = talloc_strdup(td, info_ex->netbios_name.string);
+ sid_copy(&td->security_identifier, info_ex->sid);
+ if (td->domain_name == NULL ||
+ td->netbios_name == NULL ||
+ is_null_sid(&td->security_identifier)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ td->trust_direction = info_ex->trust_direction;
+ td->trust_type = info_ex->trust_type;
+ td->trust_attributes = info_ex->trust_attributes;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS setInfoTrustedDomain_base(struct pipes_struct *p,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_info *policy,
+ enum lsa_TrustDomInfoEnum level,
+ union lsa_TrustedDomainInfo *info)
+{
+ struct lsa_TrustDomainInfoAuthInfoInternal *auth_info_int = NULL;
+ DATA_BLOB auth_blob;
+ struct trustDomainPasswords auth_struct;
+ NTSTATUS nt_status;
+
+ struct pdb_trusted_domain *td;
+ struct pdb_trusted_domain *orig_td;
+
+ td = talloc_zero(mem_ctx, struct pdb_trusted_domain);
+ if (td == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (level) {
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ if (!(policy->access & LSA_TRUSTED_SET_POSIX)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ td->trust_posix_offset = &info->posix_offset.posix_offset;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ if (!(policy->access & LSA_TRUSTED_SET_POSIX)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ nt_status = info_ex_2_pdb_trusted_domain(&info->info_ex, td);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO:
+ if (!(policy->access & LSA_TRUSTED_SET_AUTH)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ nt_status = auth_info_2_auth_blob(td, &info->auth_info,
+ &td->trust_auth_incoming,
+ &td->trust_auth_outgoing);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ if (!(policy->access & (LSA_TRUSTED_SET_AUTH | LSA_TRUSTED_SET_POSIX))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ td->trust_posix_offset = &info->full_info.posix_offset.posix_offset;
+ nt_status = info_ex_2_pdb_trusted_domain(&info->full_info.info_ex,
+ td);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ nt_status = auth_info_2_auth_blob(td,
+ &info->full_info.auth_info,
+ &td->trust_auth_incoming,
+ &td->trust_auth_outgoing);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL:
+ if (!(policy->access & LSA_TRUSTED_SET_AUTH)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ auth_info_int = &info->auth_info_internal;
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL:
+ if (!(policy->access & (LSA_TRUSTED_SET_AUTH | LSA_TRUSTED_SET_POSIX))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ td->trust_posix_offset = &info->full_info_internal.posix_offset.posix_offset;
+ nt_status = info_ex_2_pdb_trusted_domain(&info->full_info_internal.info_ex,
+ td);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ auth_info_int = &info->full_info_internal.auth_info;
+ break;
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES:
+ if (!(policy->access & LSA_TRUSTED_SET_POSIX)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ td->supported_enc_type = &info->enc_types.enc_types;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* decode auth_info_int if set */
+ if (auth_info_int) {
+
+ /* now decrypt blob */
+ auth_blob = data_blob_const(auth_info_int->auth_blob.data,
+ auth_info_int->auth_blob.size);
+
+ nt_status = get_trustdom_auth_blob(p, mem_ctx,
+ &auth_blob, &auth_struct);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else {
+ memset(&auth_struct, 0, sizeof(auth_struct));
+ }
+
+/* TODO: verify only one object matches the dns/netbios/sid triplet and that
+ * this is the one we already have */
+
+/* TODO: check if the trust direction is changed and we need to add or remove
+ * auth data */
+
+/* TODO: check if trust type shall be changed and return an error in this case
+ * */
+ nt_status = pdb_get_trusted_domain_by_sid(p->mem_ctx, &policy->sid,
+ &orig_td);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+
+ /* TODO: should we fetch previous values from the existing entry
+ * and append them ? */
+ if (auth_struct.incoming.count) {
+ nt_status = get_trustauth_inout_blob(mem_ctx,
+ &auth_struct.incoming,
+ &td->trust_auth_incoming);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else {
+ ZERO_STRUCT(td->trust_auth_incoming);
+ }
+
+ if (auth_struct.outgoing.count) {
+ nt_status = get_trustauth_inout_blob(mem_ctx,
+ &auth_struct.outgoing,
+ &td->trust_auth_outgoing);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else {
+ ZERO_STRUCT(td->trust_auth_outgoing);
+ }
+
+ nt_status = pdb_set_trusted_domain(orig_td->domain_name, td);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _lsa_SetTrustedDomainInfo(struct pipes_struct *p,
+ struct lsa_SetTrustedDomainInfo *r)
+{
+ NTSTATUS status;
+ struct policy_handle trustdom_handle;
+ struct lsa_OpenTrustedDomain o;
+ struct lsa_SetInformationTrustedDomain s;
+ struct lsa_Close c;
+
+ o.in.handle = r->in.handle;
+ o.in.sid = r->in.dom_sid;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.trustdom_handle = &trustdom_handle;
+
+ status = _lsa_OpenTrustedDomain(p, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ s.in.trustdom_handle = &trustdom_handle;
+ s.in.level = r->in.level;
+ s.in.info = r->in.info;
+
+ status = _lsa_SetInformationTrustedDomain(p, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &trustdom_handle;
+
+ return _lsa_Close(p, &c);
+}
+
+NTSTATUS _lsa_SetTrustedDomainInfoByName(struct pipes_struct *p,
+ struct lsa_SetTrustedDomainInfoByName *r)
+{
+ NTSTATUS status;
+ struct policy_handle trustdom_handle;
+ struct lsa_OpenTrustedDomainByName o;
+ struct lsa_SetInformationTrustedDomain s;
+ struct lsa_Close c;
+
+ o.in.handle = r->in.handle;
+ o.in.name.string = r->in.trusted_domain->string;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.trustdom_handle = &trustdom_handle;
+
+ status = _lsa_OpenTrustedDomainByName(p, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return status;
+ }
+
+ s.in.trustdom_handle = &trustdom_handle;
+ s.in.level = r->in.level;
+ s.in.info = r->in.info;
+
+ status = _lsa_SetInformationTrustedDomain(p, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &trustdom_handle;
+
+ return _lsa_Close(p, &c);
+}
+
+NTSTATUS _lsa_SetInformationTrustedDomain(struct pipes_struct *p,
+ struct lsa_SetInformationTrustedDomain *r)
+{
+ struct lsa_info *policy;
+ NTSTATUS status;
+
+ policy = find_policy_by_hnd(p,
+ r->in.trustdom_handle,
+ LSA_HANDLE_TRUST_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return setInfoTrustedDomain_base(p, p->mem_ctx, policy,
+ r->in.level, r->in.info);
+}
+
+
+/*
+ * From here on the server routines are just dummy ones to make smbd link with
+ * librpc/gen_ndr/srv_lsa.c. These routines are actually never called, we are
+ * pulling the server stubs across one by one.
+ */
+
+NTSTATUS _lsa_SetSecObj(struct pipes_struct *p, struct lsa_SetSecObj *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_ChangePassword(struct pipes_struct *p,
+ struct lsa_ChangePassword *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetInfoPolicy(struct pipes_struct *p, struct lsa_SetInfoPolicy *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_ClearAuditLog(struct pipes_struct *p, struct lsa_ClearAuditLog *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_GetQuotasForAccount(struct pipes_struct *p,
+ struct lsa_GetQuotasForAccount *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetQuotasForAccount(struct pipes_struct *p,
+ struct lsa_SetQuotasForAccount *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_StorePrivateData(struct pipes_struct *p,
+ struct lsa_StorePrivateData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_RetrievePrivateData(struct pipes_struct *p,
+ struct lsa_RetrievePrivateData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetInfoPolicy2(struct pipes_struct *p,
+ struct lsa_SetInfoPolicy2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_EnumTrustedDomainsEx(struct pipes_struct *p,
+ struct lsa_EnumTrustedDomainsEx *r)
+{
+ struct lsa_info *info;
+ uint32_t count;
+ struct pdb_trusted_domain **domains;
+ struct lsa_TrustDomainInfoInfoEx *entries;
+ int i;
+ NTSTATUS nt_status;
+
+ /* bail out early if pdb backend is not capable of ex trusted domains,
+ * if we don't do that, the client might not call
+ * _lsa_EnumTrustedDomains() afterwards - gd */
+
+ if (!(pdb_capabilities() & PDB_CAP_TRUSTED_DOMAINS_EX)) {
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ info = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_POLICY_TYPE,
+ struct lsa_info,
+ &nt_status);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* check if the user has enough rights */
+ if (!(info->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ become_root();
+ nt_status = pdb_enum_trusted_domains(p->mem_ctx, &count, &domains);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ entries = talloc_zero_array(p->mem_ctx, struct lsa_TrustDomainInfoInfoEx,
+ count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<count; i++) {
+ init_lsa_StringLarge(&entries[i].domain_name,
+ domains[i]->domain_name);
+ init_lsa_StringLarge(&entries[i].netbios_name,
+ domains[i]->netbios_name);
+ entries[i].sid = &domains[i]->security_identifier;
+ entries[i].trust_direction = domains[i]->trust_direction;
+ entries[i].trust_type = domains[i]->trust_type;
+ entries[i].trust_attributes = domains[i]->trust_attributes;
+ }
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+ TALLOC_FREE(entries);
+ 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,
+ (r->in.max_size/LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+
+ 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;
+ }
+
+ /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST
+ * always be larger than the previous input resume handle, in
+ * particular when hitting the last query it is vital to set the
+ * resume handle correctly to avoid infinite client loops, as
+ * seen e.g. with Windows XP SP3 when resume handle is 0 and
+ * status is NT_STATUS_OK - gd */
+
+ *r->out.resume_handle = (uint32_t)-1;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _lsa_QueryDomainInformationPolicy(struct pipes_struct *p,
+ struct lsa_QueryDomainInformationPolicy *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetDomainInformationPolicy(struct pipes_struct *p,
+ struct lsa_SetDomainInformationPolicy *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_TestCall(struct pipes_struct *p, struct lsa_TestCall *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRWRITE(struct pipes_struct *p, struct lsa_CREDRWRITE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRREAD(struct pipes_struct *p, struct lsa_CREDRREAD *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRENUMERATE(struct pipes_struct *p, struct lsa_CREDRENUMERATE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRWRITEDOMAINCREDENTIALS(struct pipes_struct *p,
+ struct lsa_CREDRWRITEDOMAINCREDENTIALS *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRREADDOMAINCREDENTIALS(struct pipes_struct *p,
+ struct lsa_CREDRREADDOMAINCREDENTIALS *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRDELETE(struct pipes_struct *p, struct lsa_CREDRDELETE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRGETTARGETINFO(struct pipes_struct *p,
+ struct lsa_CREDRGETTARGETINFO *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRPROFILELOADED(struct pipes_struct *p,
+ struct lsa_CREDRPROFILELOADED *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRGETSESSIONTYPES(struct pipes_struct *p,
+ struct lsa_CREDRGETSESSIONTYPES *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARREGISTERAUDITEVENT(struct pipes_struct *p,
+ struct lsa_LSARREGISTERAUDITEVENT *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARGENAUDITEVENT(struct pipes_struct *p,
+ struct lsa_LSARGENAUDITEVENT *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARUNREGISTERAUDITEVENT(struct pipes_struct *p,
+ struct lsa_LSARUNREGISTERAUDITEVENT *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_lsaRQueryForestTrustInformation(struct pipes_struct *p,
+ struct lsa_lsaRQueryForestTrustInformation *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+#define DNS_CMP_MATCH 0
+#define DNS_CMP_FIRST_IS_CHILD 1
+#define DNS_CMP_SECOND_IS_CHILD 2
+#define DNS_CMP_NO_MATCH 3
+
+/* this function assumes names are well formed DNS names.
+ * it doesn't validate them */
+static int dns_cmp(const char *s1, size_t l1,
+ const char *s2, size_t l2)
+{
+ const char *p1, *p2;
+ size_t t1, t2;
+ int cret;
+
+ if (l1 == l2) {
+ if (strcasecmp_m(s1, s2) == 0) {
+ return DNS_CMP_MATCH;
+ }
+ return DNS_CMP_NO_MATCH;
+ }
+
+ if (l1 > l2) {
+ p1 = s1;
+ p2 = s2;
+ t1 = l1;
+ t2 = l2;
+ cret = DNS_CMP_FIRST_IS_CHILD;
+ } else {
+ p1 = s2;
+ p2 = s1;
+ t1 = l2;
+ t2 = l1;
+ cret = DNS_CMP_SECOND_IS_CHILD;
+ }
+
+ if (p1[t1 - t2 - 1] != '.') {
+ return DNS_CMP_NO_MATCH;
+ }
+
+ if (strcasecmp_m(&p1[t1 - t2], p2) == 0) {
+ return cret;
+ }
+
+ return DNS_CMP_NO_MATCH;
+}
+
+static NTSTATUS make_ft_info(TALLOC_CTX *mem_ctx,
+ struct lsa_ForestTrustInformation *lfti,
+ struct ForestTrustInfo *fti)
+{
+ struct lsa_ForestTrustRecord *lrec;
+ struct ForestTrustInfoRecord *rec;
+ struct lsa_StringLarge *tln;
+ struct lsa_ForestTrustDomainInfo *info;
+ uint32_t i;
+
+ fti->version = 1;
+ fti->count = lfti->count;
+ fti->records = talloc_array(mem_ctx,
+ struct ForestTrustInfoRecordArmor,
+ fti->count);
+ if (!fti->records) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i = 0; i < fti->count; i++) {
+ lrec = lfti->entries[i];
+ rec = &fti->records[i].record;
+
+ rec->flags = lrec->flags;
+ rec->timestamp = lrec->time;
+ rec->type = (enum ForestTrustInfoRecordType)lrec->type;
+
+ switch (lrec->type) {
+ case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+ case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+ tln = &lrec->forest_trust_data.top_level_name;
+ rec->data.name.string =
+ talloc_strdup(mem_ctx, tln->string);
+ if (!rec->data.name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ rec->data.name.size = strlen(rec->data.name.string);
+ break;
+ case LSA_FOREST_TRUST_DOMAIN_INFO:
+ info = &lrec->forest_trust_data.domain_info;
+ rec->data.info.sid = *info->domain_sid;
+ rec->data.info.dns_name.string =
+ talloc_strdup(mem_ctx,
+ info->dns_domain_name.string);
+ if (!rec->data.info.dns_name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ rec->data.info.dns_name.size =
+ strlen(rec->data.info.dns_name.string);
+ rec->data.info.netbios_name.string =
+ talloc_strdup(mem_ctx,
+ info->netbios_domain_name.string);
+ if (!rec->data.info.netbios_name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ rec->data.info.netbios_name.size =
+ strlen(rec->data.info.netbios_name.string);
+ break;
+ default:
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS add_collision(struct lsa_ForestTrustCollisionInfo *c_info,
+ uint32_t index, uint32_t collision_type,
+ uint32_t conflict_type, const char *tdo_name);
+
+static NTSTATUS check_ft_info(TALLOC_CTX *mem_ctx,
+ const char *tdo_name,
+ struct ForestTrustInfo *tdo_fti,
+ struct ForestTrustInfo *new_fti,
+ struct lsa_ForestTrustCollisionInfo *c_info)
+{
+ struct ForestTrustInfoRecord *nrec;
+ struct ForestTrustInfoRecord *trec;
+ const char *dns_name;
+ const char *nb_name = NULL;
+ struct dom_sid *sid = NULL;
+ const char *tname = NULL;
+ size_t dns_len = 0;
+ size_t tlen = 0;
+ uint32_t new_fti_idx;
+ uint32_t i;
+ /* use always TDO type, until we understand when Xref can be used */
+ uint32_t collision_type = LSA_FOREST_TRUST_COLLISION_TDO;
+ bool tln_conflict;
+ bool sid_conflict;
+ bool nb_conflict;
+ bool exclusion;
+ bool ex_rule = false;
+ int ret;
+
+ for (new_fti_idx = 0; new_fti_idx < new_fti->count; new_fti_idx++) {
+
+ nrec = &new_fti->records[new_fti_idx].record;
+ dns_name = NULL;
+ tln_conflict = false;
+ sid_conflict = false;
+ nb_conflict = false;
+ exclusion = false;
+
+ switch (nrec->type) {
+ case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+ /* exclusions do not conflict by definition */
+ break;
+
+ case FOREST_TRUST_TOP_LEVEL_NAME:
+ dns_name = nrec->data.name.string;
+ dns_len = nrec->data.name.size;
+ break;
+
+ case LSA_FOREST_TRUST_DOMAIN_INFO:
+ dns_name = nrec->data.info.dns_name.string;
+ dns_len = nrec->data.info.dns_name.size;
+ nb_name = nrec->data.info.netbios_name.string;
+ sid = &nrec->data.info.sid;
+ break;
+ }
+
+ if (!dns_name) continue;
+
+ /* check if this is already taken and not excluded */
+ for (i = 0; i < tdo_fti->count; i++) {
+ trec = &tdo_fti->records[i].record;
+
+ switch (trec->type) {
+ case FOREST_TRUST_TOP_LEVEL_NAME:
+ ex_rule = false;
+ tname = trec->data.name.string;
+ tlen = trec->data.name.size;
+ break;
+ case FOREST_TRUST_TOP_LEVEL_NAME_EX:
+ ex_rule = true;
+ tname = trec->data.name.string;
+ tlen = trec->data.name.size;
+ break;
+ case FOREST_TRUST_DOMAIN_INFO:
+ ex_rule = false;
+ tname = trec->data.info.dns_name.string;
+ tlen = trec->data.info.dns_name.size;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = dns_cmp(dns_name, dns_len, tname, tlen);
+ switch (ret) {
+ case DNS_CMP_MATCH:
+ /* if it matches exclusion,
+ * it doesn't conflict */
+ if (ex_rule) {
+ exclusion = true;
+ break;
+ }
+
+ FALL_THROUGH;
+ case DNS_CMP_FIRST_IS_CHILD:
+ case DNS_CMP_SECOND_IS_CHILD:
+ tln_conflict = true;
+
+ FALL_THROUGH;
+ default:
+ break;
+ }
+
+ /* explicit exclusion, no dns name conflict here */
+ if (exclusion) {
+ tln_conflict = false;
+ }
+
+ if (trec->type != FOREST_TRUST_DOMAIN_INFO) {
+ continue;
+ }
+
+ /* also test for domain info */
+ if (!(trec->flags & LSA_SID_DISABLED_ADMIN) &&
+ dom_sid_compare(&trec->data.info.sid, sid) == 0) {
+ sid_conflict = true;
+ }
+ if (!(trec->flags & LSA_NB_DISABLED_ADMIN) &&
+ strcasecmp_m(trec->data.info.netbios_name.string,
+ nb_name) == 0) {
+ nb_conflict = true;
+ }
+ }
+
+ if (tln_conflict) {
+ (void)add_collision(c_info, new_fti_idx,
+ collision_type,
+ LSA_TLN_DISABLED_CONFLICT,
+ tdo_name);
+ }
+ if (sid_conflict) {
+ (void)add_collision(c_info, new_fti_idx,
+ collision_type,
+ LSA_SID_DISABLED_CONFLICT,
+ tdo_name);
+ }
+ if (nb_conflict) {
+ (void)add_collision(c_info, new_fti_idx,
+ collision_type,
+ LSA_NB_DISABLED_CONFLICT,
+ tdo_name);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS add_collision(struct lsa_ForestTrustCollisionInfo *c_info,
+ uint32_t idx, uint32_t collision_type,
+ uint32_t conflict_type, const char *tdo_name)
+{
+ struct lsa_ForestTrustCollisionRecord **es;
+ uint32_t i = c_info->count;
+
+ es = talloc_realloc(c_info, c_info->entries,
+ struct lsa_ForestTrustCollisionRecord *, i + 1);
+ if (!es) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ c_info->entries = es;
+ c_info->count = i + 1;
+
+ es[i] = talloc(es, struct lsa_ForestTrustCollisionRecord);
+ if (!es[i]) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ es[i]->index = idx;
+ es[i]->type = collision_type;
+ es[i]->flags = conflict_type;
+ es[i]->name.string = talloc_strdup(es[i], tdo_name);
+ if (!es[i]->name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ es[i]->name.size = strlen(es[i]->name.string);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_ft_info(TALLOC_CTX *mem_ctx,
+ struct pdb_trusted_domain *td,
+ struct ForestTrustInfo *info)
+{
+ enum ndr_err_code ndr_err;
+
+ if (td->trust_forest_trust_info.length == 0 ||
+ td->trust_forest_trust_info.data == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ ndr_err = ndr_pull_struct_blob_all(&td->trust_forest_trust_info, mem_ctx,
+ info,
+ (ndr_pull_flags_fn_t)ndr_pull_ForestTrustInfo);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS own_ft_info(struct pdb_domain_info *dom_info,
+ struct ForestTrustInfo *fti)
+{
+ struct ForestTrustDataDomainInfo *info;
+ struct ForestTrustInfoRecord *rec;
+
+ fti->version = 1;
+ fti->count = 2;
+ fti->records = talloc_array(fti,
+ struct ForestTrustInfoRecordArmor, 2);
+ if (!fti->records) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* TLN info */
+ rec = &fti->records[0].record;
+
+ rec->flags = 0;
+ rec->timestamp = 0;
+ rec->type = FOREST_TRUST_TOP_LEVEL_NAME;
+
+ rec->data.name.string = talloc_strdup(fti, dom_info->dns_forest);
+ if (!rec->data.name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ rec->data.name.size = strlen(rec->data.name.string);
+
+ /* DOMAIN info */
+ rec = &fti->records[1].record;
+
+ rec->flags = 0;
+ rec->timestamp = 0;
+ rec->type = FOREST_TRUST_DOMAIN_INFO;
+
+ info = &rec->data.info;
+
+ info->sid = dom_info->sid;
+ info->dns_name.string = talloc_strdup(fti, dom_info->dns_domain);
+ if (!info->dns_name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->dns_name.size = strlen(info->dns_name.string);
+ info->netbios_name.string = talloc_strdup(fti, dom_info->name);
+ if (!info->netbios_name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->netbios_name.size = strlen(info->netbios_name.string);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _lsa_lsaRSetForestTrustInformation(struct pipes_struct *p,
+ struct lsa_lsaRSetForestTrustInformation *r)
+{
+ NTSTATUS status;
+ int i;
+ int j;
+ struct lsa_info *handle;
+ uint32_t num_domains;
+ struct pdb_trusted_domain **domains;
+ struct ForestTrustInfo *nfti;
+ struct ForestTrustInfo *fti;
+ struct lsa_ForestTrustCollisionInfo *c_info;
+ struct pdb_domain_info *dom_info;
+ enum ndr_err_code ndr_err;
+
+ if (!IS_DC) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ handle = find_policy_by_hnd(p,
+ r->in.handle,
+ LSA_HANDLE_TRUST_TYPE,
+ struct lsa_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!(handle->access & LSA_TRUSTED_SET_AUTH)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pdb_enum_trusted_domains(p->mem_ctx, &num_domains, &domains);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (num_domains == 0) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ if (domains[i]->domain_name == NULL) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+ if (strcasecmp_m(domains[i]->domain_name,
+ r->in.trusted_domain_name->string) == 0) {
+ break;
+ }
+ }
+ if (i >= num_domains) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ if (!(domains[i]->trust_attributes &
+ LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.highest_record_type >= LSA_FOREST_TRUST_RECORD_TYPE_LAST) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* The following section until COPY_END is a copy from
+ * source4/rpmc_server/lsa/scesrc_lsa.c */
+ nfti = talloc(p->mem_ctx, struct ForestTrustInfo);
+ if (!nfti) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = make_ft_info(nfti, r->in.forest_trust_info, nfti);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ c_info = talloc_zero(r->out.collision_info,
+ struct lsa_ForestTrustCollisionInfo);
+ if (!c_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* first check own info, then other domains */
+ fti = talloc(p->mem_ctx, struct ForestTrustInfo);
+ if (!fti) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dom_info = pdb_get_domain_info(p->mem_ctx);
+
+ status = own_ft_info(dom_info, fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = check_ft_info(c_info, dom_info->dns_domain, fti, nfti, c_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (j = 0; j < num_domains; j++) {
+ fti = talloc(p->mem_ctx, struct ForestTrustInfo);
+ if (!fti) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = get_ft_info(p->mem_ctx, domains[j], fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ }
+ return status;
+ }
+
+ if (domains[j]->domain_name == NULL) {
+ return NT_STATUS_INVALID_DOMAIN_STATE;
+ }
+
+ status = check_ft_info(c_info, domains[j]->domain_name,
+ fti, nfti, c_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (c_info->count != 0) {
+ *r->out.collision_info = c_info;
+ }
+
+ if (r->in.check_only != 0) {
+ return NT_STATUS_OK;
+ }
+
+ /* COPY_END */
+
+ ndr_err = ndr_push_struct_blob(&domains[i]->trust_forest_trust_info,
+ p->mem_ctx, nfti,
+ (ndr_push_flags_fn_t)ndr_push_ForestTrustInfo);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = pdb_set_trusted_domain(domains[i]->domain_name, domains[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _lsa_CREDRRENAME(struct pipes_struct *p,
+ struct lsa_CREDRRENAME *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSAROPENPOLICYSCE(struct pipes_struct *p,
+ struct lsa_LSAROPENPOLICYSCE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARADTREGISTERSECURITYEVENTSOURCE(struct pipes_struct *p,
+ struct lsa_LSARADTREGISTERSECURITYEVENTSOURCE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE(struct pipes_struct *p,
+ struct lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARADTREPORTSECURITYEVENT(struct pipes_struct *p,
+ struct lsa_LSARADTREPORTSECURITYEVENT *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+void _lsa_Opnum82NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum82NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum83NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum83NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum84NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum84NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum85NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum85NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum86NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum86NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum87NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum87NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum88NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum88NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum89NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum89NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum90NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum90NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum91NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum91NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum92NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum92NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum93NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum93NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum94NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum94NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum95NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum95NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum96NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum96NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum97NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum97NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum98NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum98NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum99NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum99NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum100NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum100NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum101NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum101NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum102NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum102NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum103NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum103NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum104NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum104NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum105NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum105NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum106NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum106NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum107NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum107NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum108NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum108NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum109NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum109NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum110NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum110NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum111NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum111NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum112NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum112NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum113NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum113NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum114NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum114NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum115NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum115NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum116NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum116NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum117NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum117NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum118NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum118NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum119NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum119NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum120NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum120NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum121NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum121NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum122NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum122NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum123NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum123NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum124NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum124NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum125NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum125NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum126NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum126NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum127NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum127NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _lsa_Opnum128NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum128NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+/***************************************************************************
+ _lsa_CreateTrustedDomainEx3
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateTrustedDomainEx3(struct pipes_struct *p,
+ struct lsa_CreateTrustedDomainEx3 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/***************************************************************************
+ _lsa_OpenPolicy3
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenPolicy3(struct pipes_struct *p,
+ struct lsa_OpenPolicy3 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd = NULL;
+ size_t sd_size;
+ uint32_t des_access = r->in.access_mask;
+ uint32_t acc_granted;
+ NTSTATUS status;
+
+ if (p->transport != NCACN_NP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ /*
+ * The attributes have no effect and MUST be ignored, except the
+ * root_dir which MUST be NULL.
+ */
+ if (r->in.attr != NULL && r->in.attr->root_dir != NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (r->in.in_version) {
+ case 1:
+ *r->out.out_version = 1;
+
+ r->out.out_revision_info->info1.revision = 1;
+ /* TODO: Enable as soon as we support it */
+#if 0
+ r->out.out_revision_info->info1.supported_features =
+ LSA_FEATURE_TDO_AUTH_INFO_AES_CIPHER;
+#endif
+
+ break;
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /* Work out max allowed. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_policy_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ status = make_lsa_object_sd(p->mem_ctx,
+ &psd,
+ &sd_size,
+ &lsa_policy_mapping,
+ NULL,
+ 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = access_check_object(psd,
+ session_info->security_token,
+ SEC_PRIV_INVALID,
+ SEC_PRIV_INVALID,
+ 0,
+ des_access,
+ &acc_granted,
+ "_lsa_OpenPolicy2");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = create_lsa_policy_handle(p->mem_ctx,
+ p,
+ LSA_HANDLE_POLICY_TYPE,
+ acc_granted,
+ get_global_sam_sid(),
+ NULL,
+ psd,
+ r->out.handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+void _lsa_Opnum131NotUsedOnWire(struct pipes_struct *p,
+ struct lsa_Opnum131NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+NTSTATUS _lsa_lsaRQueryForestTrustInformation2(struct pipes_struct *p,
+ struct lsa_lsaRQueryForestTrustInformation2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_lsaRSetForestTrustInformation2(struct pipes_struct *p,
+ struct lsa_lsaRSetForestTrustInformation2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+#include "librpc/rpc/dcesrv_core.h"
+
+#define DCESRV_INTERFACE_LSARPC_BIND(context, iface) \
+ dcesrv_interface_lsarpc_bind(context, iface)
+
+static NTSTATUS dcesrv_interface_lsarpc_bind(
+ struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ return dcesrv_interface_bind_reject_connect(context, iface);
+}
+
+static NTSTATUS lsarpc__op_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+static const struct dcesrv_interface dcesrv_lsarpc_interface;
+
+#define NCACN_NP_PIPE_NETLOGON "ncacn_np:[\\pipe\\netlogon]"
+#define NCACN_NP_PIPE_LSASS "ncacn_np:[\\pipe\\lsass]"
+
+#define DCESRV_INTERFACE_LSARPC_NCACN_NP_SECONDARY_ENDPOINT \
+ NCACN_NP_PIPE_LSASS
+
+#define DCESRV_INTERFACE_LSARPC_INIT_SERVER \
+ dcesrv_interface_lsarpc_init_server
+
+static NTSTATUS dcesrv_interface_lsarpc_init_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ NTSTATUS ret = dcesrv_interface_register(dce_ctx,
+ NCACN_NP_PIPE_NETLOGON,
+ NCACN_NP_PIPE_LSASS,
+ &dcesrv_lsarpc_interface,
+ NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DBG_ERR("Failed to register endpoint "
+ "'\\pipe\\netlogon'\n");
+ return ret;
+ }
+
+ return lsarpc__op_init_server(dce_ctx, ep_server);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_lsa_scompat.c"
diff --git a/source3/rpc_server/mdssvc/README b/source3/rpc_server/mdssvc/README
new file mode 100644
index 0000000..7dff83e
--- /dev/null
+++ b/source3/rpc_server/mdssvc/README
@@ -0,0 +1,14 @@
+Introduction:
+=============
+This directory contains source code for the metadata search service
+aka Spotlight.
+
+Bison and flex:
+===============
+Not yet integrated into the waf buildsystem, run these by hand:
+
+$ bison -d -o sparql_parser.c sparql_parser.y
+$ flex -o sparql_lexer.c sparql_lexer.l
+
+or use the bundled Makefile.
+
diff --git a/source3/rpc_server/mdssvc/dalloc.c b/source3/rpc_server/mdssvc/dalloc.c
new file mode 100644
index 0000000..44db9f8
--- /dev/null
+++ b/source3/rpc_server/mdssvc/dalloc.c
@@ -0,0 +1,404 @@
+/*
+ Copyright (c) Ralph Boehme 2012-2014
+
+ 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 "replace.h"
+#include <talloc.h>
+#include "dalloc.h"
+#include "marshalling.h"
+#include "lib/util/charset/charset.h"
+#include "lib/util/talloc_stack.h"
+#include "system/time.h"
+
+/**
+ * Dynamic Datastore
+ **/
+struct dalloc_ctx {
+ void **dd_talloc_array;
+};
+
+void *_dalloc_new(TALLOC_CTX *mem_ctx, const char *type)
+{
+ void *p;
+
+ p = talloc_zero(mem_ctx, DALLOC_CTX);
+ if (p == NULL) {
+ return NULL;
+ }
+ talloc_set_name_const(p, type);
+
+ return p;
+}
+
+int _dalloc_add_talloc_chunk(DALLOC_CTX *dd, void *obj, const char *type, size_t size)
+{
+ size_t array_len = talloc_array_length(dd->dd_talloc_array);
+
+ dd->dd_talloc_array = talloc_realloc(dd,
+ dd->dd_talloc_array,
+ void *,
+ array_len + 1);
+ if (dd->dd_talloc_array == NULL) {
+ return -1;
+ }
+
+ if (size != 0) {
+ void *p;
+
+ p = talloc_named_const(dd->dd_talloc_array, size, type);
+ if (p == NULL) {
+ return -1;
+ }
+ memcpy(p, obj, size);
+ obj = p;
+ } else {
+ _talloc_get_type_abort(obj, type, __location__);
+ }
+
+ dd->dd_talloc_array[array_len] = obj;
+
+ return 0;
+}
+
+/* Get number of elements, returns 0 if the structure is empty or not initialized */
+size_t dalloc_size(const DALLOC_CTX *d)
+{
+ if (d == NULL) {
+ return 0;
+ }
+ return talloc_array_length(d->dd_talloc_array);
+}
+
+/* Return element at position */
+void *dalloc_get_object(const DALLOC_CTX *d, int i)
+{
+ size_t size = dalloc_size(d);
+
+ if (i >= size) {
+ return NULL;
+ }
+
+ return d->dd_talloc_array[i];
+}
+
+/* Return typename of element at position */
+const char *dalloc_get_name(const DALLOC_CTX *d, int i)
+{
+ void *o = dalloc_get_object(d, i);
+
+ if (o == NULL) {
+ return NULL;
+ }
+
+ return talloc_get_name(o);
+}
+
+/*
+ * Get pointer to value from a DALLOC object
+ *
+ * Returns pointer to object from a DALLOC object. Nested object integration
+ * is supported by using the type string "DALLOC_CTX". Any other type string
+ * designates the requested objects type.
+ */
+void *dalloc_get(const DALLOC_CTX *d, ...)
+{
+ int result = 0;
+ void *p = NULL;
+ va_list args;
+ const char *type;
+ int elem;
+
+ va_start(args, d);
+ type = va_arg(args, const char *);
+
+ while (strcmp(type, "DALLOC_CTX") == 0) {
+ elem = va_arg(args, int);
+ if (elem >= talloc_array_length(d->dd_talloc_array)) {
+ result = -1;
+ goto done;
+ }
+ d = d->dd_talloc_array[elem];
+ type = va_arg(args, const char *);
+ }
+
+ elem = va_arg(args, int);
+ if (elem >= talloc_array_length(d->dd_talloc_array)) {
+ result = -1;
+ goto done;
+ }
+
+ p = talloc_check_name(d->dd_talloc_array[elem], type);
+ if (p == NULL) {
+ result = -1;
+ goto done;
+ }
+
+done:
+ va_end(args);
+ if (result != 0) {
+ p = NULL;
+ }
+ return p;
+}
+
+void *dalloc_value_for_key(const DALLOC_CTX *d, ...)
+{
+ int result = 0;
+ void *p = NULL;
+ va_list args;
+ const char *type = NULL;
+ int elem;
+ size_t array_len;
+
+ va_start(args, d);
+ type = va_arg(args, const char *);
+
+ while (strcmp(type, "DALLOC_CTX") == 0) {
+ array_len = talloc_array_length(d->dd_talloc_array);
+ elem = va_arg(args, int);
+ if (elem >= array_len) {
+ result = -1;
+ goto done;
+ }
+ d = d->dd_talloc_array[elem];
+ type = va_arg(args, const char *);
+ }
+
+ array_len = talloc_array_length(d->dd_talloc_array);
+
+ for (elem = 0; elem + 1 < array_len; elem += 2) {
+ if (strcmp(talloc_get_name(d->dd_talloc_array[elem]), "char *") != 0) {
+ result = -1;
+ goto done;
+ }
+ if (strcmp((char *)d->dd_talloc_array[elem],type) == 0) {
+ p = d->dd_talloc_array[elem + 1];
+ break;
+ }
+ }
+ if (p == NULL) {
+ goto done;
+ }
+
+ type = va_arg(args, const char *);
+ if (strcmp(talloc_get_name(p), type) != 0) {
+ p = NULL;
+ }
+
+done:
+ va_end(args);
+ if (result != 0) {
+ p = NULL;
+ }
+ return p;
+}
+
+static char *dalloc_strdup(TALLOC_CTX *mem_ctx, const char *string)
+{
+ char *p;
+
+ p = talloc_strdup(mem_ctx, string);
+ if (p == NULL) {
+ return NULL;
+ }
+ talloc_set_name_const(p, "char *");
+ return p;
+}
+
+int dalloc_stradd(DALLOC_CTX *d, const char *string)
+{
+ int result;
+ char *p;
+
+ p = dalloc_strdup(d, string);
+ if (p == NULL) {
+ return -1;
+ }
+
+ result = dalloc_add(d, p, char *);
+ if (result != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *tab_level(TALLOC_CTX *mem_ctx, int level)
+{
+ int i;
+ char *string = talloc_array(mem_ctx, char, level + 1);
+
+ for (i = 0; i < level; i++) {
+ string[i] = '\t';
+ }
+
+ string[i] = '\0';
+ return string;
+}
+
+char *dalloc_dump(DALLOC_CTX *dd, int nestinglevel)
+{
+ const char *type;
+ int n, result;
+ uint64_t i;
+ sl_bool_t bl;
+ sl_time_t t;
+ struct tm *tm;
+ char datestring[256];
+ sl_cnids_t cnids;
+ char *logstring, *nested_logstring;
+ char *tab_string1, *tab_string2;
+ void *p;
+ bool ok;
+ char *utf8string;
+ size_t utf8len;
+
+ tab_string1 = tab_level(dd, nestinglevel);
+ if (tab_string1 == NULL) {
+ return NULL;
+ }
+ tab_string2 = tab_level(dd, nestinglevel + 1);
+ if (tab_string2 == NULL) {
+ return NULL;
+ }
+
+ logstring = talloc_asprintf(dd,
+ "%s%s(#%zu): {\n",
+ tab_string1,
+ talloc_get_name(dd),
+ dalloc_size(dd));
+ if (logstring == NULL) {
+ return NULL;
+ }
+
+ for (n = 0; n < dalloc_size(dd); n++) {
+ type = dalloc_get_name(dd, n);
+ if (type == NULL) {
+ return NULL;
+ }
+ p = dalloc_get_object(dd, n);
+ if (p == NULL) {
+ return NULL;
+ }
+ if (strcmp(type, "DALLOC_CTX") == 0
+ || strcmp(type, "sl_array_t") == 0
+ || strcmp(type, "sl_filemeta_t") == 0
+ || strcmp(type, "sl_dict_t") == 0) {
+ nested_logstring = dalloc_dump(p, nestinglevel + 1);
+ if (nested_logstring == NULL) {
+ return NULL;
+ }
+ logstring = talloc_strdup_append(logstring,
+ nested_logstring);
+ } else if (strcmp(type, "uint64_t") == 0) {
+ memcpy(&i, p, sizeof(uint64_t));
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%suint64_t: 0x%04jx\n",
+ tab_string2, (uintmax_t)i);
+ } else if (strcmp(type, "char *") == 0) {
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%sstring: %s\n",
+ tab_string2,
+ (char *)p);
+ } else if (strcmp(type, "smb_ucs2_t *") == 0) {
+ ok = convert_string_talloc(talloc_tos(),
+ CH_UTF16LE,
+ CH_UTF8,
+ p,
+ talloc_get_size(p),
+ &utf8string,
+ &utf8len);
+ if (!ok) {
+ return NULL;
+ }
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%sUTF16-string: %s\n",
+ tab_string2,
+ utf8string);
+ TALLOC_FREE(utf8string);
+ } else if (strcmp(type, "sl_bool_t") == 0) {
+ memcpy(&bl, p, sizeof(sl_bool_t));
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%sbool: %s\n",
+ tab_string2,
+ bl ? "true" : "false");
+ } else if (strcmp(type, "sl_nil_t") == 0) {
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%snil\n",
+ tab_string2);
+ } else if (strcmp(type, "sl_time_t") == 0) {
+ memcpy(&t, p, sizeof(sl_time_t));
+ tm = localtime(&t.tv_sec);
+ if (tm == NULL) {
+ return NULL;
+ }
+ result = strftime(datestring,
+ sizeof(datestring),
+ "%Y-%m-%d %H:%M:%S", tm);
+ if (result == 0) {
+ return NULL;
+ }
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%ssl_time_t: %s.%06lu\n",
+ tab_string2,
+ datestring,
+ (unsigned long)t.tv_usec);
+ } else if (strcmp(type, "sl_cnids_t") == 0) {
+ memcpy(&cnids, p, sizeof(sl_cnids_t));
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%sCNIDs: unkn1: 0x%" PRIx16 ", unkn2: 0x%" PRIx32 "\n",
+ tab_string2,
+ cnids.ca_unkn1,
+ cnids.ca_context);
+ if (logstring == NULL) {
+ return NULL;
+ }
+ if (cnids.ca_cnids) {
+ nested_logstring = dalloc_dump(
+ cnids.ca_cnids,
+ nestinglevel + 2);
+ if (!nested_logstring) {
+ return NULL;
+ }
+ logstring = talloc_strdup_append(logstring,
+ nested_logstring);
+ }
+ } else {
+ logstring = talloc_asprintf_append(
+ logstring,
+ "%stype: %s\n",
+ tab_string2,
+ type);
+ }
+ if (logstring == NULL) {
+ return NULL;
+ }
+ }
+ logstring = talloc_asprintf_append(logstring,
+ "%s}\n",
+ tab_string1);
+ if (logstring == NULL) {
+ return NULL;
+ }
+ return logstring;
+}
diff --git a/source3/rpc_server/mdssvc/dalloc.h b/source3/rpc_server/mdssvc/dalloc.h
new file mode 100644
index 0000000..69650b8
--- /dev/null
+++ b/source3/rpc_server/mdssvc/dalloc.h
@@ -0,0 +1,165 @@
+/*
+ Copyright (c) Ralph Boehme 2012-2014
+
+ 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/>.
+*/
+
+/*
+ Typesafe, dynamic object store based on talloc
+
+ Usage
+ =====
+
+ Define some types:
+
+ A key/value store aka dictionary that supports retrieving elements
+ by key:
+
+ typedef dict_t DALLOC_CTX;
+
+ An ordered set that can store different objects which can be
+ retrieved by number:
+
+ typedef set_t DALLOC_CTX;
+
+ Create an dalloc object and add elementes of different type:
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ DALLOC_CTX *d = dalloc_new(mem_ctx);
+
+ Store an int value in the object:
+
+ uint64_t i = 1;
+ dalloc_add_copy(d, &i, uint64_t);
+
+ Store a string:
+
+ dalloc_stradd(d, "hello world");
+
+ Add a nested object:
+
+ DALLOC_CTX *nested = dalloc_new(d);
+ dalloc_add(d, nested, DALLOC_CTX);
+
+ Add an int value to the nested object, this can be fetched:
+
+ i = 2;
+ dalloc_add_copy(nested, &i, uint64_t);
+
+ Add a nested set:
+
+ set_t *set = dalloc_zero(nested, set_t);
+ dalloc_add(nested, set, set_t);
+
+ Add an int value to the set:
+
+ i = 3;
+ dalloc_add_copy(set, &i, uint64_t);
+
+ Add a dictionary (key/value store):
+
+ dict_t *dict = dalloc_zero(nested, dict_t);
+ dalloc_add(nested, dict, dict_t);
+
+ Store a string as key in the dict:
+
+ dalloc_stradd(dict, "key");
+
+ Add a value for the key:
+
+ i = 4;
+ dalloc_add_copy(dict, &i, uint64_t);
+
+ Fetching value references
+ =========================
+
+ You can fetch anything that is not a DALLOC_CTXs, because passing
+ "DALLOC_CTXs" as type to the functions dalloc_get() and
+ dalloc_value_for_key() tells the function to step into that object
+ and expect more arguments that specify which element to fetch.
+
+ Get reference to an objects element by position:
+
+ uint64_t *p = dalloc_get(d, "uint64_t", 0);
+
+ p now points to the first int with a value of 1.
+
+ Get reference to the "hello world" string:
+
+ str = dalloc_get(d, "char *", 1);
+
+ You can't fetch a DALLOC_CTX itself:
+
+ nested = dalloc_get(d, "DALLOC_CTX", 2);
+
+ But you can fetch elements from the nested DALLOC_CTX:
+
+ p = dalloc_get(d, "DALLOC_CTX", 2, "uint64_t", 0);
+
+ p now points to the value 2.
+
+ You can fetch types that are typedefd DALLOC_CTXs:
+
+ set = dalloc_get(d, "DALLOC_CTX", 2, "set_t", 1);
+
+ Fetch int from set, must use DALLOC_CTX as type for the set:
+
+ p = dalloc_get(d, "DALLOC_CTX", 2, "DALLOC_CTX", 1, "uint64_t", 0);
+
+ p points to 3.
+
+ Fetch value by key from dictionary:
+
+ p = dalloc_value_for_key(d, "DALLOC_CTX", 2, "DALLOC_CTX", 2, "key");
+
+ p now points to 4.
+*/
+
+#ifndef DALLOC_H
+#define DALLOC_H
+
+#include <talloc.h>
+
+struct dalloc_ctx;
+typedef struct dalloc_ctx DALLOC_CTX;
+
+#define dalloc_new(mem_ctx) (DALLOC_CTX *)_dalloc_new((mem_ctx), "DALLOC_CTX")
+#define dalloc_zero(mem_ctx, type) (type *)_dalloc_new((mem_ctx), #type)
+
+/**
+ * talloc a chunk for obj of required size, copy the obj into the
+ * chunk and add the chunk to the dalloc ctx
+ **/
+#define dalloc_add_copy(d, obj, type) _dalloc_add_talloc_chunk((d), (obj), #type, sizeof(type))
+
+/**
+ * Add a pointer to a talloced object to the dalloc ctx. The object
+ * must be a talloc child of the dalloc ctx.
+ **/
+#define dalloc_add(d, obj, type) _dalloc_add_talloc_chunk((d), (obj), #type, 0)
+
+
+extern void *dalloc_get(const DALLOC_CTX *d, ...);
+extern void *dalloc_value_for_key(const DALLOC_CTX *d, ...);
+extern size_t dalloc_size(const DALLOC_CTX *d);
+extern void *dalloc_get_object(const DALLOC_CTX *d, int i);
+extern const char *dalloc_get_name(const DALLOC_CTX *d, int i);
+extern int dalloc_stradd(DALLOC_CTX *d, const char *string);
+
+extern void *_dalloc_new(TALLOC_CTX *mem_ctx, const char *type);
+extern int _dalloc_add_talloc_chunk(DALLOC_CTX *d, void *obj, const char *type, size_t size);
+
+extern char *dalloc_dump(DALLOC_CTX *dd, int nestinglevel);
+
+#endif /* DALLOC_H */
diff --git a/source3/rpc_server/mdssvc/elasticsearch_mappings.json b/source3/rpc_server/mdssvc/elasticsearch_mappings.json
new file mode 100644
index 0000000..9f68a64
--- /dev/null
+++ b/source3/rpc_server/mdssvc/elasticsearch_mappings.json
@@ -0,0 +1,142 @@
+{
+ "attribute_mappings": {
+ "*": {
+ "type": "fts",
+ "attribute": ""
+ },
+ "kMDItemTextContent": {
+ "type": "str",
+ "attribute": "content"
+ },
+ "_kMDItemGroupId": {
+ "type": "type",
+ "attribute": "file.content_type"
+ },
+ "kMDItemContentType": {
+ "type": "type",
+ "attribute": "file.content_type"
+ },
+ "kMDItemContentTypeTree": {
+ "type": "type",
+ "attribute": "file.content_type"
+ },
+ "kMDItemFSContentChangeDate": {
+ "type": "date",
+ "attribute": "file.last_modified"
+ },
+ "kMDItemFSCreationDate": {
+ "type": "date",
+ "attribute": "file.created"
+ },
+ "kMDItemFSName": {
+ "type": "str",
+ "attribute": "file.filename"
+ },
+ "kMDItemFSOwnerGroupID": {
+ "type": "str",
+ "attribute": "attributes.owner"
+ },
+ "kMDItemFSOwnerUserID": {
+ "type": "str",
+ "attribute": "attributes.group"
+ },
+ "kMDItemFSSize": {
+ "type": "num",
+ "attribute": "file.filesize"
+ },
+ "kMDItemPath": {
+ "type": "str",
+ "attribute": "path.real"
+ },
+ "kMDItemAttributeChangeDate": {
+ "type": "date",
+ "attribute": "file.last_modified"
+ },
+ "kMDItemAuthors": {
+ "type": "str",
+ "attribute": "meta.author"
+ },
+ "kMDItemContentCreationDate": {
+ "type": "date",
+ "attribute": "file.created"
+ },
+ "kMDItemContentModificationDate": {
+ "type": "date",
+ "attribute": "file.last_modified"
+ },
+ "kMDItemCreator": {
+ "type": "str",
+ "attribute": "meta.raw.creator"
+ },
+ "kMDItemDescription": {
+ "type": "str",
+ "attribute": "meta.raw.description"
+ },
+ "kMDItemDisplayName": {
+ "type": "str",
+ "attribute": "file.filename"
+ },
+ "kMDItemDurationSeconds": {
+ "type": "num",
+ "attribute": "meta.raw.xmpDM:duration"
+ },
+ "kMDItemNumberOfPages": {
+ "type": "num",
+ "attribute": "meta.raw.xmpTPg:NPages"
+ },
+ "kMDItemTitle": {
+ "type": "str",
+ "attribute": "meta.title"
+ },
+ "kMDItemAlbum": {
+ "type": "str",
+ "attribute": "meta.raw.xmpDM:album"
+ },
+ "kMDItemBitsPerSample": {
+ "type": "num",
+ "attribute": "meta.raw.tiff:BitsPerSample"
+ },
+ "kMDItemPixelHeight": {
+ "type": "num",
+ "attribute": "meta.raw.Image Height"
+ },
+ "kMDItemPixelWidth": {
+ "type": "num",
+ "attribute": "meta.raw.Image Width"
+ },
+ "kMDItemResolutionHeightDPI": {
+ "type": "num",
+ "attribute": "meta.raw.Y Resolution"
+ },
+ "kMDItemResolutionWidthDPI": {
+ "type": "num",
+ "attribute": "meta.raw.X Resolution"
+ }
+ },
+ "mime_mappings": {
+ "1": "message/rfc822",
+ "2": "text/x-vcard",
+ "6": "text/x-vcard",
+ "7": "video/*",
+ "8": "application/octet-stream",
+ "9": "text/directory",
+ "10": "audio/*",
+ "11": "application/pdf",
+ "12": "application/vnd.oasis.opendocument.presentation",
+ "13": "image/*",
+ "public.content": "message/rfc822 application/pdf application/vnd.oasis.opendocument.presentation image/* text/*",
+ "public.jpeg": "image/jpeg",
+ "public.tiff": "image/tiff",
+ "com.compuserve.gif": "image/gif",
+ "public.png": "image/png",
+ "com.microsoft.bmp": "image/bmp",
+ "public.mp3": "audio/mpeg",
+ "public.mpeg-4-audio": "audio/x-aac",
+ "public.text": "text/*",
+ "public.plain-text": "text/plain",
+ "public.rtf": "text/rtf",
+ "public.html": "text/html",
+ "public.xml": "text/xml",
+ "public.archive": "application/zip application/x-bzip application/x-bzip2 application/x-tar application/x-7z-compressed"
+ }
+}
diff --git a/source3/rpc_server/mdssvc/es_lexer.l b/source3/rpc_server/mdssvc/es_lexer.l
new file mode 100644
index 0000000..4be4225
--- /dev/null
+++ b/source3/rpc_server/mdssvc/es_lexer.l
@@ -0,0 +1,92 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / Elasticsearch backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+%{
+#include "includes.h"
+#include "rpc_server/mdssvc/es_parser.tab.h"
+
+#define YY_NO_INPUT
+#define mdsyylalloc SMB_MALLOC
+#define mdsyylrealloc SMB_REALLOC
+
+static char *strip_quote(const char *phrase);
+%}
+
+%option nounput noyyalloc noyyrealloc prefix="mdsyyl"
+
+ASC [a-zA-Z0-9_\*\:\-\.]
+U [\x80-\xbf]
+U2 [\xc2-\xdf]
+U3 [\xe0-\xef]
+U4 [\xf0-\xf4]
+SPECIAL [\!\#\$\%\&\'\(\)\+\,\.\/\;\<\=\>\?\@\[\]\^\`\{\}\|\~\\]
+ESCHAR [\"\*]
+BLANK [ \t\n]
+
+UANY {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+UPHRASE {UANY}|{SPECIAL}|{BLANK}|\\{ESCHAR}
+
+%%
+InRange return FUNC_INRANGE;
+\$time\.iso return DATE_ISO;
+false {mdsyyllval.bval = false; return BOOLEAN;}
+true {mdsyyllval.bval = true; return BOOLEAN;}
+\" return QUOTE;
+\( return OBRACE;
+\) return CBRACE;
+\&\& return AND;
+\|\| return OR;
+\=\= return EQUAL;
+\!\= return UNEQUAL;
+\= return EQUAL;
+\< return LT;
+\> return GT;
+\, return COMMA;
+{UANY}+ {mdsyyllval.sval = talloc_strdup(talloc_tos(), yytext); return WORD;}
+\"{UPHRASE}+\" {mdsyyllval.sval = strip_quote(yytext); return PHRASE;}
+{BLANK} /* ignore */
+%%
+
+static char *strip_quote(const char *phrase)
+{
+ size_t phrase_len = 0;
+ char *stripped_phrase = NULL;
+
+ if (phrase == NULL) {
+ return NULL;
+ }
+
+ phrase_len = strlen(phrase);
+ if (phrase_len < 2 ||
+ phrase[0] != '\"' ||
+ phrase[phrase_len - 1] != '\"')
+ {
+ return talloc_strdup(talloc_tos(), phrase);
+ }
+
+ phrase++;
+
+ stripped_phrase = talloc_strndup(talloc_tos(), phrase, phrase_len - 2);
+ if (stripped_phrase == NULL) {
+ return NULL;
+ }
+ return stripped_phrase;
+}
diff --git a/source3/rpc_server/mdssvc/es_mapping.c b/source3/rpc_server/mdssvc/es_mapping.c
new file mode 100644
index 0000000..e8d181d
--- /dev/null
+++ b/source3/rpc_server/mdssvc/es_mapping.c
@@ -0,0 +1,241 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / Elasticsearch backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "es_mapping.h"
+
+/*
+ * Escaping of special characters in Lucene query syntax across HTTP and JSON
+ * ==========================================================================
+ *
+ * These characters in Lucene queries need escaping [1]:
+ *
+ * + - & | ! ( ) { } [ ] ^ " ~ * ? : \ /
+ *
+ * Additionally JSON requires escaping of:
+ *
+ * " \
+ *
+ * Characters already escaped by the mdssvc client:
+ *
+ * * " \
+ *
+ * The following table contains the resulting escaped strings, beginning with the
+ * search term, the corresponding Spotlight query and the final string that gets
+ * sent to the target Elasticsearch server.
+ *
+ * string | mdfind | http
+ * -------+--------+------
+ * x!x x!x x\\!x
+ * x&x x&x x\\&x
+ * x+x x+x x\\+x
+ * x-x x-x x\\-x
+ * x.x x.x x\\.x
+ * x<x x<x x\\<x
+ * x>x x>x x\\>x
+ * x=x x=x x\\=x
+ * x?x x?x x\\?x
+ * x[x x[x x\\[x
+ * x]x x]x x\\]x
+ * x^x x^x x\\^x
+ * x{x x{x x\\{x
+ * x}x x}x x\\}x
+ * x|x x|x x\\|x
+ * x x x x x\\ x
+ * x*x x\*x x\\*x
+ * x\x x\\x x\\\\x
+ * x"x x\"x x\\\"x
+ *
+ * Special cases:
+ * x y It's not possible to search for terms including spaces, Spotlight
+ * will search for x OR y.
+ * x(x Search for terms including ( and ) does not work with Spotlight.
+ *
+ * [1] <http://lucene.apache.org/core/8_2_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters>
+ */
+
+static char *escape_str(TALLOC_CTX *mem_ctx,
+ const char *in,
+ const char *escape_list,
+ const char *escape_exceptions)
+{
+ char *out = NULL;
+ size_t in_len;
+ size_t new_len;
+ size_t in_pos;
+ size_t out_pos = 0;
+
+ if (in == NULL) {
+ return NULL;
+ }
+ in_len = strlen(in);
+
+ if (escape_list == NULL) {
+ escape_list = "";
+ }
+ if (escape_exceptions == NULL) {
+ escape_exceptions = "";
+ }
+
+ /*
+ * Allocate enough space for the worst case: every char needs to be
+ * escaped and requires an additional char.
+ */
+ new_len = (in_len * 2) + 1;
+ if (new_len <= in_len) {
+ return NULL;
+ }
+
+ out = talloc_zero_array(mem_ctx, char, new_len);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ for (in_pos = 0, out_pos = 0; in_pos < in_len; in_pos++, out_pos++) {
+ if (strchr(escape_list, in[in_pos]) != NULL &&
+ strchr(escape_exceptions, in[in_pos]) == NULL)
+ {
+ out[out_pos++] = '\\';
+ }
+ out[out_pos] = in[in_pos];
+ }
+
+ return out;
+}
+
+char *es_escape_str(TALLOC_CTX *mem_ctx,
+ const char *in,
+ const char *exceptions)
+{
+ const char *lucene_escape_list = "+-&|!(){}[]^\"~*?:\\/ ";
+ const char *json_escape_list = "\\\"";
+ char *lucene_escaped = NULL;
+ char *full_escaped = NULL;
+
+ lucene_escaped = escape_str(mem_ctx,
+ in,
+ lucene_escape_list,
+ exceptions);
+ if (lucene_escaped == NULL) {
+ return NULL;
+ }
+
+ full_escaped = escape_str(mem_ctx,
+ lucene_escaped,
+ json_escape_list,
+ NULL);
+ TALLOC_FREE(lucene_escaped);
+ return full_escaped;
+}
+
+struct es_attr_map *es_map_sl_attr(TALLOC_CTX *mem_ctx,
+ json_t *kmd_map,
+ const char *sl_attr)
+{
+ struct es_attr_map *es_map = NULL;
+ const char *typestr = NULL;
+ enum ssm_type type = ssmt_bool;
+ char *es_attr = NULL;
+ size_t i;
+ int cmp;
+ int ret;
+
+ static struct {
+ const char *typestr;
+ enum ssm_type typeval;
+ } ssmt_type_map[] = {
+ {"bool", ssmt_bool},
+ {"num", ssmt_num},
+ {"str", ssmt_str},
+ {"fts", ssmt_fts},
+ {"date", ssmt_date},
+ {"type", ssmt_type},
+ };
+
+ if (sl_attr == NULL) {
+ return NULL;
+ }
+
+ ret = json_unpack(kmd_map,
+ "{s: {s: s}}",
+ sl_attr,
+ "type",
+ &typestr);
+ if (ret != 0) {
+ DBG_DEBUG("No JSON type mapping for [%s]\n", sl_attr);
+ return NULL;
+ }
+
+ ret = json_unpack(kmd_map,
+ "{s: {s: s}}",
+ sl_attr,
+ "attribute",
+ &es_attr);
+ if (ret != 0) {
+ DBG_ERR("No JSON attribute mapping for [%s]\n", sl_attr);
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ssmt_type_map); i++) {
+ cmp = strcmp(typestr, ssmt_type_map[i].typestr);
+ if (cmp == 0) {
+ type = ssmt_type_map[i].typeval;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(ssmt_type_map)) {
+ return NULL;
+ }
+
+ es_map = talloc_zero(mem_ctx, struct es_attr_map);
+ if (es_map == NULL) {
+ return NULL;
+ }
+ es_map->type = type;
+
+ es_map->name = es_escape_str(es_map, es_attr, NULL);
+ if (es_map->name == NULL) {
+ TALLOC_FREE(es_map);
+ return false;
+ }
+
+ return es_map;
+}
+
+const char *es_map_sl_type(json_t *mime_map,
+ const char *sl_type)
+{
+ const char *mime_type = NULL;
+ int ret;
+
+ if (sl_type == NULL) {
+ return NULL;
+ }
+
+ ret = json_unpack(mime_map,
+ "{s: s}",
+ sl_type,
+ &mime_type);
+ if (ret != 0) {
+ return NULL;
+ }
+
+ return mime_type;
+}
diff --git a/source3/rpc_server/mdssvc/es_mapping.h b/source3/rpc_server/mdssvc/es_mapping.h
new file mode 100644
index 0000000..29511b5
--- /dev/null
+++ b/source3/rpc_server/mdssvc/es_mapping.h
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / Elasticsearch backend
+
+ Copyright (c) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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 _ES_MAPPING_H_
+#define _ES_MAPPING_H_
+
+#include <jansson.h>
+
+enum ssm_type {
+ ssmt_bool, /* a boolean value */
+ ssmt_num, /* a numeric value */
+ ssmt_str, /* a string value */
+ ssmt_fts, /* a string value */
+ ssmt_date, /* date values */
+ ssmt_type /* kMDItemContentType, requires special mapping */
+};
+
+struct es_attr_map {
+ enum ssm_type type;
+ const char *name;
+};
+
+char *es_escape_str(TALLOC_CTX *mem_ctx,
+ const char *in,
+ const char *exceptions);
+struct es_attr_map *es_map_sl_attr(TALLOC_CTX *mem_ctx,
+ json_t *kmd_map,
+ const char *sl_attr);
+const char *es_map_sl_type(json_t *mime_map,
+ const char *sl_type);
+
+#endif
diff --git a/source3/rpc_server/mdssvc/es_parser.y b/source3/rpc_server/mdssvc/es_parser.y
new file mode 100644
index 0000000..3fbdf93
--- /dev/null
+++ b/source3/rpc_server/mdssvc/es_parser.y
@@ -0,0 +1,686 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / Elasticsearch backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+%{
+ #include "includes.h"
+ #include "rpc_server/mdssvc/mdssvc.h"
+ #include "rpc_server/mdssvc/mdssvc_es.h"
+ #include "rpc_server/mdssvc/es_parser.tab.h"
+ #include "rpc_server/mdssvc/es_mapping.h"
+ #include "lib/util/smb_strtox.h"
+ #include <jansson.h>
+
+ /*
+ * allow building with -O3 -Wp,-D_FORTIFY_SOURCE=2
+ *
+ * /tmp/samba-testbase/.../mdssvc/es_parser.y: In function
+ * ‘mdsyylparse’:
+ * es_parser.tab.c:1124:6: error: assuming pointer wraparound
+ * does not occur when comparing P +- C1 with P +- C2
+ * [-Werror=strict-overflow]
+ *
+ * The generated code in es_parser.tab.c looks like this:
+ *
+ * if (yyss + yystacksize - 1 <= yyssp)
+ */
+ #pragma GCC diagnostic ignored "-Wstrict-overflow"
+
+ #define YYMALLOC SMB_MALLOC
+ #define YYREALLOC SMB_REALLOC
+
+ struct yy_buffer_state;
+ typedef struct yy_buffer_state *YY_BUFFER_STATE;
+ int mdsyyllex(void);
+ void mdsyylerror(char const *);
+ void *mdsyylterminate(void);
+ YY_BUFFER_STATE mdsyyl_scan_string(const char *str);
+ void mdsyyl_delete_buffer(YY_BUFFER_STATE buffer);
+
+ /* forward declarations */
+ static char *isodate_to_sldate(const char *s);
+ static char *map_expr(const struct es_attr_map *attr,
+ char op,
+ const char *val1,
+ const char *val2);
+
+ /* global vars, eg needed by the lexer */
+ struct es_parser_state {
+ TALLOC_CTX *frame;
+ json_t *kmd_map;
+ json_t *mime_map;
+ bool ignore_unknown_attribute;
+ bool ignore_unknown_type;
+ bool type_error;
+ YY_BUFFER_STATE s;
+ const char *result;
+ } *global_es_parser_state;
+%}
+
+%code provides {
+ #include <stdbool.h>
+ #include <jansson.h>
+ #include "rpc_server/mdssvc/mdssvc.h"
+
+ /* 2001-01-01T00:00:00Z - Unix Epoch = SP_RAW_TIME_OFFSET */
+ #define SP_RAW_TIME_OFFSET 978307200
+
+ int mdsyylwrap(void);
+ bool map_spotlight_to_es_query(TALLOC_CTX *mem_ctx,
+ json_t *mappings,
+ const char *path_scope,
+ const char *query_string,
+ char **_es_query);
+}
+
+%union {
+ bool bval;
+ const char *sval;
+ struct es_attr_map *attr_map;
+}
+
+%name-prefix "mdsyyl"
+%expect 1
+%error-verbose
+
+%type <sval> match expr line function value isodate
+%type <attr_map> attribute
+
+%token <sval> WORD PHRASE
+%token <bval> BOOLEAN
+%token FUNC_INRANGE
+%token DATE_ISO
+%token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
+%left OR
+%left AND
+%%
+
+input:
+/* empty */
+| input line
+;
+
+line:
+expr {
+ if ($1 == NULL) {
+ YYABORT;
+ }
+ if (global_es_parser_state->type_error) {
+ YYABORT;
+ }
+ global_es_parser_state->result = $1;
+}
+;
+
+expr:
+OBRACE expr CBRACE {
+ if ($2 == NULL) {
+ $$ = NULL;
+ } else {
+ $$ = talloc_asprintf(talloc_tos(), "(%s)", $2);
+ if ($$ == NULL) YYABORT;
+ }
+}
+| expr AND expr {
+ if ($1 == NULL && $3 == NULL) {
+ $$ = NULL;
+ } else if ($1 == NULL) {
+ $$ = $3;
+ } else if ($3 == NULL) {
+ $$ = $1;
+ } else {
+ $$ = talloc_asprintf(talloc_tos(), "(%s) AND (%s)", $1, $3);
+ if ($$ == NULL) YYABORT;
+ }
+}
+| expr OR expr {
+ if ($1 == NULL && $3 == NULL) {
+ $$ = NULL;
+ } else if ($1 == NULL) {
+ $$ = $3;
+ } else if ($3 == NULL) {
+ $$ = $1;
+ } else {
+ $$ = talloc_asprintf(talloc_tos(), "%s OR %s", $1, $3);
+ if ($$ == NULL) YYABORT;
+ }
+}
+| match {
+ $$ = $1;
+}
+| BOOLEAN {
+ /*
+ * We can't properly handle these in expressions, fortunately this
+ * is probably only ever used by OS X as sole element in an
+ * expression ie "False" (when Finder window selected our share
+ * but no search string entered yet). Packet traces showed that OS
+ * X Spotlight server then returns a failure (ie -1) which is what
+ * we do here too by calling YYABORT.
+ */
+ YYABORT;
+};
+
+match:
+attribute EQUAL value {
+ if ($1 == NULL) {
+ $$ = NULL;
+ } else {
+ $$ = map_expr($1, '=', $3, NULL);
+ }
+}
+| attribute UNEQUAL value {
+ if ($1 == NULL) {
+ $$ = NULL;
+ } else {
+ $$ = map_expr($1, '!', $3, NULL);
+ }
+}
+| attribute LT value {
+ if ($1 == NULL) {
+ $$ = NULL;
+ } else {
+ $$ = map_expr($1, '<', $3, NULL);
+ }
+}
+| attribute GT value {
+ if ($1 == NULL) {
+ $$ = NULL;
+ } else {
+ $$ = map_expr($1, '>', $3, NULL);
+ }
+}
+| function {
+ $$ = $1;
+}
+| match WORD {
+ $$ = $1;
+};
+
+function:
+FUNC_INRANGE OBRACE attribute COMMA WORD COMMA WORD CBRACE {
+ if ($3 == NULL) {
+ $$ = NULL;
+ } else {
+ $$ = map_expr($3, '~', $5, $7);
+ }
+};
+
+attribute:
+WORD {
+ $$ = es_map_sl_attr(global_es_parser_state->frame,
+ global_es_parser_state->kmd_map,
+ $1);
+ if ($$ == NULL &&
+ !global_es_parser_state->ignore_unknown_attribute)
+ {
+ YYABORT;
+ }
+};
+
+value:
+PHRASE {
+ $$ = $1;
+}
+| isodate {
+ $$ = $1;
+};
+
+isodate:
+DATE_ISO OBRACE WORD CBRACE {
+ $$ = isodate_to_sldate($3);
+ if ($$ == NULL) YYABORT;
+};
+
+%%
+
+/*
+ * Spotlight has two date formats:
+ * - seconds since 2001-01-01 00:00:00Z
+ * - as string "$time.iso(%Y-%m-%dT%H:%M:%SZ)"
+ * This function converts the latter to the former as string, so the parser
+ * can work on a uniform format.
+ */
+static char *isodate_to_sldate(const char *isodate)
+{
+ struct es_parser_state *s = global_es_parser_state;
+ struct tm tm = {};
+ const char *p = NULL;
+ char *tstr = NULL;
+ time_t t;
+
+ p = strptime(isodate, "%Y-%m-%dT%H:%M:%SZ", &tm);
+ if (p == NULL) {
+ DBG_ERR("strptime [%s] failed\n", isodate);
+ return NULL;
+ }
+
+ t = timegm(&tm);
+ t -= SP_RAW_TIME_OFFSET;
+
+ tstr = talloc_asprintf(s->frame, "%jd", (intmax_t)t);
+ if (tstr == NULL) {
+ return NULL;
+ }
+
+ return tstr;
+}
+
+static char *map_type(const struct es_attr_map *attr,
+ char op,
+ const char *val)
+{
+ struct es_parser_state *s = global_es_parser_state;
+ const char *mime_type_list = NULL;
+ char *esc_mime_type_list = NULL;
+ const char *not = NULL;
+ const char *end = NULL;
+ char *es = NULL;
+
+ mime_type_list = es_map_sl_type(s->mime_map, val);
+ if (mime_type_list == NULL) {
+ DBG_DEBUG("Mapping type [%s] failed\n", val);
+ if (!s->ignore_unknown_type) {
+ s->type_error = true;
+ }
+ return NULL;
+ }
+
+ esc_mime_type_list = es_escape_str(s->frame,
+ mime_type_list,
+ "* ");
+ if (esc_mime_type_list == NULL) {
+ return NULL;
+ }
+
+ switch (op) {
+ case '=':
+ not = "";
+ end = "";
+ break;
+ case '!':
+ not = "(NOT ";
+ end = ")";
+ break;
+ default:
+ DBG_ERR("Mapping type [%s] unexpected op [%c]\n", val, op);
+ return NULL;
+ }
+ es = talloc_asprintf(s->frame,
+ "%s%s:(%s)%s",
+ not,
+ attr->name,
+ esc_mime_type_list,
+ end);
+ if (es == NULL) {
+ return NULL;
+ }
+
+ return es;
+}
+
+static char *map_num(const struct es_attr_map *attr,
+ char op,
+ const char *val1,
+ const char *val2)
+{
+ struct es_parser_state *s = global_es_parser_state;
+ char *es = NULL;
+
+ switch (op) {
+ case '>':
+ es = talloc_asprintf(s->frame,
+ "%s:{%s TO *}",
+ attr->name,
+ val1);
+ break;
+ case '<':
+ es = talloc_asprintf(s->frame,
+ "%s:{* TO %s}",
+ attr->name,
+ val1);
+ break;
+ case '~':
+ es = talloc_asprintf(s->frame,
+ "%s:[%s TO %s]",
+ attr->name,
+ val1,
+ val2);
+ break;
+ case '=':
+ es = talloc_asprintf(s->frame,
+ "%s:%s",
+ attr->name,
+ val1);
+ break;
+ case '!':
+ es = talloc_asprintf(s->frame,
+ "(NOT %s:%s)",
+ attr->name,
+ val1);
+ break;
+ default:
+ DBG_ERR("Mapping num unexpected op [%c]\n", op);
+ return NULL;
+ }
+ if (es == NULL) {
+ return NULL;
+ }
+
+ return es;
+}
+
+static char *map_fts(const struct es_attr_map *attr,
+ char op,
+ const char *val)
+{
+ struct es_parser_state *s = global_es_parser_state;
+ const char *not = NULL;
+ const char *end = NULL;
+ char *esval = NULL;
+ char *es = NULL;
+
+ esval = es_escape_str(s->frame, val, "*\\\"");
+ if (esval == NULL) {
+ yyerror("es_escape_str failed");
+ return NULL;
+ }
+
+ switch (op) {
+ case '=':
+ not = "";
+ end = "";
+ break;
+ case '!':
+ not = "(NOT ";
+ end = ")";
+ break;
+ default:
+ DBG_ERR("Mapping fts [%s] unexpected op [%c]\n", val, op);
+ return NULL;
+ }
+ es = talloc_asprintf(s->frame,
+ "%s%s%s",
+ not,
+ esval,
+ end);
+ if (es == NULL) {
+ return NULL;
+ }
+ return es;
+}
+
+static char *map_str(const struct es_attr_map *attr,
+ char op,
+ const char *val)
+{
+ struct es_parser_state *s = global_es_parser_state;
+ char *esval = NULL;
+ char *es = NULL;
+ const char *not = NULL;
+ const char *end = NULL;
+
+ esval = es_escape_str(s->frame, val, "*\\\"");
+ if (esval == NULL) {
+ yyerror("es_escape_str failed");
+ return NULL;
+ }
+
+ switch (op) {
+ case '=':
+ not = "";
+ end = "";
+ break;
+ case '!':
+ not = "(NOT ";
+ end = ")";
+ break;
+ default:
+ DBG_ERR("Mapping string [%s] unexpected op [%c]\n", val, op);
+ return NULL;
+ }
+
+ es = talloc_asprintf(s->frame,
+ "%s%s:%s%s",
+ not,
+ attr->name,
+ esval,
+ end);
+ if (es == NULL) {
+ return NULL;
+ }
+ return es;
+}
+
+/*
+ * Convert Spotlight date seconds since 2001-01-01 00:00:00Z
+ * to a date string in the format %Y-%m-%dT%H:%M:%SZ.
+ */
+static char *map_sldate_to_esdate(TALLOC_CTX *mem_ctx,
+ const char *sldate)
+{
+ struct tm *tm = NULL;
+ char *esdate = NULL;
+ char buf[21];
+ size_t len;
+ time_t t;
+ int error;
+
+ t = (time_t)smb_strtoull(sldate, NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ DBG_ERR("smb_strtoull [%s] failed\n", sldate);
+ return NULL;
+ }
+ t += SP_RAW_TIME_OFFSET;
+
+ tm = gmtime(&t);
+ if (tm == NULL) {
+ DBG_ERR("localtime [%s] failed\n", sldate);
+ return NULL;
+ }
+
+ len = strftime(buf, sizeof(buf),
+ "%Y-%m-%dT%H:%M:%SZ", tm);
+ if (len != 20) {
+ DBG_ERR("strftime [%s] failed\n", sldate);
+ return NULL;
+ }
+
+ esdate = es_escape_str(mem_ctx, buf, NULL);
+ if (esdate == NULL) {
+ yyerror("es_escape_str failed");
+ return NULL;
+ }
+ return esdate;
+}
+
+static char *map_date(const struct es_attr_map *attr,
+ char op,
+ const char *sldate1,
+ const char *sldate2)
+{
+ struct es_parser_state *s = global_es_parser_state;
+ char *esdate1 = NULL;
+ char *esdate2 = NULL;
+ char *es = NULL;
+
+ if (op == '~' && sldate2 == NULL) {
+ DBG_ERR("Date range query, but second date is NULL\n");
+ return NULL;
+ }
+
+ esdate1 = map_sldate_to_esdate(s->frame, sldate1);
+ if (esdate1 == NULL) {
+ DBG_ERR("map_sldate_to_esdate [%s] failed\n", sldate1);
+ return NULL;
+ }
+ if (sldate2 != NULL) {
+ esdate2 = map_sldate_to_esdate(s->frame, sldate2);
+ if (esdate2 == NULL) {
+ DBG_ERR("map_sldate_to_esdate [%s] failed\n", sldate2);
+ return NULL;
+ }
+ }
+
+ switch (op) {
+ case '>':
+ es = talloc_asprintf(s->frame,
+ "%s:{%s TO *}",
+ attr->name,
+ esdate1);
+ break;
+ case '<':
+ es = talloc_asprintf(s->frame,
+ "%s:{* TO %s}",
+ attr->name,
+ esdate1);
+ break;
+ case '~':
+ es = talloc_asprintf(s->frame,
+ "%s:[%s TO %s]",
+ attr->name,
+ esdate1,
+ esdate2);
+ break;
+ case '=':
+ es = talloc_asprintf(s->frame,
+ "%s:%s",
+ attr->name,
+ esdate1);
+ break;
+ case '!':
+ es = talloc_asprintf(s->frame,
+ "(NOT %s:%s)",
+ attr->name,
+ esdate1);
+ break;
+ }
+ if (es == NULL) {
+ return NULL;
+ }
+ return es;
+}
+
+static char *map_expr(const struct es_attr_map *attr,
+ char op,
+ const char *val1,
+ const char *val2)
+{
+ char *es = NULL;
+
+ switch (attr->type) {
+ case ssmt_type:
+ es = map_type(attr, op, val1);
+ break;
+ case ssmt_num:
+ es = map_num(attr, op, val1, val2);
+ break;
+ case ssmt_fts:
+ es = map_fts(attr, op, val1);
+ break;
+ case ssmt_str:
+ es = map_str(attr, op, val1);
+ break;
+ case ssmt_date:
+ es = map_date(attr, op, val1, val2);
+ break;
+ default:
+ break;
+ }
+ if (es == NULL) {
+ DBG_DEBUG("Mapping [%s %c %s (%s)] failed\n",
+ attr->name, op, val1, val2 ? val2 : "");
+ return NULL;
+ }
+
+ return es;
+}
+
+void mdsyylerror(const char *str)
+{
+ DBG_ERR("Parser failed: %s\n", str);
+}
+
+int mdsyylwrap(void)
+{
+ return 1;
+}
+
+/**
+ * Map a Spotlight RAW query string to a ES query string
+ **/
+bool map_spotlight_to_es_query(TALLOC_CTX *mem_ctx,
+ json_t *mappings,
+ const char *path_scope,
+ const char *query_string,
+ char **_es_query)
+{
+ struct es_parser_state s = {
+ .frame = talloc_stackframe(),
+ };
+ int result;
+ char *es_query = NULL;
+
+ s.kmd_map = json_object_get(mappings, "attribute_mappings");
+ if (s.kmd_map == NULL) {
+ DBG_ERR("Failed to load attribute_mappings from JSON\n");
+ return false;
+ }
+ s.mime_map = json_object_get(mappings, "mime_mappings");
+ if (s.mime_map == NULL) {
+ DBG_ERR("Failed to load mime_mappings from JSON\n");
+ return false;
+ }
+
+ s.s = mdsyyl_scan_string(query_string);
+ if (s.s == NULL) {
+ DBG_WARNING("Failed to parse [%s]\n", query_string);
+ TALLOC_FREE(s.frame);
+ return false;
+ }
+
+ s.ignore_unknown_attribute = lp_parm_bool(GLOBAL_SECTION_SNUM,
+ "elasticsearch",
+ "ignore unknown attribute",
+ false);
+ s.ignore_unknown_type = lp_parm_bool(GLOBAL_SECTION_SNUM,
+ "elasticsearch",
+ "ignore unknown type",
+ false);
+
+ global_es_parser_state = &s;
+ result = mdsyylparse();
+ global_es_parser_state = NULL;
+ mdsyyl_delete_buffer(s.s);
+
+ if (result != 0) {
+ TALLOC_FREE(s.frame);
+ return false;
+ }
+
+ es_query = talloc_asprintf(mem_ctx,
+ "(%s) AND path.real.fulltext:\\\"%s\\\"",
+ s.result, path_scope);
+ TALLOC_FREE(s.frame);
+ if (es_query == NULL) {
+ return false;
+ }
+
+ *_es_query = es_query;
+ return true;
+}
diff --git a/source3/rpc_server/mdssvc/es_parser_test.c b/source3/rpc_server/mdssvc/es_parser_test.c
new file mode 100644
index 0000000..7d88c67
--- /dev/null
+++ b/source3/rpc_server/mdssvc/es_parser_test.c
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / ES backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/mdssvc/mdssvc.h"
+#include "rpc_server/mdssvc/mdssvc_es.h"
+#include "rpc_server/mdssvc/es_parser.tab.h"
+#include "rpc_server/mdssvc/es_mapping.h"
+
+/*
+ * Examples:
+ *
+ * $ ./spotlight2es '_kMDItemGroupId=="11"'
+ * ...
+ * $ ./spotlight2es '*=="test*"||kMDItemTextContent=="test*"'
+ * ...
+ */
+
+int main(int argc, char **argv)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ json_t *mappings = NULL;
+ json_error_t json_error;
+ char *default_path = NULL;
+ const char *path = NULL;
+ const char *query_string = NULL;
+ const char *path_scope = NULL;
+ char *es_query = NULL;
+ bool ok;
+
+ if (argc != 2) {
+ printf("usage: %s QUERY\n", argv[0]);
+ return 1;
+ }
+ query_string = argv[1];
+ path_scope = "/foo/bar";
+
+ lp_load_global(get_dyn_CONFIGFILE());
+
+ mem_ctx = talloc_init("es_parser_test");
+ if (mem_ctx == NULL) {
+ return 1;
+ }
+
+ default_path = talloc_asprintf(mem_ctx,
+ "%s/mdssvc/elasticsearch_mappings.json",
+ get_dyn_SAMBA_DATADIR());
+ if (default_path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ path = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+ "elasticsearch",
+ "mappings",
+ default_path);
+ if (path == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ mappings = json_load_file(path, 0, &json_error);
+ if (mappings == NULL) {
+ DBG_ERR("Opening mapping file [%s] failed: %s\n",
+ path, strerror(errno));
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ ok = map_spotlight_to_es_query(mem_ctx,
+ mappings,
+ path_scope,
+ query_string,
+ &es_query);
+ printf("%s\n", ok ? es_query : "*mapping failed*");
+
+ json_decref(mappings);
+ talloc_free(mem_ctx);
+ return ok ? 0 : 1;
+}
diff --git a/source3/rpc_server/mdssvc/marshalling.c b/source3/rpc_server/mdssvc/marshalling.c
new file mode 100644
index 0000000..c85fae7
--- /dev/null
+++ b/source3/rpc_server/mdssvc/marshalling.c
@@ -0,0 +1,1422 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2012-2014
+
+ 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 "dalloc.h"
+#include "marshalling.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*
+ * This is used to talloc an array that will hold the table of
+ * contents of a marshalled Spotlight RPC (S-RPC) reply. Each ToC
+ * entry is 8 bytes, so we allocate space for 1024 entries which
+ * should be sufficient for even the largest S-RPC replies.
+ *
+ * The total buffersize for S-RPC packets is typically limited to 64k,
+ * so we can only store so many elements there anyway.
+ */
+#define MAX_SLQ_TOC 1024*64
+#define MAX_SLQ_TOCIDX 1024*8
+#define MAX_SLQ_COUNT 1024*64
+#define MAX_SL_STRLEN 1024
+
+/******************************************************************************
+ * RPC data marshalling and unmarshalling
+ ******************************************************************************/
+
+/* Spotlight epoch is 1.1.2001 00:00 UTC */
+#define SPOTLIGHT_TIME_DELTA 978307200 /* Diff from UNIX epoch to Spotlight epoch */
+
+#define SQ_TYPE_NULL 0x0000
+#define SQ_TYPE_COMPLEX 0x0200
+#define SQ_TYPE_INT64 0x8400
+#define SQ_TYPE_BOOL 0x0100
+#define SQ_TYPE_FLOAT 0x8500
+#define SQ_TYPE_DATA 0x0700
+#define SQ_TYPE_CNIDS 0x8700
+#define SQ_TYPE_UUID 0x0e00
+#define SQ_TYPE_DATE 0x8600
+#define SQ_TYPE_TOC 0x8800
+
+#define SQ_CPX_TYPE_ARRAY 0x0a00
+#define SQ_CPX_TYPE_STRING 0x0c00
+#define SQ_CPX_TYPE_UTF16_STRING 0x1c00
+#define SQ_CPX_TYPE_DICT 0x0d00
+#define SQ_CPX_TYPE_CNIDS 0x1a00
+#define SQ_CPX_TYPE_FILEMETA 0x1b00
+
+struct sl_tag {
+ int type;
+ int count;
+ size_t length;
+ size_t size;
+};
+
+static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf,
+ ssize_t offset, size_t bufsize,
+ char *toc_buf, int *toc_idx, int *count);
+static ssize_t sl_unpack_loop(DALLOC_CTX *query, const char *buf,
+ ssize_t offset, size_t bufsize,
+ int count, ssize_t toc_offset,
+ int encoding);
+static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize);
+
+/******************************************************************************
+ * Wrapper functions for the *VAL macros with bound checking
+ ******************************************************************************/
+
+static ssize_t sl_push_uint64_val(char *buf,
+ ssize_t offset,
+ size_t max_offset,
+ uint64_t val)
+{
+ if (offset + 8 > max_offset) {
+ DEBUG(1, ("%s: offset: %zd, max_offset: %zu\n",
+ __func__, offset, max_offset));
+ return -1;
+ }
+
+ SBVAL(buf, offset, val);
+ return offset + 8;
+}
+
+static ssize_t sl_pull_uint64_val(const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ uint encoding,
+ uint64_t *presult)
+{
+ uint64_t val;
+
+ if (offset + 8 > bufsize) {
+ DEBUG(1,("%s: buffer overflow\n", __func__));
+ return -1;
+ }
+
+ if (encoding == SL_ENC_LITTLE_ENDIAN) {
+ val = BVAL(buf, offset);
+ } else {
+ val = RBVAL(buf, offset);
+ }
+
+ *presult = val;
+
+ return offset + 8;
+}
+
+/*
+ * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
+ * If there is no byte order mark, -1 is returned.
+ */
+static int spotlight_get_utf16_string_encoding(const char *buf, ssize_t offset,
+ size_t query_length, int encoding)
+{
+ int utf16_encoding;
+
+ /* Assumed encoding in absence of a bom is little endian */
+ utf16_encoding = SL_ENC_LITTLE_ENDIAN;
+
+ if (query_length >= 2) {
+ uint8_t le_bom[] = {0xff, 0xfe};
+ uint8_t be_bom[] = {0xfe, 0xff};
+ if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0) {
+ utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
+ } else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0) {
+ utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
+ }
+ }
+
+ return utf16_encoding;
+}
+
+/******************************************************************************
+ * marshalling functions
+ ******************************************************************************/
+
+static inline uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
+{
+ uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count;
+ return tag;
+}
+
+static ssize_t sl_pack_float(double d, char *buf, ssize_t offset, size_t bufsize)
+{
+ union {
+ double d;
+ uint64_t w;
+ } ieee_fp_union;
+
+ ieee_fp_union.d = d;
+
+ offset = sl_push_uint64_val(buf, offset, bufsize, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1));
+ if (offset == -1) {
+ return -1;
+ }
+ offset = sl_push_uint64_val(buf, offset, bufsize, ieee_fp_union.w);
+ if (offset == -1) {
+ return -1;
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_uint64(uint64_t u, char *buf, ssize_t offset, size_t bufsize)
+{
+ uint64_t tag;
+
+ tag = sl_pack_tag(SQ_TYPE_INT64, 2, 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+ offset = sl_push_uint64_val(buf, offset, bufsize, u);
+ if (offset == -1) {
+ return -1;
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_uint64_array(uint64_t *u, char *buf, ssize_t offset, size_t bufsize, int *toc_count)
+{
+ int count, i;
+ uint64_t tag;
+
+ count = talloc_array_length(u);
+
+ tag = sl_pack_tag(SQ_TYPE_INT64, count + 1, count);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ for (i = 0; i < count; i++) {
+ offset = sl_push_uint64_val(buf, offset, bufsize, u[i]);
+ if (offset == -1) {
+ return -1;
+ }
+ }
+
+ if (count > 1) {
+ *toc_count += (count - 1);
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_bool(sl_bool_t val, char *buf, ssize_t offset, size_t bufsize)
+{
+ uint64_t tag;
+
+ tag = sl_pack_tag(SQ_TYPE_BOOL, 1, val ? 1 : 0);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_nil(char *buf, ssize_t offset, size_t bufsize)
+{
+ uint64_t tag;
+
+ tag = sl_pack_tag(SQ_TYPE_NULL, 1, 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_date(sl_time_t t, char *buf, ssize_t offset, size_t bufsize)
+{
+ uint64_t data;
+ uint64_t tag;
+ union {
+ double d;
+ uint64_t w;
+ } ieee_fp_union;
+
+ tag = sl_pack_tag(SQ_TYPE_DATE, 2, 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ ieee_fp_union.d = (double)(t.tv_sec - SPOTLIGHT_TIME_DELTA);
+ ieee_fp_union.d += (double)t.tv_usec / 1000000;
+
+ data = ieee_fp_union.w;
+ offset = sl_push_uint64_val(buf, offset, bufsize, data);
+ if (offset == -1) {
+ return -1;
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_uuid(sl_uuid_t *uuid, char *buf, ssize_t offset, size_t bufsize)
+{
+ uint64_t tag;
+
+ tag = sl_pack_tag(SQ_TYPE_UUID, 3, 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ if (offset + 16 > bufsize) {
+ return -1;
+ }
+ memcpy(buf + offset, uuid, 16);
+
+ return offset + 16;
+}
+
+static ssize_t sl_pack_CNID(sl_cnids_t *cnids, char *buf, ssize_t offset,
+ size_t bufsize, char *toc_buf, int *toc_idx)
+{
+ ssize_t result;
+ int len, i;
+ int cnid_count = dalloc_size(cnids->ca_cnids);
+ uint64_t tag;
+ uint64_t id;
+ void *p;
+
+ tag = sl_pack_tag(SQ_CPX_TYPE_CNIDS, offset / 8, 0);
+ result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ *toc_idx += 1;
+
+ len = cnid_count + 1;
+ if (cnid_count > 0) {
+ len ++;
+ }
+
+ /* unknown meaning, but always 8 */
+ tag = sl_pack_tag(SQ_TYPE_CNIDS, len, 8 );
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ if (cnid_count > 0) {
+ tag = sl_pack_tag(cnids->ca_unkn1, cnid_count, cnids->ca_context);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ for (i = 0; i < cnid_count; i++) {
+ p = dalloc_get_object(cnids->ca_cnids, i);
+ if (p == NULL) {
+ return -1;
+ }
+ memcpy(&id, p, sizeof(uint64_t));
+ offset = sl_push_uint64_val(buf, offset, bufsize, id);
+ if (offset == -1) {
+ return -1;
+ }
+ }
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_array(sl_array_t *array, char *buf, ssize_t offset,
+ size_t bufsize, char *toc_buf, int *toc_idx)
+{
+ ssize_t result;
+ int count = dalloc_size(array);
+ int octets = offset / 8;
+ uint64_t tag;
+ int toc_idx_save = *toc_idx;
+
+ tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ *toc_idx += 1;
+
+ offset = sl_pack_loop(array, buf, offset, bufsize - offset, toc_buf, toc_idx, &count);
+
+ tag = sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count);
+ result = sl_push_uint64_val(toc_buf, toc_idx_save * 8, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack_dict(sl_array_t *dict, char *buf, ssize_t offset,
+ size_t bufsize, char *toc_buf, int *toc_idx, int *count)
+{
+ ssize_t result;
+ uint64_t tag;
+
+ tag = sl_pack_tag(SQ_CPX_TYPE_DICT, offset / 8,
+ dalloc_size(dict));
+ result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ *toc_idx += 1;
+
+ offset = sl_pack_loop(dict, buf, offset, bufsize - offset, toc_buf, toc_idx, count);
+
+ return offset;
+}
+
+static ssize_t sl_pack_filemeta(sl_filemeta_t *fm, char *buf, ssize_t offset,
+ size_t bufsize, char *toc_buf, int *toc_idx)
+{
+ ssize_t result;
+ ssize_t fmlen;
+ ssize_t saveoff = offset;
+ uint64_t tag;
+
+ tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ offset += 8;
+
+ fmlen = sl_pack(fm, buf + offset, bufsize - offset);
+ if (fmlen == -1) {
+ return -1;
+ }
+
+ /*
+ * Check for empty filemeta array, if it's only 40 bytes, it's
+ * only the header but no content
+ */
+ if (fmlen > 40) {
+ offset += fmlen;
+ } else {
+ fmlen = 0;
+ }
+
+ /* unknown meaning, but always 8 */
+ tag = sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8);
+ result = sl_push_uint64_val(buf, saveoff + 8, bufsize, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ tag = sl_pack_tag(SQ_CPX_TYPE_FILEMETA, saveoff / 8, fmlen / 8);
+ result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ *toc_idx += 1;
+
+ return offset;
+}
+
+static ssize_t sl_pack_string(char *s, char *buf, ssize_t offset, size_t bufsize,
+ char *toc_buf, int *toc_idx)
+{
+ ssize_t result;
+ size_t len, octets, used_in_last_octet;
+ uint64_t tag;
+
+ len = strlen(s);
+ if (len > MAX_SL_STRLEN) {
+ return -1;
+ }
+ octets = (len + 7) / 8;
+ used_in_last_octet = len % 8;
+ if (used_in_last_octet == 0) {
+ used_in_last_octet = 8;
+ }
+
+ tag = sl_pack_tag(SQ_CPX_TYPE_STRING, offset / 8, used_in_last_octet);
+ result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ *toc_idx += 1;
+
+ tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ if (offset + (octets * 8) > bufsize) {
+ return -1;
+ }
+
+ memset(buf + offset, 0, octets * 8);
+ memcpy(buf + offset, s, len);
+ offset += octets * 8;
+
+ return offset;
+}
+
+static ssize_t sl_pack_string_as_utf16(char *s, char *buf, ssize_t offset,
+ size_t bufsize, char *toc_buf, int *toc_idx)
+{
+ ssize_t result;
+ int utf16_plus_bom_len, octets, used_in_last_octet;
+ char *utf16string = NULL;
+ char bom[] = { 0xff, 0xfe };
+ size_t slen, utf16len;
+ uint64_t tag;
+ bool ok;
+
+ slen = strlen(s);
+ if (slen > MAX_SL_STRLEN) {
+ return -1;
+ }
+
+ ok = convert_string_talloc(talloc_tos(),
+ CH_UTF8,
+ CH_UTF16LE,
+ s,
+ slen,
+ &utf16string,
+ &utf16len);
+ if (!ok) {
+ return -1;
+ }
+
+ utf16_plus_bom_len = utf16len + 2;
+ octets = (utf16_plus_bom_len + 7) / 8;
+ used_in_last_octet = utf16_plus_bom_len % 8;
+ if (used_in_last_octet == 0) {
+ used_in_last_octet = 8;
+ }
+
+ tag = sl_pack_tag(SQ_CPX_TYPE_UTF16_STRING, offset / 8, used_in_last_octet);
+ result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ offset = -1;
+ goto done;
+ }
+
+ tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ goto done;
+ }
+
+ *toc_idx += 1;
+
+ tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet);
+ offset = sl_push_uint64_val(buf, offset, bufsize, tag);
+ if (offset == -1) {
+ goto done;
+ }
+
+ if (offset + (octets * 8) > bufsize) {
+ offset = -1;
+ goto done;
+ }
+
+ memset(buf + offset, 0, octets * 8);
+ memcpy(buf + offset, &bom, sizeof(bom));
+ memcpy(buf + offset + 2, utf16string, utf16len);
+ offset += octets * 8;
+
+done:
+ TALLOC_FREE(utf16string);
+ return offset;
+}
+
+static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf, ssize_t offset,
+ size_t bufsize, char *toc_buf, int *toc_idx, int *count)
+{
+ const char *type;
+ int n;
+ uint64_t i;
+ sl_bool_t bl;
+ double d;
+ sl_time_t t;
+ void *p;
+
+ for (n = 0; n < dalloc_size(query); n++) {
+
+ type = dalloc_get_name(query, n);
+ if (type == NULL) {
+ return -1;
+ }
+ p = dalloc_get_object(query, n);
+ if (p == NULL) {
+ return -1;
+ }
+
+ if (strcmp(type, "sl_array_t") == 0) {
+ offset = sl_pack_array(p, buf, offset, bufsize,
+ toc_buf, toc_idx);
+ } else if (strcmp(type, "sl_dict_t") == 0) {
+ offset = sl_pack_dict(p, buf, offset, bufsize,
+ toc_buf, toc_idx, count);
+ } else if (strcmp(type, "sl_filemeta_t") == 0) {
+ offset = sl_pack_filemeta(p, buf, offset, bufsize,
+ toc_buf, toc_idx);
+ } else if (strcmp(type, "uint64_t") == 0) {
+ memcpy(&i, p, sizeof(uint64_t));
+ offset = sl_pack_uint64(i, buf, offset, bufsize);
+ } else if (strcmp(type, "uint64_t *") == 0) {
+ offset = sl_pack_uint64_array(p, buf, offset,
+ bufsize, count);
+ } else if (strcmp(type, "char *") == 0) {
+ offset = sl_pack_string(p, buf, offset, bufsize,
+ toc_buf, toc_idx);
+ } else if (strcmp(type, "smb_ucs2_t *") == 0) {
+ offset = sl_pack_string_as_utf16(p, buf, offset, bufsize,
+ toc_buf, toc_idx);
+ } else if (strcmp(type, "sl_bool_t") == 0) {
+ memcpy(&bl, p, sizeof(sl_bool_t));
+ offset = sl_pack_bool(bl, buf, offset, bufsize);
+ } else if (strcmp(type, "double") == 0) {
+ memcpy(&d, p, sizeof(double));
+ offset = sl_pack_float(d, buf, offset, bufsize);
+ } else if (strcmp(type, "sl_nil_t") == 0) {
+ offset = sl_pack_nil(buf, offset, bufsize);
+ } else if (strcmp(type, "sl_time_t") == 0) {
+ memcpy(&t, p, sizeof(sl_time_t));
+ offset = sl_pack_date(t, buf, offset, bufsize);
+ } else if (strcmp(type, "sl_uuid_t") == 0) {
+ offset = sl_pack_uuid(p, buf, offset, bufsize);
+ } else if (strcmp(type, "sl_cnids_t") == 0) {
+ offset = sl_pack_CNID(p, buf, offset,
+ bufsize, toc_buf, toc_idx);
+ } else {
+ DEBUG(1, ("unknown type: %s\n", type));
+ return -1;
+ }
+ if (offset == -1) {
+ DEBUG(1, ("error packing type: %s\n", type));
+ return -1;
+ }
+ }
+
+ return offset;
+}
+
+/******************************************************************************
+ * unmarshalling functions
+ ******************************************************************************/
+
+static ssize_t sl_unpack_tag(const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ uint encoding,
+ struct sl_tag *tag)
+{
+ uint64_t val;
+
+ if (offset + 8 > bufsize) {
+ DEBUG(1,("%s: buffer overflow\n", __func__));
+ return -1;
+ }
+
+ if (encoding == SL_ENC_LITTLE_ENDIAN) {
+ val = BVAL(buf, offset);
+ } else {
+ val = RBVAL(buf, offset);
+ }
+
+ tag->size = (val & 0xffff) * 8;
+ tag->type = (val & 0xffff0000) >> 16;
+ tag->count = val >> 32;
+ tag->length = tag->count * 8;
+
+ if (tag->size > MAX_MDSCMD_SIZE) {
+ DEBUG(1,("%s: size limit %zu\n", __func__, tag->size));
+ return -1;
+ }
+
+ if (tag->length > MAX_MDSCMD_SIZE) {
+ DEBUG(1,("%s: length limit %zu\n", __func__, tag->length));
+ return -1;
+ }
+
+ if (tag->count > MAX_SLQ_COUNT) {
+ DEBUG(1,("%s: count limit %d\n", __func__, tag->count));
+ return -1;
+ }
+
+ return offset + 8;
+}
+
+static int sl_unpack_ints(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int encoding)
+{
+ int i, result;
+ struct sl_tag tag;
+ uint64_t query_data64;
+
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ for (i = 0; i < tag.count; i++) {
+ offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
+ if (offset == -1) {
+ return -1;
+ }
+ result = dalloc_add_copy(query, &query_data64, uint64_t);
+ if (result != 0) {
+ return -1;
+ }
+ }
+
+ return tag.count;
+}
+
+static int sl_unpack_date(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int encoding)
+{
+ int i, result;
+ struct sl_tag tag;
+ uint64_t query_data64;
+ union {
+ double d;
+ uint64_t w;
+ } ieee_fp_union;
+ double fraction;
+ sl_time_t t;
+
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ for (i = 0; i < tag.count; i++) {
+ offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
+ if (offset == -1) {
+ return -1;
+ }
+ ieee_fp_union.w = query_data64;
+ fraction = ieee_fp_union.d - (uint64_t)ieee_fp_union.d;
+
+ t = (sl_time_t) {
+ .tv_sec = ieee_fp_union.d + SPOTLIGHT_TIME_DELTA,
+ .tv_usec = fraction * 1000000
+ };
+
+ result = dalloc_add_copy(query, &t, sl_time_t);
+ if (result != 0) {
+ return -1;
+ }
+ }
+
+ return tag.count;
+}
+
+static int sl_unpack_uuid(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int encoding)
+{
+ int i, result;
+ sl_uuid_t uuid;
+ struct sl_tag tag;
+
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ for (i = 0; i < tag.count; i++) {
+ if (offset + 16 > bufsize) {
+ DEBUG(1,("%s: buffer overflow\n", __func__));
+ return -1;
+ }
+ memcpy(uuid.sl_uuid, buf + offset, 16);
+ result = dalloc_add_copy(query, &uuid, sl_uuid_t);
+ if (result != 0) {
+ return -1;
+ }
+ offset += 16;
+ }
+
+ return tag.count;
+}
+
+static int sl_unpack_floats(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int encoding)
+{
+ int i, result;
+ union {
+ double d;
+ uint32_t w[2];
+ } ieee_fp_union;
+ struct sl_tag tag;
+
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ for (i = 0; i < tag.count; i++) {
+ if (offset + 8 > bufsize) {
+ DEBUG(1,("%s: buffer overflow\n", __func__));
+ return -1;
+ }
+ if (encoding == SL_ENC_LITTLE_ENDIAN) {
+#ifdef WORDS_BIGENDIAN
+ ieee_fp_union.w[0] = IVAL(buf, offset + 4);
+ ieee_fp_union.w[1] = IVAL(buf, offset);
+#else
+ ieee_fp_union.w[0] = IVAL(buf, offset);
+ ieee_fp_union.w[1] = IVAL(buf, offset + 4);
+#endif
+ } else {
+#ifdef WORDS_BIGENDIAN
+ ieee_fp_union.w[0] = RIVAL(buf, offset);
+ ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
+#else
+ ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
+ ieee_fp_union.w[1] = RIVAL(buf, offset);
+#endif
+ }
+ result = dalloc_add_copy(query, &ieee_fp_union.d, double);
+ if (result != 0) {
+ return -1;
+ }
+ offset += 8;
+ }
+
+ return tag.count;
+}
+
+static int sl_unpack_CNID(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int length,
+ int encoding)
+{
+ int i, count, result;
+ uint64_t query_data64;
+ sl_cnids_t *cnids;
+
+ cnids = talloc_zero(query, sl_cnids_t);
+ if (cnids == NULL) {
+ return -1;
+ }
+ cnids->ca_cnids = dalloc_new(cnids);
+ if (cnids->ca_cnids == NULL) {
+ return -1;
+ }
+
+ if (length < 8) {
+ return -1;
+ }
+ if (length == 8) {
+ /*
+ * That's permitted, length=8 is an empty CNID array.
+ */
+ result = dalloc_add(query, cnids, sl_cnids_t);
+ if (result != 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
+ if (offset == -1) {
+ return -1;
+ }
+
+ /*
+ * Note: ca_unkn1 and ca_context could be taken from the tag
+ * type and count members, but the fields are packed
+ * differently in this context, so we can't use
+ * sl_unpack_tag().
+ */
+ count = query_data64 & 0xffff;;
+ cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
+ cnids->ca_context = query_data64 >> 32;
+
+ for (i = 0; i < count; i++) {
+ offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
+ if (offset == -1) {
+ return -1;
+ }
+
+ result = dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
+ if (result != 0) {
+ return -1;
+ }
+ }
+
+ result = dalloc_add(query, cnids, sl_cnids_t);
+ if (result != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ssize_t sl_unpack_cpx(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int cpx_query_type,
+ int cpx_query_count,
+ ssize_t toc_offset,
+ int encoding)
+{
+ int result;
+ ssize_t roffset = offset;
+ int unicode_encoding;
+ bool mark_exists;
+ char *p;
+ size_t slen, tmp_len;
+ sl_array_t *sl_array;
+ sl_dict_t *sl_dict;
+ sl_filemeta_t *sl_fm;
+ bool ok;
+ struct sl_tag tag;
+
+ switch (cpx_query_type) {
+ case SQ_CPX_TYPE_ARRAY:
+ sl_array = dalloc_zero(query, sl_array_t);
+ if (sl_array == NULL) {
+ return -1;
+ }
+ roffset = sl_unpack_loop(sl_array, buf, offset, bufsize,
+ cpx_query_count, toc_offset, encoding);
+ if (roffset == -1) {
+ return -1;
+ }
+ result = dalloc_add(query, sl_array, sl_array_t);
+ if (result != 0) {
+ return -1;
+ }
+ break;
+
+ case SQ_CPX_TYPE_DICT:
+ sl_dict = dalloc_zero(query, sl_dict_t);
+ if (sl_dict == NULL) {
+ return -1;
+ }
+ roffset = sl_unpack_loop(sl_dict, buf, offset, bufsize,
+ cpx_query_count, toc_offset, encoding);
+ if (roffset == -1) {
+ return -1;
+ }
+ result = dalloc_add(query, sl_dict, sl_dict_t);
+ if (result != 0) {
+ return -1;
+ }
+ break;
+
+ case SQ_CPX_TYPE_STRING:
+ case SQ_CPX_TYPE_UTF16_STRING:
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ if (tag.size < 16) {
+ DEBUG(1,("%s: string buffer too small\n", __func__));
+ return -1;
+ }
+ slen = tag.size - 16 + tag.count;
+ if (slen > MAX_MDSCMD_SIZE) {
+ return -1;
+ }
+
+ if (offset + slen > bufsize) {
+ DEBUG(1,("%s: buffer overflow\n", __func__));
+ return -1;
+ }
+
+ if (cpx_query_type == SQ_CPX_TYPE_STRING) {
+ p = talloc_strndup(query, buf + offset, slen);
+ if (p == NULL) {
+ return -1;
+ }
+ } else {
+ unicode_encoding = spotlight_get_utf16_string_encoding(
+ buf, offset, slen, encoding);
+ mark_exists = (unicode_encoding & SL_ENC_UTF_16) ? true : false;
+ if (unicode_encoding & SL_ENC_BIG_ENDIAN) {
+ DEBUG(1, ("Unsupported big endian UTF16 string\n"));
+ return -1;
+ }
+ slen -= mark_exists ? 2 : 0;
+ ok = convert_string_talloc(
+ query,
+ CH_UTF16LE,
+ CH_UTF8,
+ buf + offset + (mark_exists ? 2 : 0),
+ slen,
+ &p,
+ &tmp_len);
+ if (!ok) {
+ return -1;
+ }
+ }
+
+ result = dalloc_stradd(query, p);
+ if (result != 0) {
+ return -1;
+ }
+ roffset += tag.size;
+ break;
+
+ case SQ_CPX_TYPE_FILEMETA:
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+ if (tag.size < 8) {
+ DBG_WARNING("size too mall: %zu\n", tag.size);
+ return -1;
+ }
+
+ sl_fm = dalloc_zero(query, sl_filemeta_t);
+ if (sl_fm == NULL) {
+ return -1;
+ }
+
+ if (tag.size >= 16) {
+ result = sl_unpack(sl_fm,
+ buf + offset,
+ bufsize - offset );
+ if (result == -1) {
+ return -1;
+ }
+ }
+ result = dalloc_add(query, sl_fm, sl_filemeta_t);
+ if (result != 0) {
+ return -1;
+ }
+ roffset += tag.size;
+ break;
+
+ case SQ_CPX_TYPE_CNIDS:
+ offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (offset == -1) {
+ return -1;
+ }
+
+ result = sl_unpack_CNID(query, buf, offset, bufsize,
+ tag.size, encoding);
+ if (result == -1) {
+ return -1;
+ }
+ roffset += tag.size;
+ break;
+
+ default:
+ DEBUG(1, ("unknown complex query type: %u\n", cpx_query_type));
+ return -1;
+ }
+
+ return roffset;
+}
+
+static ssize_t sl_unpack_loop(DALLOC_CTX *query,
+ const char *buf,
+ ssize_t offset,
+ size_t bufsize,
+ int count,
+ ssize_t toc_offset,
+ int encoding)
+{
+ int i, toc_index, subcount;
+ uint64_t result;
+
+ while (count > 0) {
+ struct sl_tag tag;
+
+ if (offset >= toc_offset) {
+ return -1;
+ }
+
+ result = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ switch (tag.type) {
+ case SQ_TYPE_COMPLEX: {
+ struct sl_tag cpx_tag;
+
+ if (tag.count < 1) {
+ DEBUG(1,("%s: invalid tag.count: %d\n",
+ __func__, tag.count));
+ return -1;
+ }
+ toc_index = tag.count - 1;
+ if (toc_index > MAX_SLQ_TOCIDX) {
+ DEBUG(1,("%s: toc_index too large: %d\n",
+ __func__, toc_index));
+ return -1;
+ }
+ result = sl_unpack_tag(buf, toc_offset + (toc_index * 8),
+ bufsize, encoding, &cpx_tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ offset = sl_unpack_cpx(query, buf, offset + 8, bufsize, cpx_tag.type,
+ cpx_tag.count, toc_offset, encoding);
+ if (offset == -1) {
+ return -1;
+ }
+ /*
+ * tag.size is not the size here, so we need
+ * to use the offset returned from sl_unpack_cpx()
+ * instead of offset += tag.size;
+ */
+ count--;
+ break;
+ }
+
+ case SQ_TYPE_NULL: {
+ sl_nil_t nil = 0;
+
+ subcount = tag.count;
+ if (subcount < 1 || subcount > count) {
+ return -1;
+ }
+ for (i = 0; i < subcount; i++) {
+ result = dalloc_add_copy(query, &nil, sl_nil_t);
+ if (result != 0) {
+ return -1;
+ }
+ }
+ offset += tag.size;
+ count -= subcount;
+ break;
+ }
+
+ case SQ_TYPE_BOOL: {
+ sl_bool_t b = (tag.count != 0);
+
+ result = dalloc_add_copy(query, &b, sl_bool_t);
+ if (result != 0) {
+ return -1;
+ }
+ offset += tag.size;
+ count--;
+ break;
+ }
+
+ case SQ_TYPE_INT64:
+ subcount = sl_unpack_ints(query, buf, offset, bufsize, encoding);
+ if (subcount < 1 || subcount > count) {
+ return -1;
+ }
+ offset += tag.size;
+ count -= subcount;
+ break;
+
+ case SQ_TYPE_UUID:
+ subcount = sl_unpack_uuid(query, buf, offset, bufsize, encoding);
+ if (subcount < 1 || subcount > count) {
+ return -1;
+ }
+ offset += tag.size;
+ count -= subcount;
+ break;
+
+ case SQ_TYPE_FLOAT:
+ subcount = sl_unpack_floats(query, buf, offset, bufsize, encoding);
+ if (subcount < 1 || subcount > count) {
+ return -1;
+ }
+ offset += tag.size;
+ count -= subcount;
+ break;
+
+ case SQ_TYPE_DATE:
+ subcount = sl_unpack_date(query, buf, offset, bufsize, encoding);
+ if (subcount < 1 || subcount > count) {
+ return -1;
+ }
+ offset += tag.size;
+ count -= subcount;
+ break;
+
+ default:
+ DEBUG(1, ("unknown query type: %d\n", tag.type));
+ return -1;
+ }
+ }
+
+ return offset;
+}
+
+static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize)
+{
+ ssize_t result;
+ char *toc_buf;
+ int toc_index = 0;
+ int toc_count = 0;
+ ssize_t offset, len;
+ uint64_t hdr;
+ uint32_t total_octets;
+ uint32_t data_octets;
+ uint64_t tag;
+
+ memset(buf, 0, bufsize);
+
+ toc_buf = talloc_zero_size(query, MAX_SLQ_TOC + 8);
+ if (toc_buf == NULL) {
+ return -1;
+ }
+
+ offset = sl_pack_loop(query, buf, 16, bufsize, toc_buf + 8, &toc_index, &toc_count);
+ if (offset == -1 || offset < 16) {
+ DEBUG(10,("%s: sl_pack_loop error\n", __func__));
+ return -1;
+ }
+ len = offset - 16;
+
+ /*
+ * Marshalling overview:
+ *
+ * 16 bytes at the start of buf:
+ *
+ * 8 bytes byte order mark
+ * 4 bytes total octets
+ * 4 bytes table of content octets
+ *
+ * x bytes total octets * 8 from sl_pack_loop
+ * x bytes ToC octets * 8 from toc_buf
+ */
+
+ /* Byte-order mark - we are using little endian only for now */
+ memcpy(buf, "432130dm", strlen("432130dm"));
+
+ /*
+ * The data buffer and ToC buffer sizes are enocoded in number
+ * of octets (size / 8), plus one, because the octet encoding
+ * the sizes is included.
+ */
+ data_octets = (len / 8) + 1;
+ total_octets = data_octets + toc_index + 1;
+
+ hdr = total_octets;
+ hdr |= ((uint64_t)data_octets << 32);
+
+ /* HDR */
+ result = sl_push_uint64_val(buf, 8, bufsize, hdr);
+ if (result == -1) {
+ return -1;
+ }
+
+ /*
+ * ToC tag with number of ToC entries plus one, the ToC tag
+ * header.
+ */
+ tag = sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0);
+ result = sl_push_uint64_val(toc_buf, 0, MAX_SLQ_TOC, tag);
+ if (result == -1) {
+ return -1;
+ }
+
+ if ((16 + len + ((toc_index + 1 ) * 8)) > bufsize) {
+ DEBUG(1, ("%s: exceeding size limit %zu\n", __func__, bufsize));
+ return -1;
+ }
+
+ memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
+ len += 16 + (toc_index + 1 ) * 8;
+
+ return len;
+}
+
+/******************************************************************************
+ * Global functions for packing und unpacking
+ ******************************************************************************/
+
+NTSTATUS sl_pack_alloc(TALLOC_CTX *mem_ctx,
+ DALLOC_CTX *d,
+ struct mdssvc_blob *b,
+ size_t max_fragment_size)
+{
+ ssize_t len;
+
+ b->spotlight_blob = talloc_zero_array(mem_ctx,
+ uint8_t,
+ max_fragment_size);
+ if (b->spotlight_blob == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = sl_pack(d, (char *)b->spotlight_blob, max_fragment_size);
+ if (len == -1) {
+ return NT_STATUS_DATA_ERROR;
+ }
+
+ b->length = len;
+ b->size = len;
+ return NT_STATUS_OK;
+}
+
+bool sl_unpack(DALLOC_CTX *query, const char *buf, size_t bufsize)
+{
+ ssize_t result;
+ ssize_t offset = 0;
+ int encoding;
+ uint64_t hdr;
+ uint32_t total_octets;
+ uint64_t total_bytes;
+ uint32_t data_octets;
+ uint64_t data_bytes;
+ uint64_t toc_offset;
+ struct sl_tag toc_tag;
+
+ if (bufsize > MAX_MDSCMD_SIZE) {
+ return false;
+ }
+
+ if (bufsize < 8) {
+ return false;
+ }
+ if (strncmp(buf + offset, "md031234", 8) == 0) {
+ encoding = SL_ENC_BIG_ENDIAN;
+ } else {
+ encoding = SL_ENC_LITTLE_ENDIAN;
+ }
+ offset += 8;
+
+ offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &hdr);
+ if (offset == -1) {
+ return false;
+ }
+
+ total_octets = hdr & UINT32_MAX;
+ data_octets = hdr >> 32;
+
+ /*
+ * Both fields contain the number of octets of the
+ * corresponding buffer plus the tag octet. We adjust the
+ * values to match just the number of octets in the buffers.
+ */
+ if (total_octets < 1) {
+ return false;
+ }
+ if (data_octets < 1) {
+ return false;
+ }
+ total_octets--;
+ data_octets--;
+ data_bytes = ((uint64_t)data_octets) * 8;
+ total_bytes = ((uint64_t)total_octets) * 8;
+
+ if (data_bytes >= total_bytes) {
+ DEBUG(1,("%s: data_bytes: %" PRIu64 ", total_bytes: %" PRIu64 "\n",
+ __func__, data_bytes, total_bytes));
+ return false;
+ }
+
+ if (total_bytes > (bufsize - offset)) {
+ return false;
+ }
+
+ toc_offset = data_bytes;
+
+ toc_offset = sl_unpack_tag(buf + offset, toc_offset,
+ bufsize - offset, encoding, &toc_tag);
+ if (toc_offset == -1) {
+ return false;
+ }
+
+ if (toc_tag.type != SQ_TYPE_TOC) {
+ DEBUG(1,("%s: unknown tag type %d\n", __func__, toc_tag.type));
+ return false;
+ }
+
+ /*
+ * Check toc_tag.size even though we don't use it when unmarshalling
+ */
+ if (toc_tag.size > MAX_SLQ_TOC) {
+ DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size));
+ return false;
+ }
+ if (toc_tag.size > (total_bytes - data_bytes)) {
+ DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size));
+ return false;
+ }
+
+ if (toc_tag.count != 0) {
+ DEBUG(1,("%s: bad count %u\n", __func__, toc_tag.count));
+ return false;
+ }
+
+ /*
+ * We already consumed 16 bytes from the buffer (BOM and size
+ * tag), so we start at buf + offset.
+ */
+ result = sl_unpack_loop(query, buf + offset, 0, bufsize - offset,
+ 1, toc_offset, encoding);
+ if (result == -1) {
+ DEBUG(1,("%s: sl_unpack_loop failed\n", __func__));
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/rpc_server/mdssvc/marshalling.h b/source3/rpc_server/mdssvc/marshalling.h
new file mode 100644
index 0000000..ccc8b44
--- /dev/null
+++ b/source3/rpc_server/mdssvc/marshalling.h
@@ -0,0 +1,63 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2015
+
+ 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 _MDSSVC_MARSHALLING_H
+#define _MDSSVC_MARSHALLING_H
+
+#include "dalloc.h"
+#include "libcli/util/ntstatus.h"
+#include "lib/util/data_blob.h"
+#include "librpc/gen_ndr/mdssvc.h"
+
+#define MAX_SL_FRAGMENT_SIZE 0xFFFFF
+#define MAX_MDSCMD_SIZE 0xFFFFFF
+
+/* Can be ored and used as flags */
+#define SL_ENC_LITTLE_ENDIAN 1
+#define SL_ENC_BIG_ENDIAN 2
+#define SL_ENC_UTF_16 4
+
+typedef DALLOC_CTX sl_array_t; /* an array of elements */
+typedef DALLOC_CTX sl_dict_t; /* an array of key/value elements */
+typedef DALLOC_CTX sl_filemeta_t; /* contains one sl_array_t */
+typedef int sl_nil_t; /* a nil element */
+typedef bool sl_bool_t;
+typedef struct timeval sl_time_t;
+typedef struct {
+ char sl_uuid[16];
+} sl_uuid_t;
+typedef struct {
+ uint16_t ca_unkn1;
+ uint32_t ca_context;
+ DALLOC_CTX *ca_cnids;
+} sl_cnids_t; /* an array of CNIDs */
+
+/******************************************************************************
+ * Function declarations
+ ******************************************************************************/
+
+extern NTSTATUS sl_pack_alloc(TALLOC_CTX *mem_ctx,
+ DALLOC_CTX *d,
+ struct mdssvc_blob *b,
+ size_t max_fragment_size);
+
+extern bool sl_unpack(DALLOC_CTX *query, const char *buf, size_t bufsize);
+
+#endif
diff --git a/source3/rpc_server/mdssvc/mdssvc.c b/source3/rpc_server/mdssvc/mdssvc.c
new file mode 100644
index 0000000..23a3088
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc.c
@@ -0,0 +1,1893 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2012-2014
+
+ 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 "smbd/proto.h"
+#include "librpc/gen_ndr/auth.h"
+#include "dbwrap/dbwrap.h"
+#include "lib/util/dlinklist.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/time_basic.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/security.h"
+#include "mdssvc.h"
+#include "mdssvc_noindex.h"
+#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
+#include "mdssvc_tracker.h"
+#endif
+#ifdef HAVE_SPOTLIGHT_BACKEND_ES
+#include "mdssvc_es.h"
+#endif
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+struct slrpc_cmd {
+ const char *name;
+ bool (*function)(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query,
+ DALLOC_CTX *reply);
+};
+
+struct slq_destroy_state {
+ struct tevent_context *ev;
+ struct sl_query *slq;
+};
+
+/*
+ * This is a static global because we may be called multiple times and
+ * we only want one mdssvc_ctx per connection to Tracker.
+ *
+ * The client will bind multiple times to the mdssvc RPC service, once
+ * for every tree connect.
+ */
+static struct mdssvc_ctx *mdssvc_ctx = NULL;
+
+/*
+ * If these functions return an error, they hit something like a non
+ * recoverable talloc error. Most errors are dealt with by returning
+ * an error code in the Spotlight RPC reply.
+ */
+static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+static bool slrpc_open_query(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+static bool slrpc_close_query(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply);
+
+/************************************************
+ * Misc utility functions
+ ************************************************/
+
+/**
+ * Add requested metadata for a query result element
+ *
+ * This could be rewritten to something more sophisticated like
+ * querying metadata from Tracker.
+ *
+ * If path or sp is NULL, simply add nil values for all attributes.
+ **/
+static bool add_filemeta(struct mds_ctx *mds_ctx,
+ sl_array_t *reqinfo,
+ sl_array_t *fm_array,
+ const char *path,
+ const struct stat_ex *sp)
+{
+ sl_array_t *meta;
+ sl_nil_t nil;
+ int i, metacount, result;
+ uint64_t uint64var;
+ sl_time_t sl_time;
+ char *p;
+ const char *attribute;
+ size_t nfc_len;
+ const char *nfc_path = path;
+ size_t nfd_buf_size;
+ char *nfd_path = NULL;
+ char *dest = NULL;
+ size_t dest_remaining;
+ size_t nconv;
+
+ metacount = dalloc_size(reqinfo);
+ if (metacount == 0 || path == NULL || sp == NULL) {
+ result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
+ if (result != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ meta = dalloc_zero(fm_array, sl_array_t);
+ if (meta == NULL) {
+ return false;
+ }
+
+ nfc_len = strlen(nfc_path);
+ /*
+ * Simple heuristic, strlen by two should give enough room for NFC to
+ * NFD conversion.
+ */
+ nfd_buf_size = nfc_len * 2;
+ nfd_path = talloc_array(meta, char, nfd_buf_size);
+ if (nfd_path == NULL) {
+ return false;
+ }
+ dest = nfd_path;
+ dest_remaining = talloc_array_length(dest);
+
+ nconv = smb_iconv(mds_ctx->ic_nfc_to_nfd,
+ &nfc_path,
+ &nfc_len,
+ &dest,
+ &dest_remaining);
+ if (nconv == (size_t)-1) {
+ return false;
+ }
+
+ for (i = 0; i < metacount; i++) {
+ attribute = dalloc_get_object(reqinfo, i);
+ if (attribute == NULL) {
+ return false;
+ }
+ if (strcmp(attribute, "kMDItemDisplayName") == 0
+ || strcmp(attribute, "kMDItemFSName") == 0) {
+ p = strrchr(nfd_path, '/');
+ if (p) {
+ result = dalloc_stradd(meta, p + 1);
+ if (result != 0) {
+ return false;
+ }
+ }
+ } else if (strcmp(attribute, "kMDItemPath") == 0) {
+ result = dalloc_stradd(meta, nfd_path);
+ if (result != 0) {
+ return false;
+ }
+ } else if (strcmp(attribute, "kMDItemFSSize") == 0) {
+ uint64var = sp->st_ex_size;
+ result = dalloc_add_copy(meta, &uint64var, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ } else if (strcmp(attribute, "kMDItemFSOwnerUserID") == 0) {
+ uint64var = sp->st_ex_uid;
+ result = dalloc_add_copy(meta, &uint64var, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ } else if (strcmp(attribute, "kMDItemFSOwnerGroupID") == 0) {
+ uint64var = sp->st_ex_gid;
+ result = dalloc_add_copy(meta, &uint64var, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ } else if (strcmp(attribute, "kMDItemFSContentChangeDate") == 0 ||
+ strcmp(attribute, "kMDItemContentModificationDate") == 0)
+ {
+ sl_time = convert_timespec_to_timeval(sp->st_ex_mtime);
+ result = dalloc_add_copy(meta, &sl_time, sl_time_t);
+ if (result != 0) {
+ return false;
+ }
+ } else {
+ result = dalloc_add_copy(meta, &nil, sl_nil_t);
+ if (result != 0) {
+ return false;
+ }
+ }
+ }
+
+ result = dalloc_add(fm_array, meta, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+ return true;
+}
+
+static int cnid_comp_fn(const void *p1, const void *p2)
+{
+ const uint64_t *cnid1 = p1, *cnid2 = p2;
+ if (*cnid1 == *cnid2) {
+ return 0;
+ }
+ if (*cnid1 < *cnid2) {
+ return -1;
+ }
+ return 1;
+}
+
+/**
+ * Create a sorted copy of a CNID array
+ **/
+static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
+{
+ uint64_t *cnids = NULL;
+ int i;
+ const void *p;
+
+ cnids = talloc_array(slq, uint64_t, dalloc_size(d));
+ if (cnids == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < dalloc_size(d); i++) {
+ p = dalloc_get_object(d, i);
+ if (p == NULL) {
+ return NULL;
+ }
+ memcpy(&cnids[i], p, sizeof(uint64_t));
+ }
+ qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
+
+ slq->cnids = cnids;
+ slq->cnids_num = dalloc_size(d);
+
+ return true;
+}
+
+/**
+ * Allocate result handle used in the async Tracker cursor result
+ * handler for storing results
+ **/
+static bool create_result_handle(struct sl_query *slq)
+{
+ sl_nil_t nil = 0;
+ struct sl_rslts *query_results;
+ int result;
+
+ if (slq->query_results) {
+ DEBUG(1, ("unexpected existing result handle\n"));
+ return false;
+ }
+
+ query_results = talloc_zero(slq, struct sl_rslts);
+ if (query_results == NULL) {
+ return false;
+ }
+
+ /* CNIDs */
+ query_results->cnids = talloc_zero(query_results, sl_cnids_t);
+ if (query_results->cnids == NULL) {
+ return false;
+ }
+ query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
+ if (query_results->cnids->ca_cnids == NULL) {
+ return false;
+ }
+
+ query_results->cnids->ca_unkn1 = 0xadd;
+ if (slq->ctx2 > UINT32_MAX) {
+ DEBUG(1,("64bit ctx2 id too large: 0x%jx\n", (uintmax_t)slq->ctx2));
+ return false;
+ }
+ query_results->cnids->ca_context = (uint32_t)slq->ctx2;
+
+ /* FileMeta */
+ query_results->fm_array = dalloc_zero(query_results, sl_array_t);
+ if (query_results->fm_array == NULL) {
+ return false;
+ }
+
+ /* For some reason the list of results always starts with a nil entry */
+ result = dalloc_add_copy(query_results->fm_array, &nil, sl_nil_t);
+ if (result != 0) {
+ return false;
+ }
+
+ slq->query_results = query_results;
+ return true;
+}
+
+static bool add_results(sl_array_t *array, struct sl_query *slq)
+{
+ sl_filemeta_t *fm;
+ uint64_t status;
+ int result;
+ bool ok;
+
+ /*
+ * Taken from network traces against a macOS SMB Spotlight server: if
+ * the search is not finished yet in the backend macOS returns 0x23,
+ * otherwise 0x0.
+ */
+ if (slq->state >= SLQ_STATE_DONE) {
+ status = 0;
+ } else {
+ status = 0x23;
+ }
+
+ /* FileMeta */
+ fm = dalloc_zero(array, sl_filemeta_t);
+ if (fm == NULL) {
+ return false;
+ }
+
+ result = dalloc_add_copy(array, &status, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
+ if (result != 0) {
+ return false;
+ }
+ if (slq->query_results->num_results > 0) {
+ result = dalloc_add(fm, slq->query_results->fm_array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+ }
+ result = dalloc_add(array, fm, sl_filemeta_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* This ensure the results get clean up after been sent to the client */
+ talloc_move(array, &slq->query_results);
+
+ ok = create_result_handle(slq);
+ if (!ok) {
+ DEBUG(1, ("couldn't add result handle\n"));
+ slq->state = SLQ_STATE_ERROR;
+ return false;
+ }
+
+ return true;
+}
+
+static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
+{
+ size_t i;
+ static const struct slrpc_cmd cmds[] = {
+ { "fetchPropertiesForContext:", slrpc_fetch_properties},
+ { "openQueryWithParams:forContext:", slrpc_open_query},
+ { "fetchQueryResultsForContext:", slrpc_fetch_query_results},
+ { "storeAttributes:forOIDArray:context:", slrpc_store_attributes},
+ { "fetchAttributeNamesForOIDArray:context:", slrpc_fetch_attributenames},
+ { "fetchAttributes:forOIDArray:context:", slrpc_fetch_attributes},
+ { "fetchAllAttributes:forOIDArray:context:", slrpc_fetch_attributes},
+ { "closeQueryForContext:", slrpc_close_query},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(cmds); i++) {
+ int cmp;
+
+ cmp = strcmp(cmds[i].name, rpccmd);
+ if (cmp == 0) {
+ return &cmds[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Search the list of active queries given their context ids
+ **/
+static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
+ uint64_t ctx1, uint64_t ctx2)
+{
+ struct sl_query *q;
+
+ for (q = mds_ctx->query_list; q; q = q->next) {
+ if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+static int slq_destructor_cb(struct sl_query *slq)
+{
+ SLQ_DEBUG(10, slq, "destroying");
+
+ /* Free all entries before freeing the slq handle! */
+ TALLOC_FREE(slq->entries_ctx);
+ TALLOC_FREE(slq->te);
+
+ if (slq->mds_ctx != NULL) {
+ DLIST_REMOVE(slq->mds_ctx->query_list, slq);
+ slq->mds_ctx = NULL;
+ }
+
+ TALLOC_FREE(slq->backend_private);
+
+ return 0;
+}
+
+/**
+ * Remove talloc_refcounted entry from mapping db
+ *
+ * Multiple queries (via the slq handle) may reference a
+ * sl_inode_path_map entry, when the last reference goes away as the
+ * queries are closed and this gets called to remove the entry from
+ * the db.
+ **/
+static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
+{
+ NTSTATUS status;
+ TDB_DATA key;
+
+ key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
+
+ status = dbwrap_delete(entry->mds_ctx->ino_path_map, key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to delete record: %s\n", nt_errstr(status)));
+ return -1;
+ }
+
+ DBG_DEBUG("deleted [0x%"PRIx64"] [%s]\n", entry->ino, entry->path);
+ return 0;
+}
+
+/**
+ * Add result to inode->path mapping dbwrap rbt db
+ *
+ * This is necessary as a CNID db substitute, ie we need a way to
+ * simulate unique, constant numerical identifiers for paths with an
+ * API that supports mapping from id to path.
+ *
+ * Entries are talloc'ed of the query, using talloc_reference() if
+ * multiple queries returned the same result. That way we can cleanup
+ * entries by calling talloc_free() on the query slq handles.
+ **/
+
+static bool inode_map_add(struct sl_query *slq,
+ uint64_t ino,
+ const char *path,
+ struct stat_ex *st)
+{
+ NTSTATUS status;
+ struct sl_inode_path_map *entry;
+ TDB_DATA key, value;
+ void *p;
+
+ key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
+ status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * We have one db, so when different parallel queries
+ * return the same file, we have to refcount entries
+ * in the db.
+ */
+
+ if (value.dsize != sizeof(void *)) {
+ DEBUG(1, ("invalid dsize\n"));
+ return false;
+ }
+ memcpy(&p, value.dptr, sizeof(p));
+ entry = talloc_get_type_abort(p, struct sl_inode_path_map);
+
+ DEBUG(10, ("map: %s\n", entry->path));
+
+ entry = talloc_reference(slq->entries_ctx, entry);
+ if (entry == NULL) {
+ DEBUG(1, ("talloc_reference failed\n"));
+ return false;
+ }
+ return true;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ entry = talloc_zero(slq->entries_ctx, struct sl_inode_path_map);
+ if (entry == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return false;
+ }
+
+ entry->ino = ino;
+ entry->mds_ctx = slq->mds_ctx;
+ entry->st = *st;
+ entry->path = talloc_strdup(entry, path);
+ if (entry->path == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ TALLOC_FREE(entry);
+ return false;
+ }
+
+ status = dbwrap_store(slq->mds_ctx->ino_path_map, key,
+ make_tdb_data((void *)&entry, sizeof(void *)), 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to store record: %s\n", nt_errstr(status)));
+ TALLOC_FREE(entry);
+ return false;
+ }
+
+ talloc_set_destructor(entry, ino_path_map_destr_cb);
+
+ return true;
+}
+
+bool mds_add_result(struct sl_query *slq, const char *path)
+{
+ struct smb_filename *smb_fname = NULL;
+ const char *relative = NULL;
+ char *fake_path = NULL;
+ struct stat_ex sb;
+ uint64_t ino64;
+ int result;
+ NTSTATUS status;
+ bool sub;
+ bool ok;
+
+ /*
+ * We're in a tevent callback which means in the case of
+ * running as external RPC service we're running as root and
+ * not as the user.
+ */
+ if (!become_authenticated_pipe_user(slq->mds_ctx->pipe_session_info)) {
+ DBG_ERR("can't become authenticated user: %d\n",
+ slq->mds_ctx->uid);
+ smb_panic("can't become authenticated user");
+ }
+
+ if (geteuid() != slq->mds_ctx->uid) {
+ DBG_ERR("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid);
+ smb_panic("uid mismatch");
+ }
+
+ /*
+ * We've changed identity to the authenticated pipe user, so
+ * any function exit below must ensure we switch back
+ */
+
+ status = synthetic_pathref(talloc_tos(),
+ slq->mds_ctx->conn->cwd_fsp,
+ path,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("synthetic_pathref [%s]: %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status));
+ unbecome_authenticated_pipe_user();
+ return true;
+ }
+
+ sb = smb_fname->st;
+
+ status = smbd_check_access_rights_fsp(slq->mds_ctx->conn->cwd_fsp,
+ smb_fname->fsp,
+ false,
+ FILE_READ_DATA);
+ unbecome_authenticated_pipe_user();
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_fname);
+ return true;
+ }
+
+ /* Done with smb_fname now. */
+ TALLOC_FREE(smb_fname);
+
+ ino64 = SMB_VFS_FS_FILE_ID(slq->mds_ctx->conn, &sb);
+
+ if (slq->cnids) {
+ bool found;
+
+ /*
+ * Check whether the found element is in the requested
+ * set of IDs. Note that we're faking CNIDs by using
+ * filesystem inode numbers here
+ */
+ found = bsearch(&ino64,
+ slq->cnids,
+ slq->cnids_num,
+ sizeof(uint64_t),
+ cnid_comp_fn);
+ if (!found) {
+ return true;
+ }
+ }
+
+ sub = subdir_of(slq->mds_ctx->spath,
+ slq->mds_ctx->spath_len,
+ path,
+ &relative);
+ if (!sub) {
+ DBG_ERR("[%s] is not inside [%s]\n",
+ path, slq->mds_ctx->spath);
+ slq->state = SLQ_STATE_ERROR;
+ return false;
+ }
+
+ /*
+ * Add inode number and filemeta to result set, this is what
+ * we return as part of the result set of a query
+ */
+ result = dalloc_add_copy(slq->query_results->cnids->ca_cnids,
+ &ino64,
+ uint64_t);
+ if (result != 0) {
+ DBG_ERR("dalloc error\n");
+ slq->state = SLQ_STATE_ERROR;
+ return false;
+ }
+
+ fake_path = talloc_asprintf(slq,
+ "/%s/%s",
+ slq->mds_ctx->sharename,
+ relative);
+ if (fake_path == NULL) {
+ slq->state = SLQ_STATE_ERROR;
+ return false;
+ }
+
+ ok = add_filemeta(slq->mds_ctx,
+ slq->reqinfo,
+ slq->query_results->fm_array,
+ fake_path,
+ &sb);
+ if (!ok) {
+ DBG_ERR("add_filemeta error\n");
+ TALLOC_FREE(fake_path);
+ slq->state = SLQ_STATE_ERROR;
+ return false;
+ }
+
+ ok = inode_map_add(slq, ino64, fake_path, &sb);
+ TALLOC_FREE(fake_path);
+ if (!ok) {
+ DEBUG(1, ("inode_map_add error\n"));
+ slq->state = SLQ_STATE_ERROR;
+ return false;
+ }
+
+ slq->query_results->num_results++;
+ return true;
+}
+
+/***********************************************************
+ * Spotlight RPC functions
+ ***********************************************************/
+
+static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply)
+{
+ sl_dict_t *dict;
+ sl_array_t *array;
+ char *s;
+ uint64_t u;
+ sl_bool_t b;
+ sl_uuid_t uuid;
+ int result;
+
+ dict = dalloc_zero(reply, sl_dict_t);
+ if (dict == NULL) {
+ return false;
+ }
+
+ /* kMDSStoreHasPersistentUUID = false */
+ result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
+ if (result != 0) {
+ return false;
+ }
+ b = false;
+ result = dalloc_add_copy(dict, &b, sl_bool_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStoreIsBackup = false */
+ result = dalloc_stradd(dict, "kMDSStoreIsBackup");
+ if (result != 0) {
+ return false;
+ }
+ b = false;
+ result = dalloc_add_copy(dict, &b, sl_bool_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStoreUUID = uuid */
+ result = dalloc_stradd(dict, "kMDSStoreUUID");
+ if (result != 0) {
+ return false;
+ }
+ memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
+ result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStoreSupportsVolFS = true */
+ result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
+ if (result != 0) {
+ return false;
+ }
+ b = true;
+ result = dalloc_add_copy(dict, &b, sl_bool_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSVolumeUUID = uuid */
+ result = dalloc_stradd(dict, "kMDSVolumeUUID");
+ if (result != 0) {
+ return false;
+ }
+ memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
+ result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSDiskStoreSpindleNumber = 1 (fake) */
+ result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
+ if (result != 0) {
+ return false;
+ }
+ u = 1;
+ result = dalloc_add_copy(dict, &u, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
+ result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
+ if (result != 0) {
+ return false;
+ }
+ u = 3;
+ result = dalloc_add_copy(dict, &u, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStoreMetaScopes array */
+ result = dalloc_stradd(dict, "kMDSStoreMetaScopes");
+ if (result != 0) {
+ return false;
+ }
+ array = dalloc_zero(dict, sl_array_t);
+ if (array == NULL) {
+ return NULL;
+ }
+ result = dalloc_stradd(array, "kMDQueryScopeComputer");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(dict, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
+ result = dalloc_stradd(dict, "kMDSStoreDevice");
+ if (result != 0) {
+ return false;
+ }
+ u = 0x1000003;
+ result = dalloc_add_copy(dict, &u, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
+ result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
+ if (result != 0) {
+ return false;
+ }
+ b = true;
+ result = dalloc_add_copy(dict, &b, sl_bool_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
+ result = dalloc_stradd(dict, "kMDSStorePathScopes");
+ if (result != 0) {
+ return false;
+ }
+ array = dalloc_zero(dict, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+ s = talloc_strdup(dict, "/");
+ if (s == NULL) {
+ return false;
+ }
+ talloc_set_name(s, "smb_ucs2_t *");
+ result = dalloc_add(array, s, smb_ucs2_t *);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(dict, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+
+ result = dalloc_add(reply, dict, sl_dict_t);
+ if (result != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static void slq_close_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct sl_query *slq = talloc_get_type_abort(
+ private_data, struct sl_query);
+ struct mds_ctx *mds_ctx = slq->mds_ctx;
+
+ SLQ_DEBUG(10, slq, "expired");
+
+ TALLOC_FREE(slq);
+
+ if (CHECK_DEBUGLVL(10)) {
+ for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
+ SLQ_DEBUG(10, slq, "pending");
+ }
+ }
+}
+
+/**
+ * Translate a fake scope from the client like /sharename/dir
+ * to the real server-side path, replacing the "/sharename" part
+ * with the absolute server-side path of the share.
+ **/
+static bool mdssvc_real_scope(struct sl_query *slq, const char *fake_scope)
+{
+ size_t sname_len = strlen(slq->mds_ctx->sharename);
+ size_t fake_scope_len = strlen(fake_scope);
+
+ if (fake_scope_len < sname_len + 1) {
+ DBG_ERR("Short scope [%s] for share [%s]\n",
+ fake_scope, slq->mds_ctx->sharename);
+ return false;
+ }
+
+ slq->path_scope = talloc_asprintf(slq,
+ "%s%s",
+ slq->mds_ctx->spath,
+ fake_scope + sname_len + 1);
+ if (slq->path_scope == NULL) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Begin a search query
+ **/
+static bool slrpc_open_query(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply)
+{
+ bool ok;
+ uint64_t sl_result;
+ uint64_t *uint64p;
+ DALLOC_CTX *reqinfo;
+ sl_array_t *array, *path_scope;
+ sl_cnids_t *cnids;
+ struct sl_query *slq = NULL;
+ int result;
+ const char *querystring = NULL;
+ size_t querystring_len;
+ char *dest = NULL;
+ size_t dest_remaining;
+ size_t nconv;
+ char *scope = NULL;
+
+ array = dalloc_zero(reply, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+
+ /* Allocate and initialize query object */
+ slq = talloc_zero(mds_ctx, struct sl_query);
+ if (slq == NULL) {
+ return false;
+ }
+ slq->entries_ctx = talloc_named_const(slq, 0, "struct sl_query.entries_ctx");
+ if (slq->entries_ctx == NULL) {
+ TALLOC_FREE(slq);
+ return false;
+ }
+ talloc_set_destructor(slq, slq_destructor_cb);
+ slq->state = SLQ_STATE_NEW;
+ slq->mds_ctx = mds_ctx;
+
+ slq->last_used = timeval_current();
+ slq->start_time = slq->last_used;
+ slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
+ slq->te = tevent_add_timer(global_event_context(), slq,
+ slq->expire_time, slq_close_timer, slq);
+ if (slq->te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed\n"));
+ goto error;
+ }
+
+ querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0,
+ "DALLOC_CTX", 1,
+ "kMDQueryString",
+ "char *");
+ if (querystring == NULL) {
+ DEBUG(1, ("missing kMDQueryString\n"));
+ goto error;
+ }
+
+ querystring_len = talloc_array_length(querystring);
+
+ slq->query_string = talloc_array(slq, char, querystring_len);
+ if (slq->query_string == NULL) {
+ DEBUG(1, ("out of memory\n"));
+ goto error;
+ }
+ dest = slq->query_string;
+ dest_remaining = talloc_array_length(dest);
+
+ nconv = smb_iconv(mds_ctx->ic_nfd_to_nfc,
+ &querystring,
+ &querystring_len,
+ &dest,
+ &dest_remaining);
+ if (nconv == (size_t)-1) {
+ DBG_ERR("smb_iconv failed for: %s\n", querystring);
+ return false;
+ }
+
+ uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "uint64_t", 1);
+ if (uint64p == NULL) {
+ goto error;
+ }
+ slq->ctx1 = *uint64p;
+ uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "uint64_t", 2);
+ if (uint64p == NULL) {
+ goto error;
+ }
+ slq->ctx2 = *uint64p;
+
+ path_scope = dalloc_value_for_key(query, "DALLOC_CTX", 0,
+ "DALLOC_CTX", 1,
+ "kMDScopeArray",
+ "sl_array_t");
+ if (path_scope == NULL) {
+ DBG_ERR("missing kMDScopeArray\n");
+ goto error;
+ }
+
+ scope = dalloc_get(path_scope, "char *", 0);
+ if (scope == NULL) {
+ scope = dalloc_get(path_scope,
+ "DALLOC_CTX", 0,
+ "char *", 0);
+ }
+ if (scope == NULL) {
+ DBG_ERR("Failed to parse kMDScopeArray\n");
+ goto error;
+ }
+
+ ok = mdssvc_real_scope(slq, scope);
+ if (!ok) {
+ goto error;
+ }
+
+ reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
+ "DALLOC_CTX", 1,
+ "kMDAttributeArray",
+ "sl_array_t");
+ if (reqinfo == NULL) {
+ DBG_ERR("missing kMDAttributeArray\n");
+ goto error;
+ }
+
+ slq->reqinfo = talloc_steal(slq, reqinfo);
+ DEBUG(10, ("requested attributes: %s", dalloc_dump(reqinfo, 0)));
+
+ cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0,
+ "DALLOC_CTX", 1,
+ "kMDQueryItemArray",
+ "sl_array_t");
+ if (cnids) {
+ ok = sort_cnids(slq, cnids->ca_cnids);
+ if (!ok) {
+ goto error;
+ }
+ }
+
+ ok = create_result_handle(slq);
+ if (!ok) {
+ DEBUG(1, ("create_result_handle error\n"));
+ slq->state = SLQ_STATE_ERROR;
+ goto error;
+ }
+
+ SLQ_DEBUG(10, slq, "new");
+
+ DLIST_ADD(mds_ctx->query_list, slq);
+
+ ok = mds_ctx->backend->search_start(slq);
+ if (!ok) {
+ DBG_ERR("backend search_start failed\n");
+ goto error;
+ }
+
+ sl_result = 0;
+ result = dalloc_add_copy(array, &sl_result, uint64_t);
+ if (result != 0) {
+ goto error;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ goto error;
+ }
+ return true;
+
+error:
+ sl_result = UINT64_MAX;
+ TALLOC_FREE(slq);
+ result = dalloc_add_copy(array, &sl_result, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Fetch results of a query
+ **/
+static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query,
+ DALLOC_CTX *reply)
+{
+ bool ok;
+ struct sl_query *slq = NULL;
+ uint64_t *uint64p, ctx1, ctx2;
+ uint64_t status;
+ sl_array_t *array;
+ int result;
+
+ array = dalloc_zero(reply, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+
+ /* Get query for context */
+ uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "uint64_t", 1);
+ if (uint64p == NULL) {
+ goto error;
+ }
+ ctx1 = *uint64p;
+
+ uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "uint64_t", 2);
+ if (uint64p == NULL) {
+ goto error;
+ }
+ ctx2 = *uint64p;
+
+ slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
+ if (slq == NULL) {
+ DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
+ (uintmax_t)ctx1, (uintmax_t)ctx2));
+ goto error;
+ }
+
+ TALLOC_FREE(slq->te);
+ slq->last_used = timeval_current();
+ slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
+ slq->te = tevent_add_timer(global_event_context(), slq,
+ slq->expire_time, slq_close_timer, slq);
+ if (slq->te == NULL) {
+ DEBUG(1, ("tevent_add_timer failed\n"));
+ goto error;
+ }
+
+ SLQ_DEBUG(10, slq, "fetch");
+
+ switch (slq->state) {
+ case SLQ_STATE_RUNNING:
+ case SLQ_STATE_RESULTS:
+ case SLQ_STATE_FULL:
+ case SLQ_STATE_DONE:
+ ok = add_results(array, slq);
+ if (!ok) {
+ DEBUG(1, ("error adding results\n"));
+ goto error;
+ }
+ if (slq->state == SLQ_STATE_FULL) {
+ slq->state = SLQ_STATE_RUNNING;
+ slq->mds_ctx->backend->search_cont(slq);
+ }
+ break;
+
+ case SLQ_STATE_ERROR:
+ DEBUG(1, ("query in error state\n"));
+ goto error;
+
+ default:
+ DEBUG(1, ("unexpected query state %d\n", slq->state));
+ goto error;
+ }
+
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ goto error;
+ }
+ return true;
+
+error:
+ status = UINT64_MAX;
+ TALLOC_FREE(slq);
+ result = dalloc_add_copy(array, &status, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Store metadata attributes for a CNID
+ **/
+static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply)
+{
+ uint64_t sl_result;
+ sl_array_t *array;
+ int result;
+
+ array = dalloc_zero(reply, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+
+ /*
+ * FIXME: not implemented. Used by the client for eg setting
+ * the modification date of the shared directory which clients
+ * poll indicating changes on the share and cause the client
+ * to refresh view.
+ */
+
+ sl_result = 0;
+ result = dalloc_add_copy(array, &sl_result, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Fetch supported metadata attributes for a CNID
+ **/
+static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query,
+ DALLOC_CTX *reply)
+{
+ uint64_t id;
+ sl_cnids_t *cnids;
+ sl_array_t *array;
+ uint64_t sl_result;
+ sl_cnids_t *replycnids;
+ sl_array_t *mdattrs;
+ sl_filemeta_t *fmeta;
+ int result;
+ void *p;
+
+ cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
+ if (cnids == NULL) {
+ return false;
+ }
+
+ p = dalloc_get_object(cnids->ca_cnids, 0);
+ if (p == NULL) {
+ return NULL;
+ }
+ memcpy(&id, p, sizeof(uint64_t));
+
+ /* Result array */
+ array = dalloc_zero(reply, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* Return result value 0 */
+ sl_result = 0;
+ result = dalloc_add_copy(array, &sl_result, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /* Return CNID array */
+ replycnids = talloc_zero(reply, sl_cnids_t);
+ if (replycnids == NULL) {
+ return false;
+ }
+
+ replycnids->ca_cnids = dalloc_new(cnids);
+ if (replycnids->ca_cnids == NULL) {
+ return false;
+ }
+
+ replycnids->ca_unkn1 = 0xfec;
+ replycnids->ca_context = cnids->ca_context;
+ result = dalloc_add_copy(replycnids->ca_cnids, &id, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(array, replycnids, sl_cnids_t);
+ if (result != 0) {
+ return false;
+ }
+
+ /*
+ * FIXME: this should return the real attributes from all
+ * known metadata sources (Tracker and filesystem)
+ */
+ mdattrs = dalloc_zero(reply, sl_array_t);
+ if (mdattrs == NULL) {
+ return false;
+ }
+
+ result = dalloc_stradd(mdattrs, "kMDItemFSName");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(mdattrs, "kMDItemFSSize");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
+ if (result != 0) {
+ return false;
+ }
+
+ fmeta = dalloc_zero(reply, sl_filemeta_t);
+ if (fmeta == NULL) {
+ return false;
+ }
+ result = dalloc_add(fmeta, mdattrs, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(array, fmeta, sl_filemeta_t);
+ if (result != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Fetch metadata attribute values for a CNID
+ **/
+static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply)
+{
+ int result;
+ bool ok;
+ sl_array_t *array;
+ sl_cnids_t *cnids;
+ sl_cnids_t *replycnids;
+ sl_array_t *reqinfo;
+ uint64_t ino;
+ uint64_t sl_result;
+ sl_filemeta_t *fm;
+ sl_array_t *fm_array;
+ sl_nil_t nil;
+ char *path = NULL;
+ struct smb_filename *smb_fname = NULL;
+ struct stat_ex *sp = NULL;
+ struct sl_inode_path_map *elem = NULL;
+ void *p;
+ TDB_DATA val = tdb_null;
+ NTSTATUS status;
+
+ array = dalloc_zero(reply, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+ replycnids = talloc_zero(reply, sl_cnids_t);
+ if (replycnids == NULL) {
+ goto error;
+ }
+ replycnids->ca_cnids = dalloc_new(replycnids);
+ if (replycnids->ca_cnids == NULL) {
+ goto error;
+ }
+ fm = dalloc_zero(array, sl_filemeta_t);
+ if (fm == NULL) {
+ goto error;
+ }
+ fm_array = dalloc_zero(fm, sl_array_t);
+ if (fm_array == NULL) {
+ goto error;
+ }
+ /* For some reason the list of results always starts with a nil entry */
+ result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
+ if (result == -1) {
+ goto error;
+ }
+
+ reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
+ if (reqinfo == NULL) {
+ goto error;
+ }
+
+ cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
+ if (cnids == NULL) {
+ goto error;
+ }
+ p = dalloc_get_object(cnids->ca_cnids, 0);
+ if (p == NULL) {
+ goto error;
+ }
+ memcpy(&ino, p, sizeof(uint64_t));
+
+ replycnids->ca_unkn1 = 0xfec;
+ replycnids->ca_context = cnids->ca_context;
+ result = dalloc_add_copy(replycnids->ca_cnids, &ino, uint64_t);
+ if (result != 0) {
+ goto error;
+ }
+
+ status = dbwrap_fetch(mds_ctx->ino_path_map, reply,
+ make_tdb_data((void*)&ino, sizeof(uint64_t)),
+ &val);
+ if (NT_STATUS_IS_OK(status)) {
+ if (val.dsize != sizeof(p)) {
+ DBG_ERR("invalid record pointer size: %zd\n", val.dsize);
+ TALLOC_FREE(val.dptr);
+ goto error;
+ }
+
+ memcpy(&p, val.dptr, sizeof(p));
+ elem = talloc_get_type_abort(p, struct sl_inode_path_map);
+ path = elem->path;
+
+ sp = &elem->st;
+ }
+
+ ok = add_filemeta(mds_ctx, reqinfo, fm_array, path, sp);
+ if (!ok) {
+ goto error;
+ }
+
+ sl_result = 0;
+ result = dalloc_add_copy(array, &sl_result, uint64_t);
+ if (result != 0) {
+ goto error;
+ }
+ result = dalloc_add(array, replycnids, sl_cnids_t);
+ if (result != 0) {
+ goto error;
+ }
+ result = dalloc_add(fm, fm_array, sl_array_t);
+ if (result != 0) {
+ goto error;
+ }
+ result = dalloc_add(array, fm, sl_filemeta_t);
+ if (result != 0) {
+ goto error;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ goto error;
+ }
+
+ TALLOC_FREE(smb_fname);
+ return true;
+
+error:
+
+ TALLOC_FREE(smb_fname);
+ sl_result = UINT64_MAX;
+ result = dalloc_add_copy(array, &sl_result, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Close a query
+ **/
+static bool slrpc_close_query(struct mds_ctx *mds_ctx,
+ const DALLOC_CTX *query, DALLOC_CTX *reply)
+{
+ struct sl_query *slq = NULL;
+ uint64_t *uint64p, ctx1, ctx2;
+ sl_array_t *array;
+ uint64_t sl_res;
+ int result;
+
+ array = dalloc_zero(reply, sl_array_t);
+ if (array == NULL) {
+ return false;
+ }
+
+ /* Context */
+ uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "uint64_t", 1);
+ if (uint64p == NULL) {
+ goto done;
+ }
+ ctx1 = *uint64p;
+
+ uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "uint64_t", 2);
+ if (uint64p == NULL) {
+ goto done;
+ }
+ ctx2 = *uint64p;
+
+ /* Get query for context and free it */
+ slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
+ if (slq == NULL) {
+ DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
+ (uintmax_t)ctx1, (uintmax_t)ctx2));
+ goto done;
+ }
+
+ SLQ_DEBUG(10, slq, "close");
+ TALLOC_FREE(slq);
+
+done:
+ sl_res = UINT64_MAX;
+ result = dalloc_add_copy(array, &sl_res, uint64_t);
+ if (result != 0) {
+ return false;
+ }
+ result = dalloc_add(reply, array, sl_array_t);
+ if (result != 0) {
+ return false;
+ }
+ return true;
+}
+
+static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
+{
+ bool ok;
+
+ if (mdssvc_ctx != NULL) {
+ return mdssvc_ctx;
+ }
+
+ mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
+ if (mdssvc_ctx == NULL) {
+ return NULL;
+ }
+
+ mdssvc_ctx->ev_ctx = ev;
+
+ ok = mdsscv_backend_noindex.init(mdssvc_ctx);
+ if (!ok) {
+ DBG_ERR("backend init failed\n");
+ TALLOC_FREE(mdssvc_ctx);
+ return NULL;
+ }
+
+#ifdef HAVE_SPOTLIGHT_BACKEND_ES
+ ok = mdsscv_backend_es.init(mdssvc_ctx);
+ if (!ok) {
+ DBG_ERR("backend init failed\n");
+ TALLOC_FREE(mdssvc_ctx);
+ return NULL;
+ }
+#endif
+
+#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
+ ok = mdsscv_backend_tracker.init(mdssvc_ctx);
+ if (!ok) {
+ DBG_ERR("backend init failed\n");
+ TALLOC_FREE(mdssvc_ctx);
+ return NULL;
+ }
+#endif
+
+ return mdssvc_ctx;
+}
+
+/**
+ * Init callbacks at startup
+ *
+ * This gets typically called in the main parent smbd which means we can't
+ * initialize our global state here.
+ **/
+bool mds_init(struct messaging_context *msg_ctx)
+{
+ return true;
+}
+
+bool mds_shutdown(void)
+{
+ bool ok;
+
+ if (mdssvc_ctx == NULL) {
+ return false;
+ }
+
+ ok = mdsscv_backend_noindex.shutdown(mdssvc_ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+#ifdef HAVE_SPOTLIGHT_BACKEND_ES
+ ok = mdsscv_backend_es.shutdown(mdssvc_ctx);
+ if (!ok) {
+ goto fail;
+ }
+#endif
+
+#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
+ ok = mdsscv_backend_tracker.shutdown(mdssvc_ctx);
+ if (!ok) {
+ goto fail;
+ }
+#endif
+
+ ok = true;
+fail:
+ TALLOC_FREE(mdssvc_ctx);
+ return ok;
+}
+
+/**
+ * Tear down connections and free all resources
+ **/
+static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
+{
+ /*
+ * We need to free query_list before ino_path_map
+ */
+ while (mds_ctx->query_list != NULL) {
+ /*
+ * slq destructor removes element from list.
+ * Don't use TALLOC_FREE()!
+ */
+ talloc_free(mds_ctx->query_list);
+ }
+ TALLOC_FREE(mds_ctx->ino_path_map);
+
+ if (mds_ctx->conn != NULL) {
+ SMB_VFS_DISCONNECT(mds_ctx->conn);
+ conn_free(mds_ctx->conn);
+ }
+
+ ZERO_STRUCTP(mds_ctx);
+
+ return 0;
+}
+
+/**
+ * Initialise a context per RPC bind
+ *
+ * This ends up being called for every tcon, because the client does a
+ * RPC bind for every tcon, so this is actually a per tcon context.
+ **/
+NTSTATUS mds_init_ctx(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ int snum,
+ const char *sharename,
+ const char *path,
+ struct mds_ctx **_mds_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct smb_filename conn_basedir;
+ struct mds_ctx *mds_ctx;
+ int backend;
+ int ret;
+ bool ok;
+ smb_iconv_t iconv_hnd = (smb_iconv_t)-1;
+ NTSTATUS status;
+
+ if (!lp_spotlight(snum)) {
+ return NT_STATUS_WRONG_VOLUME;
+ }
+
+ mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
+ if (mds_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
+
+ mds_ctx->mdssvc_ctx = mdssvc_init(ev);
+ if (mds_ctx->mdssvc_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ backend = lp_spotlight_backend(snum);
+ switch (backend) {
+ case SPOTLIGHT_BACKEND_NOINDEX:
+ mds_ctx->backend = &mdsscv_backend_noindex;
+ break;
+
+#ifdef HAVE_SPOTLIGHT_BACKEND_ES
+ case SPOTLIGHT_BACKEND_ES:
+ mds_ctx->backend = &mdsscv_backend_es;
+ break;
+#endif
+
+#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
+ case SPOTLIGHT_BACKEND_TRACKER:
+ mds_ctx->backend = &mdsscv_backend_tracker;
+ break;
+#endif
+ default:
+ DBG_ERR("Unknown backend %d\n", backend);
+ TALLOC_FREE(mdssvc_ctx);
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+
+ iconv_hnd = smb_iconv_open_ex(mds_ctx,
+ "UTF8-NFD",
+ "UTF8-NFC",
+ false);
+ if (iconv_hnd == (smb_iconv_t)-1) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+ mds_ctx->ic_nfc_to_nfd = iconv_hnd;
+
+ iconv_hnd = smb_iconv_open_ex(mds_ctx,
+ "UTF8-NFC",
+ "UTF8-NFD",
+ false);
+ if (iconv_hnd == (smb_iconv_t)-1) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+ mds_ctx->ic_nfd_to_nfc = iconv_hnd;
+
+ mds_ctx->sharename = talloc_strdup(mds_ctx, sharename);
+ if (mds_ctx->sharename == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ mds_ctx->spath = talloc_strdup(mds_ctx, path);
+ if (mds_ctx->spath == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+ mds_ctx->spath_len = strlen(path);
+
+ mds_ctx->snum = snum;
+ mds_ctx->pipe_session_info = session_info;
+
+ if (session_info->security_token->num_sids < 1) {
+ status = NT_STATUS_BAD_LOGON_SESSION_STATE;
+ goto error;
+ }
+ sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
+ mds_ctx->uid = session_info->unix_token->uid;
+
+ mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
+ if (mds_ctx->ino_path_map == NULL) {
+ DEBUG(1,("open inode map db failed\n"));
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+
+ status = create_conn_struct_cwd(mds_ctx,
+ ev,
+ msg_ctx,
+ session_info,
+ snum,
+ lp_path(talloc_tos(), lp_sub, snum),
+ &mds_ctx->conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to create conn for vfs: %s\n",
+ nt_errstr(status));
+ goto error;
+ }
+
+ conn_basedir = (struct smb_filename) {
+ .base_name = mds_ctx->conn->connectpath,
+ };
+
+ ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
+ if (ret != 0) {
+ DBG_ERR("vfs_ChDir [%s] failed: %s\n",
+ conn_basedir.base_name, strerror(errno));
+ status = map_nt_error_from_unix(errno);
+ goto error;
+ }
+
+ ok = mds_ctx->backend->connect(mds_ctx);
+ if (!ok) {
+ DBG_ERR("backend connect failed\n");
+ status = NT_STATUS_CONNECTION_RESET;
+ goto error;
+ }
+
+ *_mds_ctx = mds_ctx;
+ return NT_STATUS_OK;
+
+error:
+ if (mds_ctx->ic_nfc_to_nfd != NULL) {
+ smb_iconv_close(mds_ctx->ic_nfc_to_nfd);
+ }
+ if (mds_ctx->ic_nfd_to_nfc != NULL) {
+ smb_iconv_close(mds_ctx->ic_nfd_to_nfc);
+ }
+
+ TALLOC_FREE(mds_ctx);
+ return status;
+}
+
+/**
+ * Dispatch a Spotlight RPC command
+ **/
+bool mds_dispatch(struct mds_ctx *mds_ctx,
+ struct mdssvc_blob *request_blob,
+ struct mdssvc_blob *response_blob,
+ size_t max_fragment_size)
+{
+ bool ok;
+ int ret;
+ DALLOC_CTX *query = NULL;
+ DALLOC_CTX *reply = NULL;
+ char *rpccmd;
+ const struct slrpc_cmd *slcmd;
+ const struct smb_filename conn_basedir = {
+ .base_name = mds_ctx->conn->connectpath,
+ };
+ NTSTATUS status;
+
+ if (CHECK_DEBUGLVL(10)) {
+ const struct sl_query *slq;
+
+ for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
+ SLQ_DEBUG(10, slq, "pending");
+ }
+ }
+
+ response_blob->length = 0;
+
+ DEBUG(10, ("share path: %s\n", mds_ctx->spath));
+
+ query = dalloc_new(mds_ctx);
+ if (query == NULL) {
+ ok = false;
+ goto cleanup;
+ }
+ reply = dalloc_new(mds_ctx);
+ if (reply == NULL) {
+ ok = false;
+ goto cleanup;
+ }
+
+ ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
+ request_blob->length);
+ if (!ok) {
+ DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
+ goto cleanup;
+ }
+
+ DEBUG(5, ("%s", dalloc_dump(query, 0)));
+
+ rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
+ "char *", 0);
+ if (rpccmd == NULL) {
+ DEBUG(1, ("missing primary Spotlight RPC command\n"));
+ ok = false;
+ goto cleanup;
+ }
+
+ DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
+
+ slcmd = slrpc_cmd_by_name(rpccmd);
+ if (slcmd == NULL) {
+ DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
+ rpccmd));
+ ok = false;
+ goto cleanup;
+ }
+
+ ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
+ if (ret != 0) {
+ DBG_ERR("vfs_ChDir [%s] failed: %s\n",
+ conn_basedir.base_name, strerror(errno));
+ ok = false;
+ goto cleanup;
+ }
+
+ ok = slcmd->function(mds_ctx, query, reply);
+ if (!ok) {
+ goto cleanup;
+ }
+
+ DBG_DEBUG("%s", dalloc_dump(reply, 0));
+
+ status = sl_pack_alloc(response_blob,
+ reply,
+ response_blob,
+ max_fragment_size);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("sl_pack_alloc() failed\n");
+ goto cleanup;
+ }
+
+cleanup:
+ talloc_free(query);
+ talloc_free(reply);
+ return ok;
+}
diff --git a/source3/rpc_server/mdssvc/mdssvc.h b/source3/rpc_server/mdssvc/mdssvc.h
new file mode 100644
index 0000000..6d4e684
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc.h
@@ -0,0 +1,168 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2012-2014
+
+ 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 _MDSSVC_H
+#define _MDSSVC_H
+
+#include "dalloc.h"
+#include "marshalling.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/gen_ndr/mdssvc.h"
+
+/*
+ * glib uses TRUE and FALSE which was redefined by "includes.h" to be
+ * unusable, undefine so glib can establish its own working
+ * replacement.
+ */
+#undef TRUE
+#undef FALSE
+
+#define MAX_SL_RESULTS 100
+#define SL_PAGESIZE 50
+#define MAX_SL_RUNTIME 30
+#define MDS_TRACKER_ASYNC_TIMEOUT_MS 250
+
+#define SLQ_DEBUG(lvl, _slq, state) do { if (CHECK_DEBUGLVL(lvl)) { \
+ const struct sl_query *__slq = _slq; \
+ struct timeval_buf start_buf; \
+ const char *start; \
+ struct timeval_buf last_used_buf; \
+ const char *last_used; \
+ struct timeval_buf expire_buf; \
+ const char *expire; \
+ start = timeval_str_buf(&__slq->start_time, false, \
+ true, &start_buf); \
+ last_used = timeval_str_buf(&__slq->last_used, false, \
+ true, &last_used_buf); \
+ expire = timeval_str_buf(&__slq->expire_time, false, \
+ true, &expire_buf); \
+ DEBUG(lvl,("%s slq[0x%jx,0x%jx], start: %s, last_used: %s, " \
+ "expires: %s, query: '%s'\n", state, \
+ (uintmax_t)__slq->ctx1, (uintmax_t)__slq->ctx2, \
+ start, last_used, expire, __slq->query_string)); \
+}} while(0)
+
+/******************************************************************************
+ * Some helper stuff dealing with queries
+ ******************************************************************************/
+
+/* query state */
+typedef enum {
+ SLQ_STATE_NEW, /* Query received from client */
+ SLQ_STATE_RUNNING, /* Query dispatched to Tracker */
+ SLQ_STATE_RESULTS, /* Async Tracker query read */
+ SLQ_STATE_FULL, /* the max amount of result has been queued */
+ SLQ_STATE_DONE, /* Got all results from Tracker */
+ SLQ_STATE_END, /* Query results returned to client */
+ SLQ_STATE_ERROR /* an error happened somewhere */
+} slq_state_t;
+
+/* query structure */
+struct sl_query {
+ struct sl_query *prev, *next; /* list pointers */
+ struct mds_ctx *mds_ctx; /* context handle */
+ void *backend_private; /* search backend private data */
+ slq_state_t state; /* query state */
+ struct timeval start_time; /* Query start time */
+ struct timeval last_used; /* Time of last result fetch */
+ struct timeval expire_time; /* Query expiration time */
+ struct tevent_timer *te; /* query timeout */
+ uint64_t ctx1; /* client context 1 */
+ uint64_t ctx2; /* client context 2 */
+ sl_array_t *reqinfo; /* array with requested metadata */
+ char *query_string; /* the Spotlight query string */
+ uint64_t *cnids; /* restrict query to these CNIDs */
+ size_t cnids_num; /* Size of slq_cnids array */
+ const char *path_scope; /* path to directory to search */
+ struct sl_rslts *query_results; /* query results */
+ TALLOC_CTX *entries_ctx; /* talloc parent of the search results */
+};
+
+struct sl_rslts {
+ int num_results;
+ sl_cnids_t *cnids;
+ sl_array_t *fm_array;
+};
+
+struct sl_inode_path_map {
+ struct mds_ctx *mds_ctx;
+ uint64_t ino;
+ char *path;
+ struct stat_ex st;
+};
+
+/* Per process state */
+struct mdssvc_ctx {
+ struct tevent_context *ev_ctx;
+ void *backend_private;
+};
+
+/* Per tree connect state */
+struct mds_ctx {
+ struct mdssvc_backend *backend;
+ struct mdssvc_ctx *mdssvc_ctx;
+ void *backend_private;
+ struct auth_session_info *pipe_session_info;
+ struct dom_sid sid;
+ uid_t uid;
+ smb_iconv_t ic_nfc_to_nfd;
+ smb_iconv_t ic_nfd_to_nfc;
+ int snum;
+ const char *sharename;
+ const char *spath;
+ size_t spath_len;
+ struct connection_struct *conn;
+ struct sl_query *query_list; /* list of active queries */
+ struct db_context *ino_path_map; /* dbwrap rbt for storing inode->path mappings */
+};
+
+struct mdssvc_backend {
+ bool (*init)(struct mdssvc_ctx *mdssvc_ctx);
+ bool (*connect)(struct mds_ctx *mds_ctx);
+ bool (*search_map)(struct sl_query *slq);
+ bool (*search_start)(struct sl_query *slq);
+ bool (*search_cont)(struct sl_query *slq);
+ bool (*shutdown)(struct mdssvc_ctx *mdssvc_ctx);
+};
+
+/******************************************************************************
+ * Function declarations
+ ******************************************************************************/
+
+/*
+ * mdssvc.c
+ */
+extern bool mds_init(struct messaging_context *msg_ctx);
+extern bool mds_shutdown(void);
+NTSTATUS mds_init_ctx(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ int snum,
+ const char *sharename,
+ const char *path,
+ struct mds_ctx **_mds_ctx);
+extern bool mds_dispatch(struct mds_ctx *mds_ctx,
+ struct mdssvc_blob *request_blob,
+ struct mdssvc_blob *response_blob,
+ size_t max_fragment_size);
+bool mds_add_result(struct sl_query *slq, const char *path);
+
+#endif /* _MDSSVC_H */
diff --git a/source3/rpc_server/mdssvc/mdssvc_es.c b/source3/rpc_server/mdssvc/mdssvc_es.c
new file mode 100644
index 0000000..8460b48
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc_es.c
@@ -0,0 +1,865 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / ES backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/util/time_basic.h"
+#include "lib/tls/tls.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/http/http.h"
+#include "lib/util/tevent_unix.h"
+#include "credentials.h"
+#include "mdssvc.h"
+#include "mdssvc_es.h"
+#include "rpc_server/mdssvc/es_parser.tab.h"
+
+#include <jansson.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MDSSVC_ELASTIC_QUERY_TEMPLATE \
+ "{" \
+ " \"from\": %zu," \
+ " \"size\": %zu," \
+ " \"_source\": [%s]," \
+ " \"query\": {" \
+ " \"query_string\": {" \
+ " \"query\": \"%s\"" \
+ " }" \
+ " }" \
+ "}"
+
+#define MDSSVC_ELASTIC_SOURCES \
+ "\"path.real\""
+
+static bool mdssvc_es_init(struct mdssvc_ctx *mdssvc_ctx)
+{
+ struct mdssvc_es_ctx *mdssvc_es_ctx = NULL;
+ json_error_t json_error;
+ char *default_path = NULL;
+ const char *path = NULL;
+
+ mdssvc_es_ctx = talloc_zero(mdssvc_ctx, struct mdssvc_es_ctx);
+ if (mdssvc_es_ctx == NULL) {
+ return false;
+ }
+ mdssvc_es_ctx->mdssvc_ctx = mdssvc_ctx;
+
+ mdssvc_es_ctx->creds = cli_credentials_init_anon(mdssvc_es_ctx);
+ if (mdssvc_es_ctx->creds == NULL) {
+ TALLOC_FREE(mdssvc_es_ctx);
+ return false;
+ }
+
+ default_path = talloc_asprintf(
+ mdssvc_es_ctx,
+ "%s/mdssvc/elasticsearch_mappings.json",
+ get_dyn_SAMBA_DATADIR());
+ if (default_path == NULL) {
+ TALLOC_FREE(mdssvc_es_ctx);
+ return false;
+ }
+
+ path = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+ "elasticsearch",
+ "mappings",
+ default_path);
+ if (path == NULL) {
+ TALLOC_FREE(mdssvc_es_ctx);
+ return false;
+ }
+
+ mdssvc_es_ctx->mappings = json_load_file(path, 0, &json_error);
+ if (mdssvc_es_ctx->mappings == NULL) {
+ DBG_ERR("Opening mapping file [%s] failed: %s\n",
+ path, json_error.text);
+ TALLOC_FREE(mdssvc_es_ctx);
+ return false;
+ }
+ TALLOC_FREE(default_path);
+
+ mdssvc_ctx->backend_private = mdssvc_es_ctx;
+ return true;
+}
+
+static bool mdssvc_es_shutdown(struct mdssvc_ctx *mdssvc_ctx)
+{
+ return true;
+}
+
+static struct tevent_req *mds_es_connect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mds_es_ctx *mds_es_ctx);
+static int mds_es_connect_recv(struct tevent_req *req);
+static void mds_es_connected(struct tevent_req *subreq);
+static bool mds_es_next_search_trigger(struct mds_es_ctx *mds_es_ctx);
+static void mds_es_search_set_pending(struct sl_es_search *s);
+static void mds_es_search_unset_pending(struct sl_es_search *s);
+
+static int mds_es_ctx_destructor(struct mds_es_ctx *mds_es_ctx)
+{
+ struct sl_es_search *s = mds_es_ctx->searches;
+
+ /*
+ * The per tree-connect state mds_es_ctx (a child of mds_ctx) is about
+ * to go away and has already freed all waiting searches. If there's a
+ * search remaining that's when the search is already active. Reset the
+ * mds_es_ctx pointer, so we can detect this when the search completes.
+ */
+
+ if (s == NULL) {
+ return 0;
+ }
+
+ s->mds_es_ctx = NULL;
+
+ return 0;
+}
+
+static bool mds_es_connect(struct mds_ctx *mds_ctx)
+{
+ struct mdssvc_es_ctx *mdssvc_es_ctx = talloc_get_type_abort(
+ mds_ctx->mdssvc_ctx->backend_private, struct mdssvc_es_ctx);
+ struct mds_es_ctx *mds_es_ctx = NULL;
+ struct tevent_req *subreq = NULL;
+
+ mds_es_ctx = talloc_zero(mds_ctx, struct mds_es_ctx);
+ if (mds_es_ctx == NULL) {
+ return false;
+ }
+ *mds_es_ctx = (struct mds_es_ctx) {
+ .mdssvc_es_ctx = mdssvc_es_ctx,
+ .mds_ctx = mds_ctx,
+ };
+
+ mds_ctx->backend_private = mds_es_ctx;
+ talloc_set_destructor(mds_es_ctx, mds_es_ctx_destructor);
+
+ subreq = mds_es_connect_send(
+ mds_es_ctx,
+ mdssvc_es_ctx->mdssvc_ctx->ev_ctx,
+ mds_es_ctx);
+ if (subreq == NULL) {
+ TALLOC_FREE(mds_es_ctx);
+ return false;
+ }
+ tevent_req_set_callback(subreq, mds_es_connected, mds_es_ctx);
+ return true;
+}
+
+static void mds_es_connected(struct tevent_req *subreq)
+{
+ struct mds_es_ctx *mds_es_ctx = tevent_req_callback_data(
+ subreq, struct mds_es_ctx);
+ int ret;
+ bool ok;
+
+ ret = mds_es_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ DBG_ERR("HTTP connect failed\n");
+ return;
+ }
+
+ ok = mds_es_next_search_trigger(mds_es_ctx);
+ if (!ok) {
+ DBG_ERR("mds_es_next_search_trigger failed\n");
+ }
+ return;
+}
+
+struct mds_es_connect_state {
+ struct tevent_context *ev;
+ struct mds_es_ctx *mds_es_ctx;
+ struct tevent_queue_entry *qe;
+ const char *server_addr;
+ uint16_t server_port;
+ struct tstream_tls_params *tls_params;
+};
+
+static void mds_es_http_connect_done(struct tevent_req *subreq);
+static void mds_es_http_waited(struct tevent_req *subreq);
+
+static struct tevent_req *mds_es_connect_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct mds_es_ctx *mds_es_ctx)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mds_es_connect_state *state = NULL;
+ const char *server_addr = NULL;
+ bool use_tls;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct mds_es_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mds_es_connect_state) {
+ .ev = ev,
+ .mds_es_ctx = mds_es_ctx,
+ };
+
+ server_addr = lp_parm_const_string(
+ mds_es_ctx->mds_ctx->snum,
+ "elasticsearch",
+ "address",
+ "localhost");
+ state->server_addr = talloc_strdup(state, server_addr);
+ if (tevent_req_nomem(state->server_addr, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->server_port = lp_parm_int(
+ mds_es_ctx->mds_ctx->snum,
+ "elasticsearch",
+ "port",
+ 9200);
+
+ use_tls = lp_parm_bool(
+ mds_es_ctx->mds_ctx->snum,
+ "elasticsearch",
+ "use tls",
+ false);
+
+ DBG_DEBUG("Connecting to HTTP%s [%s] port [%"PRIu16"]\n",
+ use_tls ? "S" : "", state->server_addr, state->server_port);
+
+ if (use_tls) {
+ const char *ca_file = lp__tls_cafile();
+ const char *crl_file = lp__tls_crlfile();
+ const char *tls_priority = lp_tls_priority();
+ enum tls_verify_peer_state verify_peer = lp_tls_verify_peer();
+
+ status = tstream_tls_params_client(state,
+ ca_file,
+ crl_file,
+ tls_priority,
+ verify_peer,
+ state->server_addr,
+ &state->tls_params);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed tstream_tls_params_client - %s\n",
+ nt_errstr(status));
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = http_connect_send(state,
+ state->ev,
+ state->server_addr,
+ state->server_port,
+ mds_es_ctx->mdssvc_es_ctx->creds,
+ state->tls_params);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mds_es_http_connect_done, req);
+ return req;
+}
+
+static void mds_es_http_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mds_es_connect_state *state = tevent_req_data(
+ req, struct mds_es_connect_state);
+ int error;
+
+ error = http_connect_recv(subreq,
+ state->mds_es_ctx,
+ &state->mds_es_ctx->http_conn);
+ TALLOC_FREE(subreq);
+ if (error != 0) {
+ DBG_ERR("HTTP connect failed, retrying...\n");
+
+ subreq = tevent_wakeup_send(
+ state->mds_es_ctx,
+ state->mds_es_ctx->mdssvc_es_ctx->mdssvc_ctx->ev_ctx,
+ tevent_timeval_current_ofs(10, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ mds_es_http_waited,
+ req);
+ return;
+ }
+
+ DBG_DEBUG("Connected to HTTP%s [%s] port [%"PRIu16"]\n",
+ state->tls_params ? "S" : "",
+ state->server_addr, state->server_port);
+
+ tevent_req_done(req);
+ return;
+}
+
+static void mds_es_http_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mds_es_connect_state *state = tevent_req_data(
+ req, struct mds_es_connect_state);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, ETIMEDOUT);
+ return;
+ }
+
+ subreq = mds_es_connect_send(
+ state->mds_es_ctx,
+ state->mds_es_ctx->mdssvc_es_ctx->mdssvc_ctx->ev_ctx,
+ state->mds_es_ctx);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, mds_es_connected, state->mds_es_ctx);
+}
+
+static int mds_es_connect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+static void mds_es_reconnect_on_error(struct sl_es_search *s)
+{
+ struct mds_es_ctx *mds_es_ctx = s->mds_es_ctx;
+ struct tevent_req *subreq = NULL;
+
+ if (s->slq != NULL) {
+ s->slq->state = SLQ_STATE_ERROR;
+ }
+
+ DBG_WARNING("Reconnecting HTTP...\n");
+ TALLOC_FREE(mds_es_ctx->http_conn);
+
+ subreq = mds_es_connect_send(
+ mds_es_ctx,
+ mds_es_ctx->mdssvc_es_ctx->mdssvc_ctx->ev_ctx,
+ mds_es_ctx);
+ if (subreq == NULL) {
+ DBG_ERR("mds_es_connect_send failed\n");
+ return;
+ }
+ tevent_req_set_callback(subreq, mds_es_connected, mds_es_ctx);
+}
+
+static int search_destructor(struct sl_es_search *s)
+{
+ if (s->mds_es_ctx == NULL) {
+ return 0;
+ }
+ DLIST_REMOVE(s->mds_es_ctx->searches, s);
+ return 0;
+}
+
+static struct tevent_req *mds_es_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sl_es_search *s);
+static int mds_es_search_recv(struct tevent_req *req);
+static void mds_es_search_done(struct tevent_req *subreq);
+
+static bool mds_es_search(struct sl_query *slq)
+{
+ struct mds_es_ctx *mds_es_ctx = talloc_get_type_abort(
+ slq->mds_ctx->backend_private, struct mds_es_ctx);
+ struct sl_es_search *s = NULL;
+ bool ok;
+
+ s = talloc_zero(slq, struct sl_es_search);
+ if (s == NULL) {
+ return false;
+ }
+ *s = (struct sl_es_search) {
+ .ev = mds_es_ctx->mdssvc_es_ctx->mdssvc_ctx->ev_ctx,
+ .mds_es_ctx = mds_es_ctx,
+ .slq = slq,
+ .size = SL_PAGESIZE,
+ };
+
+ /* 0 would mean no limit */
+ s->max = lp_parm_ulonglong(s->slq->mds_ctx->snum,
+ "elasticsearch",
+ "max results",
+ MAX_SL_RESULTS);
+
+ DBG_DEBUG("Spotlight query: '%s'\n", slq->query_string);
+
+ ok = map_spotlight_to_es_query(
+ s,
+ mds_es_ctx->mdssvc_es_ctx->mappings,
+ slq->path_scope,
+ slq->query_string,
+ &s->es_query);
+ if (!ok) {
+ TALLOC_FREE(s);
+ return false;
+ }
+ DBG_DEBUG("Elasticsearch query: '%s'\n", s->es_query);
+
+ slq->backend_private = s;
+ slq->state = SLQ_STATE_RUNNING;
+ DLIST_ADD_END(mds_es_ctx->searches, s);
+ talloc_set_destructor(s, search_destructor);
+
+ return mds_es_next_search_trigger(mds_es_ctx);
+}
+
+static bool mds_es_next_search_trigger(struct mds_es_ctx *mds_es_ctx)
+{
+ struct tevent_req *subreq = NULL;
+ struct sl_es_search *s = mds_es_ctx->searches;
+
+ if (mds_es_ctx->http_conn == NULL) {
+ DBG_DEBUG("Waiting for HTTP connection...\n");
+ return true;
+ }
+ if (s == NULL) {
+ DBG_DEBUG("No pending searches, idling...\n");
+ return true;
+ }
+ if (s->pending) {
+ DBG_DEBUG("Search pending [%p]\n", s);
+ return true;
+ }
+
+ subreq = mds_es_search_send(s, s->ev, s);
+ if (subreq == NULL) {
+ return false;
+ }
+ tevent_req_set_callback(subreq, mds_es_search_done, s);
+ mds_es_search_set_pending(s);
+ return true;
+}
+
+static void mds_es_search_done(struct tevent_req *subreq)
+{
+ struct sl_es_search *s = tevent_req_callback_data(
+ subreq, struct sl_es_search);
+ struct mds_es_ctx *mds_es_ctx = s->mds_es_ctx;
+ struct sl_query *slq = s->slq;
+ int ret;
+ bool ok;
+
+ DBG_DEBUG("Search done for search [%p]\n", s);
+
+ mds_es_search_unset_pending(s);
+
+ if (mds_es_ctx == NULL) {
+ /*
+ * Search connection closed by the user while s was pending.
+ */
+ TALLOC_FREE(s);
+ return;
+ }
+
+ DLIST_REMOVE(mds_es_ctx->searches, s);
+
+ ret = mds_es_search_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ mds_es_reconnect_on_error(s);
+ return;
+ }
+
+ if (slq == NULL) {
+ /*
+ * Closed by the user. Explicitly free "s" here because the
+ * talloc parent slq is already gone.
+ */
+ TALLOC_FREE(s);
+ goto trigger;
+ }
+
+ SLQ_DEBUG(10, slq, "search done");
+
+ if (s->total == 0 || s->from >= s->max) {
+ slq->state = SLQ_STATE_DONE;
+ goto trigger;
+ }
+
+ if (slq->query_results->num_results >= SL_PAGESIZE) {
+ slq->state = SLQ_STATE_FULL;
+ goto trigger;
+ }
+
+ /*
+ * Reschedule this query as there are more results waiting in the
+ * Elasticsearch server and the client result queue has room as
+ * well. But put it at the end of the list of active queries as a simple
+ * heuristic that should ensure all client queries are dispatched to the
+ * server.
+ */
+ DLIST_ADD_END(mds_es_ctx->searches, s);
+
+trigger:
+ ok = mds_es_next_search_trigger(mds_es_ctx);
+ if (!ok) {
+ DBG_ERR("mds_es_next_search_trigger failed\n");
+ }
+}
+
+static void mds_es_search_http_send_done(struct tevent_req *subreq);
+static void mds_es_search_http_read_done(struct tevent_req *subreq);
+
+struct mds_es_search_state {
+ struct tevent_context *ev;
+ struct sl_es_search *s;
+ struct tevent_queue_entry *qe;
+ struct http_request http_request;
+ struct http_request *http_response;
+};
+
+static int mds_es_search_pending_destructor(struct sl_es_search *s)
+{
+ /*
+ * s is a child of slq which may get freed when a user closes a
+ * query. To maintain the HTTP request/response sequence on the HTTP
+ * channel, we keep processing pending requests and free s when we
+ * receive the HTTP response for pending requests.
+ */
+ DBG_DEBUG("Preserving pending search [%p]\n", s);
+ s->slq = NULL;
+ return -1;
+}
+
+static void mds_es_search_set_pending(struct sl_es_search *s)
+{
+ DBG_DEBUG("Set pending [%p]\n", s);
+ SLQ_DEBUG(10, s->slq, "pending");
+
+ s->pending = true;
+ talloc_set_destructor(s, mds_es_search_pending_destructor);
+}
+
+static void mds_es_search_unset_pending(struct sl_es_search *s)
+{
+ DBG_DEBUG("Unset pending [%p]\n", s);
+ if (s->slq != NULL) {
+ SLQ_DEBUG(10, s->slq, "unset pending");
+ }
+
+ s->pending = false;
+ talloc_set_destructor(s, search_destructor);
+}
+
+static struct tevent_req *mds_es_search_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sl_es_search *s)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct mds_es_search_state *state = NULL;
+ const char *index = NULL;
+ char *elastic_query = NULL;
+ char *uri = NULL;
+ size_t elastic_query_len;
+ char *elastic_query_len_str = NULL;
+ char *hostname = NULL;
+ bool pretty = false;
+
+ req = tevent_req_create(mem_ctx, &state, struct mds_es_search_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct mds_es_search_state) {
+ .ev = ev,
+ .s = s,
+ };
+
+ if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(60, 0))) {
+ return tevent_req_post(req, s->ev);
+ }
+
+ index = lp_parm_const_string(s->slq->mds_ctx->snum,
+ "elasticsearch",
+ "index",
+ "_all");
+ if (tevent_req_nomem(index, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (DEBUGLVL(10)) {
+ pretty = true;
+ }
+
+ uri = talloc_asprintf(state,
+ "/%s/_search%s",
+ index,
+ pretty ? "?pretty" : "");
+ if (tevent_req_nomem(uri, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ elastic_query = talloc_asprintf(state,
+ MDSSVC_ELASTIC_QUERY_TEMPLATE,
+ s->from,
+ s->size,
+ MDSSVC_ELASTIC_SOURCES,
+ s->es_query);
+ if (tevent_req_nomem(elastic_query, req)) {
+ return tevent_req_post(req, ev);
+ }
+ DBG_DEBUG("Elastic query: '%s'\n", elastic_query);
+
+ elastic_query_len = strlen(elastic_query);
+
+ state->http_request = (struct http_request) {
+ .type = HTTP_REQ_POST,
+ .uri = uri,
+ .body = data_blob_const(elastic_query, elastic_query_len),
+ .major = '1',
+ .minor = '1',
+ };
+
+ elastic_query_len_str = talloc_asprintf(state, "%zu", elastic_query_len);
+ if (tevent_req_nomem(elastic_query_len_str, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ hostname = get_myname(state);
+ if (tevent_req_nomem(hostname, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ http_add_header(state, &state->http_request.headers,
+ "Content-Type", "application/json");
+ http_add_header(state, &state->http_request.headers,
+ "Accept", "application/json");
+ http_add_header(state, &state->http_request.headers,
+ "User-Agent", "Samba/mdssvc");
+ http_add_header(state, &state->http_request.headers,
+ "Host", hostname);
+ http_add_header(state, &state->http_request.headers,
+ "Content-Length", elastic_query_len_str);
+
+ subreq = http_send_request_send(state,
+ ev,
+ s->mds_es_ctx->http_conn,
+ &state->http_request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, mds_es_search_http_send_done, req);
+ return req;
+}
+
+static void mds_es_search_http_send_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mds_es_search_state *state = tevent_req_data(
+ req, struct mds_es_search_state);
+ NTSTATUS status;
+
+ DBG_DEBUG("Sent out search [%p]\n", state->s);
+
+ status = http_send_request_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_error(req, map_errno_from_nt_status(status));
+ return;
+ }
+
+ if (state->s->mds_es_ctx == NULL || state->s->slq == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = http_read_response_send(state,
+ state->ev,
+ state->s->mds_es_ctx->http_conn,
+ SL_PAGESIZE * 8192);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, mds_es_search_http_read_done, req);
+}
+
+static void mds_es_search_http_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct mds_es_search_state *state = tevent_req_data(
+ req, struct mds_es_search_state);
+ struct sl_es_search *s = state->s;
+ struct sl_query *slq = s->slq;
+ json_t *root = NULL;
+ json_t *matches = NULL;
+ json_t *match = NULL;
+ size_t i;
+ json_error_t error;
+ size_t hits;
+ NTSTATUS status;
+ int ret;
+ bool ok;
+
+ DBG_DEBUG("Got response for search [%p]\n", s);
+
+ status = http_read_response_recv(subreq, state, &state->http_response);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("HTTP response failed: %s\n", nt_errstr(status));
+ tevent_req_error(req, map_errno_from_nt_status(status));
+ return;
+ }
+
+ if (slq == NULL || s->mds_es_ctx == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ switch (state->http_response->response_code) {
+ case 200:
+ break;
+ default:
+ DBG_ERR("HTTP server response: %u\n",
+ state->http_response->response_code);
+ goto fail;
+ }
+
+ DBG_DEBUG("JSON response:\n%s\n",
+ talloc_strndup(talloc_tos(),
+ (char *)state->http_response->body.data,
+ state->http_response->body.length));
+
+ root = json_loadb((char *)state->http_response->body.data,
+ state->http_response->body.length,
+ 0,
+ &error);
+ if (root == NULL) {
+ DBG_ERR("json_loadb failed\n");
+ goto fail;
+ }
+
+ if (s->total == 0) {
+ /*
+ * Get the total number of results the first time, format
+ * used by Elasticsearch 7.0 or newer
+ */
+ ret = json_unpack(root, "{s: {s: {s: i}}}",
+ "hits", "total", "value", &s->total);
+ if (ret != 0) {
+ /* Format used before 7.0 */
+ ret = json_unpack(root, "{s: {s: i}}",
+ "hits", "total", &s->total);
+ if (ret != 0) {
+ DBG_ERR("json_unpack failed\n");
+ goto fail;
+ }
+ }
+
+ DBG_DEBUG("Total: %zu\n", s->total);
+
+ if (s->total == 0) {
+ json_decref(root);
+ tevent_req_done(req);
+ return;
+ }
+ }
+
+ if (s->max == 0 || s->max > s->total) {
+ s->max = s->total;
+ }
+
+ ret = json_unpack(root, "{s: {s:o}}",
+ "hits", "hits", &matches);
+ if (ret != 0 || matches == NULL) {
+ DBG_ERR("json_unpack hits failed\n");
+ goto fail;
+ }
+
+ hits = json_array_size(matches);
+ if (hits == 0) {
+ DBG_ERR("Hu?! No results?\n");
+ goto fail;
+ }
+ DBG_DEBUG("Hits: %zu\n", hits);
+
+ for (i = 0; i < hits && s->from + i < s->max; i++) {
+ const char *path = NULL;
+
+ match = json_array_get(matches, i);
+ if (match == NULL) {
+ DBG_ERR("Hu?! No value for index %zu\n", i);
+ goto fail;
+ }
+ ret = json_unpack(match,
+ "{s: {s: {s: s}}}",
+ "_source",
+ "path",
+ "real",
+ &path);
+ if (ret != 0) {
+ DBG_ERR("Missing path.real in JSON result\n");
+ goto fail;
+ }
+
+ ok = mds_add_result(slq, path);
+ if (!ok) {
+ DBG_ERR("error adding result for path: %s\n", path);
+ goto fail;
+ }
+ }
+ json_decref(root);
+
+ s->from += hits;
+ slq->state = SLQ_STATE_RESULTS;
+ tevent_req_done(req);
+ return;
+
+fail:
+ if (root != NULL) {
+ json_decref(root);
+ }
+ slq->state = SLQ_STATE_ERROR;
+ tevent_req_error(req, EINVAL);
+ return;
+}
+
+static int mds_es_search_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+static bool mds_es_search_cont(struct sl_query *slq)
+{
+ struct sl_es_search *s = talloc_get_type_abort(
+ slq->backend_private, struct sl_es_search);
+
+ SLQ_DEBUG(10, slq, "continue");
+ DLIST_ADD_END(s->mds_es_ctx->searches, s);
+ return mds_es_next_search_trigger(s->mds_es_ctx);
+}
+
+struct mdssvc_backend mdsscv_backend_es = {
+ .init = mdssvc_es_init,
+ .shutdown = mdssvc_es_shutdown,
+ .connect = mds_es_connect,
+ .search_start = mds_es_search,
+ .search_cont = mds_es_search_cont,
+};
diff --git a/source3/rpc_server/mdssvc/mdssvc_es.h b/source3/rpc_server/mdssvc/mdssvc_es.h
new file mode 100644
index 0000000..19797fa
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc_es.h
@@ -0,0 +1,108 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / HTTP/ES/JSON backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _MDSSVC_ES_H_
+#define _MDSSVC_ES_H_
+
+#include <jansson.h>
+
+/*
+ * Some global state
+ */
+struct mdssvc_es_ctx {
+ struct mdssvc_ctx *mdssvc_ctx;
+ struct cli_credentials *creds;
+ json_t *mappings;
+};
+
+/*
+ * Per mdssvc RPC bind state
+ */
+struct mds_es_ctx {
+ /*
+ * Pointer to higher level mds_ctx
+ */
+ struct mds_ctx *mds_ctx;
+
+ /*
+ * Pointer to our global context
+ */
+ struct mdssvc_es_ctx *mdssvc_es_ctx;
+
+ /*
+ * The HTTP connection handle to the ES server
+ */
+ struct http_conn *http_conn;
+
+ /*
+ * List of pending searches
+ */
+ struct sl_es_search *searches;
+};
+
+/* Per search request */
+struct sl_es_search {
+ /*
+ * List pointers
+ */
+ struct sl_es_search *prev, *next;
+
+ /*
+ * Search is being executed. Only the list head can be pending.
+ */
+ bool pending;
+
+ /*
+ * Shorthand to our tevent context
+ */
+ struct tevent_context *ev;
+
+ /*
+ * Pointer to the RPC connection ctx the request is using
+ */
+ struct mds_es_ctx *mds_es_ctx;
+
+ /*
+ * The upper mdssvc.c level query context
+ */
+ struct sl_query *slq;
+
+ /*
+ * Maximum number of results we process and total number of
+ * results of a query.
+ */
+ size_t total;
+ size_t max;
+
+ /*
+ * For paging results
+ */
+ size_t from;
+ size_t size;
+
+ /*
+ * The translated Es query
+ */
+ char *es_query;
+};
+
+extern struct mdssvc_backend mdsscv_backend_es;
+
+#endif /* _MDSSVC_ES_H_ */
diff --git a/source3/rpc_server/mdssvc/mdssvc_noindex.c b/source3/rpc_server/mdssvc/mdssvc_noindex.c
new file mode 100644
index 0000000..ff466af
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc_noindex.c
@@ -0,0 +1,57 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / noindex backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "mdssvc.h"
+
+static bool mdssvc_noindex_init(struct mdssvc_ctx *mdssvc_ctx)
+{
+ return true;
+}
+
+static bool mdssvc_noindex_shutdown(struct mdssvc_ctx *mdssvc_ctx)
+{
+ return true;
+}
+
+static bool mds_noindex_connect(struct mds_ctx *mds_ctx)
+{
+ return true;
+}
+
+static bool mds_noindex_search_start(struct sl_query *slq)
+{
+ slq->state = SLQ_STATE_DONE;
+ return true;
+}
+
+static bool mds_noindex_search_cont(struct sl_query *slq)
+{
+ slq->state = SLQ_STATE_DONE;
+ return true;
+}
+
+struct mdssvc_backend mdsscv_backend_noindex = {
+ .init = mdssvc_noindex_init,
+ .shutdown = mdssvc_noindex_shutdown,
+ .connect = mds_noindex_connect,
+ .search_start = mds_noindex_search_start,
+ .search_cont = mds_noindex_search_cont,
+};
diff --git a/source3/rpc_server/mdssvc/mdssvc_noindex.h b/source3/rpc_server/mdssvc/mdssvc_noindex.h
new file mode 100644
index 0000000..750ee44
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc_noindex.h
@@ -0,0 +1,26 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / noindex backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _MDSSVC_NOINDEX_H_
+#define _MDSSVC_NOINDEX_H_
+
+extern struct mdssvc_backend mdsscv_backend_noindex;
+
+#endif /* _MDSSVC_VOID_H_ */
diff --git a/source3/rpc_server/mdssvc/mdssvc_tracker.c b/source3/rpc_server/mdssvc/mdssvc_tracker.c
new file mode 100644
index 0000000..54f391e
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc_tracker.c
@@ -0,0 +1,499 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / Tracker backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/time_basic.h"
+#include "mdssvc.h"
+#include "mdssvc_tracker.h"
+#include "lib/tevent_glib_glue.h"
+#include "rpc_server/mdssvc/sparql_parser.tab.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static struct mdssvc_tracker_ctx *mdssvc_tracker_ctx;
+
+/************************************************
+ * Tracker async callbacks
+ ************************************************/
+
+static void tracker_con_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ struct mds_tracker_ctx *ctx = NULL;
+ TrackerSparqlConnection *tracker_con = NULL;
+ GError *error = NULL;
+
+ tracker_con = tracker_sparql_connection_get_finish(res, &error);
+ if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ /*
+ * If the async request was cancelled, user_data will already be
+ * talloc_free'd, so we must be carefully checking for
+ * G_IO_ERROR_CANCELLED before using user_data.
+ */
+ DBG_ERR("Tracker connection cancelled\n");
+ g_error_free(error);
+ return;
+ }
+ /*
+ * Ok, we're not cancelled, we can now safely use user_data.
+ */
+ ctx = talloc_get_type_abort(user_data, struct mds_tracker_ctx);
+ ctx->async_pending = false;
+ /*
+ * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
+ */
+ if (error) {
+ DBG_ERR("Could not connect to Tracker: %s\n", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ ctx->tracker_con = tracker_con;
+
+ DBG_DEBUG("connected to Tracker\n");
+}
+
+static void tracker_cursor_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+
+static void tracker_query_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ struct sl_tracker_query *tq = NULL;
+ struct sl_query *slq = NULL;
+ TrackerSparqlConnection *conn = NULL;
+ TrackerSparqlCursor *cursor = NULL;
+ GError *error = NULL;
+
+ conn = TRACKER_SPARQL_CONNECTION(object);
+
+ cursor = tracker_sparql_connection_query_finish(conn, res, &error);
+ /*
+ * If the async request was cancelled, user_data will already be
+ * talloc_free'd, so we must be carefully checking for
+ * G_IO_ERROR_CANCELLED before using user_data.
+ */
+ if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ DBG_ERR("Tracker query cancelled\n");
+ if (cursor != NULL) {
+ g_object_unref(cursor);
+ }
+ g_error_free(error);
+ return;
+ }
+ /*
+ * Ok, we're not cancelled, we can now safely use user_data.
+ */
+ tq = talloc_get_type_abort(user_data, struct sl_tracker_query);
+ tq->async_pending = false;
+ slq = tq->slq;
+ /*
+ * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
+ */
+ if (error) {
+ DBG_ERR("Tracker query error: %s\n", error->message);
+ g_error_free(error);
+ slq->state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ tq->cursor = cursor;
+ slq->state = SLQ_STATE_RESULTS;
+
+ tracker_sparql_cursor_next_async(tq->cursor,
+ tq->gcancellable,
+ tracker_cursor_cb,
+ tq);
+ tq->async_pending = true;
+}
+
+static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri)
+{
+ GFile *f = NULL;
+ char *path = NULL;
+ char *talloc_path = NULL;
+
+ f = g_file_new_for_uri(uri);
+ if (f == NULL) {
+ return NULL;
+ }
+
+ path = g_file_get_path(f);
+ g_object_unref(f);
+
+ if (path == NULL) {
+ return NULL;
+ }
+
+ talloc_path = talloc_strdup(mem_ctx, path);
+ g_free(path);
+ if (talloc_path == NULL) {
+ return NULL;
+ }
+
+ return talloc_path;
+}
+
+static void tracker_cursor_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ TrackerSparqlCursor *cursor = NULL;
+ struct sl_tracker_query *tq = NULL;
+ struct sl_query *slq = NULL;
+ const gchar *uri = NULL;
+ GError *error = NULL;
+ char *path = NULL;
+ gboolean more_results;
+ bool ok;
+
+ cursor = TRACKER_SPARQL_CURSOR(object);
+ more_results = tracker_sparql_cursor_next_finish(cursor,
+ res,
+ &error);
+ /*
+ * If the async request was cancelled, user_data will already be
+ * talloc_free'd, so we must be carefully checking for
+ * G_IO_ERROR_CANCELLED before using user_data.
+ */
+ if (error && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_error_free(error);
+ g_object_unref(cursor);
+ return;
+ }
+ /*
+ * Ok, we're not cancelled, we can now safely use user_data.
+ */
+ tq = talloc_get_type_abort(user_data, struct sl_tracker_query);
+ tq->async_pending = false;
+ slq = tq->slq;
+ /*
+ * Check error again, above we only checked for G_IO_ERROR_CANCELLED.
+ */
+ if (error) {
+ DBG_ERR("Tracker cursor: %s\n", error->message);
+ g_error_free(error);
+ slq->state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ SLQ_DEBUG(10, slq, "results");
+
+ if (!more_results) {
+ slq->state = SLQ_STATE_DONE;
+
+ g_object_unref(tq->cursor);
+ tq->cursor = NULL;
+
+ g_object_unref(tq->gcancellable);
+ tq->gcancellable = NULL;
+ return;
+ }
+
+ uri = tracker_sparql_cursor_get_string(tq->cursor, 0, NULL);
+ if (uri == NULL) {
+ DBG_ERR("error fetching Tracker URI\n");
+ slq->state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ path = tracker_to_unix_path(slq->query_results, uri);
+ if (path == NULL) {
+ DBG_ERR("error converting Tracker URI to path: %s\n", uri);
+ slq->state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ ok = mds_add_result(slq, path);
+ if (!ok) {
+ DBG_ERR("error adding result for path: %s\n", uri);
+ slq->state = SLQ_STATE_ERROR;
+ return;
+ }
+
+ if (slq->query_results->num_results >= MAX_SL_RESULTS) {
+ slq->state = SLQ_STATE_FULL;
+ SLQ_DEBUG(10, slq, "full");
+ return;
+ }
+
+ slq->state = SLQ_STATE_RESULTS;
+ SLQ_DEBUG(10, slq, "cursor next");
+
+ tracker_sparql_cursor_next_async(tq->cursor,
+ tq->gcancellable,
+ tracker_cursor_cb,
+ tq);
+ tq->async_pending = true;
+}
+
+/*
+ * This gets called once, even if the backend is not configured by the user
+ */
+static bool mdssvc_tracker_init(struct mdssvc_ctx *mdssvc_ctx)
+{
+ if (mdssvc_tracker_ctx != NULL) {
+ return true;
+ }
+
+#if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36)
+ g_type_init();
+#endif
+
+ mdssvc_tracker_ctx = talloc_zero(mdssvc_ctx, struct mdssvc_tracker_ctx);
+ if (mdssvc_tracker_ctx == NULL) {
+ return false;
+ }
+ mdssvc_tracker_ctx->mdssvc_ctx = mdssvc_ctx;
+
+ return true;
+}
+
+/*
+ * This gets called per mdscmd_open / tcon. This runs initialisation code that
+ * should only run if the tracker backend is actually used.
+ */
+static bool mdssvc_tracker_prepare(void)
+{
+ if (mdssvc_tracker_ctx->gmain_ctx != NULL) {
+ /*
+ * Assuming everything is setup if gmain_ctx is.
+ */
+ return true;
+ }
+
+ mdssvc_tracker_ctx->gmain_ctx = g_main_context_new();
+ if (mdssvc_tracker_ctx->gmain_ctx == NULL) {
+ DBG_ERR("error from g_main_context_new\n");
+ return false;
+ }
+
+ mdssvc_tracker_ctx->glue = samba_tevent_glib_glue_create(
+ mdssvc_tracker_ctx,
+ mdssvc_tracker_ctx->mdssvc_ctx->ev_ctx,
+ mdssvc_tracker_ctx->gmain_ctx);
+ if (mdssvc_tracker_ctx->glue == NULL) {
+ DBG_ERR("samba_tevent_glib_glue_create failed\n");
+ g_object_unref(mdssvc_tracker_ctx->gmain_ctx);
+ mdssvc_tracker_ctx->gmain_ctx = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+static bool mdssvc_tracker_shutdown(struct mdssvc_ctx *mdssvc_ctx)
+{
+ if (mdssvc_tracker_ctx == NULL) {
+ return true;
+ }
+
+ if (mdssvc_tracker_ctx->gmain_ctx == NULL) {
+ return true;
+ }
+
+ samba_tevent_glib_glue_quit(mdssvc_tracker_ctx->glue);
+ TALLOC_FREE(mdssvc_tracker_ctx->glue);
+
+ g_object_unref(mdssvc_tracker_ctx->gmain_ctx);
+ mdssvc_tracker_ctx->gmain_ctx = NULL;
+ return true;
+}
+
+static int mds_tracker_ctx_destructor(struct mds_tracker_ctx *ctx)
+{
+ /*
+ * Don't g_object_unref() the connection if there's an async request
+ * pending, it's used in the async callback and will be unreferenced
+ * there.
+ */
+ if (ctx->async_pending) {
+ g_cancellable_cancel(ctx->gcancellable);
+ ctx->gcancellable = NULL;
+ return 0;
+ }
+
+ if (ctx->tracker_con == NULL) {
+ return 0;
+ }
+ g_object_unref(ctx->tracker_con);
+ ctx->tracker_con = NULL;
+
+ return 0;
+}
+
+static bool mds_tracker_connect(struct mds_ctx *mds_ctx)
+{
+ struct mds_tracker_ctx *ctx = NULL;
+ bool ok;
+
+ ok = mdssvc_tracker_prepare();
+ if (!ok) {
+ return false;
+ }
+
+ ctx = talloc_zero(mds_ctx, struct mds_tracker_ctx);
+ if (ctx == NULL) {
+ return false;
+ }
+ talloc_set_destructor(ctx, mds_tracker_ctx_destructor);
+
+ ctx->mds_ctx = mds_ctx;
+
+ ctx->gcancellable = g_cancellable_new();
+ if (ctx->gcancellable == NULL) {
+ DBG_ERR("error from g_cancellable_new\n");
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ tracker_sparql_connection_get_async(ctx->gcancellable,
+ tracker_con_cb,
+ ctx);
+ ctx->async_pending = true;
+
+ mds_ctx->backend_private = ctx;
+
+ return true;
+}
+
+static int tq_destructor(struct sl_tracker_query *tq)
+{
+ /*
+ * Don't g_object_unref() the cursor if there's an async request
+ * pending, it's used in the async callback and will be unreferenced
+ * there.
+ */
+ if (tq->async_pending) {
+ g_cancellable_cancel(tq->gcancellable);
+ tq->gcancellable = NULL;
+ return 0;
+ }
+
+ if (tq->cursor == NULL) {
+ return 0;
+ }
+ g_object_unref(tq->cursor);
+ tq->cursor = NULL;
+ return 0;
+}
+
+static bool mds_tracker_search_start(struct sl_query *slq)
+{
+ struct mds_tracker_ctx *tmds_ctx = talloc_get_type_abort(
+ slq->mds_ctx->backend_private, struct mds_tracker_ctx);
+ struct sl_tracker_query *tq = NULL;
+ char *escaped_scope = NULL;
+ bool ok;
+
+ if (tmds_ctx->tracker_con == NULL) {
+ DBG_ERR("no connection to Tracker\n");
+ return false;
+ }
+
+ tq = talloc_zero(slq, struct sl_tracker_query);
+ if (tq == NULL) {
+ return false;
+ }
+ tq->slq = slq;
+ talloc_set_destructor(tq, tq_destructor);
+
+ tq->gcancellable = g_cancellable_new();
+ if (tq->gcancellable == NULL) {
+ DBG_ERR("g_cancellable_new() failed\n");
+ goto error;
+ }
+
+ escaped_scope = g_uri_escape_string(
+ slq->path_scope,
+ G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
+ TRUE);
+ if (escaped_scope == NULL) {
+ goto error;
+ }
+
+ tq->path_scope = talloc_strdup(tq, escaped_scope);
+ g_free(escaped_scope);
+ escaped_scope = NULL;
+ if (tq->path_scope == NULL) {
+ goto error;
+ }
+
+ slq->backend_private = tq;
+
+ ok = map_spotlight_to_sparql_query(slq);
+ if (!ok) {
+ /*
+ * Two cases:
+ *
+ * 1) the query string is "false", the parser returns
+ * an error for that. We're supposed to return -1
+ * here.
+ *
+ * 2) the parsing really failed, in that case we're
+ * probably supposed to return -1 too, this needs
+ * verification though
+ */
+ goto error;
+ }
+
+ DBG_DEBUG("SPARQL query: \"%s\"\n", tq->sparql_query);
+
+ tracker_sparql_connection_query_async(tmds_ctx->tracker_con,
+ tq->sparql_query,
+ tq->gcancellable,
+ tracker_query_cb,
+ tq);
+ tq->async_pending = true;
+
+ slq->state = SLQ_STATE_RUNNING;
+ return true;
+error:
+ g_object_unref(tq->gcancellable);
+ TALLOC_FREE(tq);
+ slq->backend_private = NULL;
+ return false;
+}
+
+static bool mds_tracker_search_cont(struct sl_query *slq)
+{
+ struct sl_tracker_query *tq = talloc_get_type_abort(
+ slq->backend_private, struct sl_tracker_query);
+
+ tracker_sparql_cursor_next_async(tq->cursor,
+ tq->gcancellable,
+ tracker_cursor_cb,
+ tq);
+ tq->async_pending = true;
+
+ return true;
+}
+
+struct mdssvc_backend mdsscv_backend_tracker = {
+ .init = mdssvc_tracker_init,
+ .shutdown = mdssvc_tracker_shutdown,
+ .connect = mds_tracker_connect,
+ .search_start = mds_tracker_search_start,
+ .search_cont = mds_tracker_search_cont,
+};
diff --git a/source3/rpc_server/mdssvc/mdssvc_tracker.h b/source3/rpc_server/mdssvc/mdssvc_tracker.h
new file mode 100644
index 0000000..54a4a33
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc_tracker.h
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines / Tracker backend
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* allow building with --enable-developer */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#include <gio/gio.h>
+#include <tracker-sparql.h>
+#pragma GCC diagnostic pop
+
+/* Global */
+struct mdssvc_tracker_ctx {
+ struct mdssvc_ctx *mdssvc_ctx;
+ GMainContext *gmain_ctx;
+ struct tevent_glib_glue *glue;
+};
+
+/* Per tree connect state */
+struct mds_tracker_ctx {
+ struct mds_ctx *mds_ctx;
+ GCancellable *gcancellable;
+ bool async_pending;
+ TrackerSparqlConnection *tracker_con;
+};
+
+/* Per query */
+struct sl_tracker_query {
+ struct sl_query *slq;
+ const char *path_scope;
+ const char *sparql_query;
+
+ /*
+ * Notes on the lifetime of cursor: we hold a reference on the object
+ * and have to call g_object_unref(cursor) at the right place. This is
+ * either done in the talloc destructor on a struct sl_tracker_query
+ * talloc object when there are no tracker glib async requests
+ * running. Or in the glib callback after cancelling the glib async
+ * request.
+ */
+ TrackerSparqlCursor *cursor;
+ GCancellable *gcancellable;
+ bool async_pending;
+};
+
+extern struct mdssvc_backend mdsscv_backend_tracker;
diff --git a/source3/rpc_server/mdssvc/sparql_lexer.l b/source3/rpc_server/mdssvc/sparql_lexer.l
new file mode 100644
index 0000000..b638350
--- /dev/null
+++ b/source3/rpc_server/mdssvc/sparql_lexer.l
@@ -0,0 +1,67 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2012-2014
+
+ 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/mdssvc/sparql_parser.tab.h"
+
+#define YY_NO_INPUT
+%}
+
+%option nounput noyyalloc noyyrealloc prefix="mdsyy"
+
+ASC [a-zA-Z0-9_\*\:\-\.]
+U [\x80-\xbf]
+U2 [\xc2-\xdf]
+U3 [\xe0-\xef]
+U4 [\xf0-\xf4]
+
+UANY {ASC}|{U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+UONLY {U2}{U}|{U3}{U}{U}|{U4}{U}{U}{U}
+
+%%
+InRange return FUNC_INRANGE;
+\$time\.iso return DATE_ISO;
+false {mdsyylval.bval = false; return BOOL;}
+true {mdsyylval.bval = true; return BOOL;}
+\" return QUOTE;
+\( return OBRACE;
+\) return CBRACE;
+\&\& return AND;
+\|\| return OR;
+\=\= return EQUAL;
+\!\= return UNEQUAL;
+\= return EQUAL;
+\< return LT;
+\> return GT;
+\, return COMMA;
+{UANY}+ {mdsyylval.sval = talloc_strdup(talloc_tos(), mdsyytext); return WORD;}
+[ \t\n] /* ignore */
+%%
+
+void *yyalloc(yy_size_t bytes)
+{
+ return SMB_MALLOC(bytes);
+}
+
+void *yyrealloc(void *ptr, yy_size_t bytes)
+{
+ return SMB_REALLOC(ptr, bytes);
+}
diff --git a/source3/rpc_server/mdssvc/sparql_mapping.c b/source3/rpc_server/mdssvc/sparql_mapping.c
new file mode 100644
index 0000000..c71c7a5
--- /dev/null
+++ b/source3/rpc_server/mdssvc/sparql_mapping.c
@@ -0,0 +1,378 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2012-2014
+
+ 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 "replace.h"
+#include "sparql_mapping.h"
+
+const struct sl_attr_map *sl_attr_map_by_spotlight(const char *sl_attr)
+{
+ static const struct sl_attr_map spotlight_sparql_attr_map[] = {
+ {
+ .spotlight_attr = "*",
+ .type = ssmt_fts,
+ .sparql_attr = "fts:match",
+ },
+
+ /* Filesystem metadata */
+ {
+ .spotlight_attr = "kMDItemFSLabel",
+ .type = ssmt_num,
+ .sparql_attr = NULL,
+ },
+ {
+ .spotlight_attr = "kMDItemDisplayName",
+ .type = ssmt_str,
+ .sparql_attr = "nfo:fileName",
+ },
+ {
+ .spotlight_attr = "kMDItemFSName",
+ .type = ssmt_str,
+ .sparql_attr = "nfo:fileName",
+ },
+ {
+ .spotlight_attr = "kMDItemFSContentChangeDate",
+ .type = ssmt_date,
+ .sparql_attr = "nfo:fileLastModified",
+ },
+ {
+ .spotlight_attr = "kMDItemLastUsedDate",
+ .type = ssmt_date,
+ .sparql_attr = "nfo:fileLastAccessed",
+ },
+
+ /* Common metadata */
+ {
+ .spotlight_attr = "kMDItemTextContent",
+ .type = ssmt_fts,
+ .sparql_attr = "fts:match",
+ },
+ {
+ .spotlight_attr = "kMDItemContentCreationDate",
+ .type = ssmt_date,
+ .sparql_attr = "nie:contentCreated",
+ },
+ {
+ .spotlight_attr = "kMDItemContentModificationDate",
+ .type = ssmt_date,
+ .sparql_attr = "nfo:fileLastModified",
+ },
+ {
+ .spotlight_attr = "kMDItemAttributeChangeDate",
+ .type = ssmt_date,
+ .sparql_attr = "nfo:fileLastModified",
+ },
+ {
+ .spotlight_attr = "kMDItemAuthors",
+ .type = ssmt_str,
+ .sparql_attr = "dc:creator",
+ },
+ {
+ .spotlight_attr = "kMDItemCopyright",
+ .type = ssmt_str,
+ .sparql_attr = "nie:copyright",
+ },
+ {
+ .spotlight_attr = "kMDItemCountry",
+ .type = ssmt_str,
+ .sparql_attr = "nco:country",
+ },
+ {
+ .spotlight_attr = "kMDItemCreator",
+ .type = ssmt_str,
+ .sparql_attr = "dc:creator",
+ },
+ {
+ .spotlight_attr = "kMDItemDurationSeconds",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:duration",
+ },
+ {
+ .spotlight_attr = "kMDItemNumberOfPages",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:pageCount",
+ },
+ {
+ .spotlight_attr = "kMDItemTitle",
+ .type = ssmt_str,
+ .sparql_attr = "nie:title",
+ },
+ {
+ .spotlight_attr = "kMDItemCity",
+ .type = ssmt_str,
+ .sparql_attr = "nco:locality",
+ },
+ {
+ .spotlight_attr = "kMDItemCoverage",
+ .type = ssmt_str,
+ .sparql_attr = "nco:locality",
+ },
+ {
+ .spotlight_attr = "_kMDItemGroupId",
+ .type = ssmt_type,
+ .sparql_attr = NULL,
+ },
+ {
+ .spotlight_attr = "kMDItemContentTypeTree",
+ .type = ssmt_type,
+ .sparql_attr = NULL,
+ },
+ {
+ .spotlight_attr = "kMDItemContentType",
+ .type = ssmt_type,
+ .sparql_attr = NULL,
+ },
+
+ /* Image metadata */
+ {
+ .spotlight_attr = "kMDItemPixelWidth",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:width",
+ },
+ {
+ .spotlight_attr = "kMDItemPixelHeight",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:height",
+ },
+ {
+ .spotlight_attr = "kMDItemColorSpace",
+ .type = ssmt_str,
+ .sparql_attr = "nexif:colorSpace",
+ },
+ {
+ .spotlight_attr = "kMDItemBitsPerSample",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:colorDepth",
+ },
+ {
+ .spotlight_attr = "kMDItemFocalLength",
+ .type = ssmt_num,
+ .sparql_attr = "nmm:focalLength",
+ },
+ {
+ .spotlight_attr = "kMDItemISOSpeed",
+ .type = ssmt_num,
+ .sparql_attr = "nmm:isoSpeed",
+ },
+ {
+ .spotlight_attr = "kMDItemOrientation",
+ .type = ssmt_bool,
+ .sparql_attr = "nfo:orientation",
+ },
+ {
+ .spotlight_attr = "kMDItemResolutionWidthDPI",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:horizontalResolution",
+ },
+ {
+ .spotlight_attr = "kMDItemResolutionHeightDPI",
+ .type = ssmt_num,
+ .sparql_attr = "nfo:verticalResolution",
+ },
+ {
+ .spotlight_attr = "kMDItemExposureTimeSeconds",
+ .type = ssmt_num,
+ .sparql_attr = "nmm:exposureTime",
+ },
+
+ /* Audio metadata */
+ {
+ .spotlight_attr = "kMDItemComposer",
+ .type = ssmt_str,
+ .sparql_attr = "nmm:composer",
+ },
+ {
+ .spotlight_attr = "kMDItemMusicalGenre",
+ .type = ssmt_str,
+ .sparql_attr = "nfo:genre",
+ },
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(spotlight_sparql_attr_map); i++) {
+ const struct sl_attr_map *m = &spotlight_sparql_attr_map[i];
+ int cmp;
+
+ cmp = strcmp(m->spotlight_attr, sl_attr);
+ if (cmp == 0) {
+ return m;
+ }
+ }
+
+ return NULL;
+}
+
+const struct sl_type_map *sl_type_map_by_spotlight(const char *sl_type)
+{
+ static const struct sl_type_map spotlight_sparql_type_map[] = {
+ {
+ .spotlight_type = "1",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Email",
+ },
+ {
+ .spotlight_type = "2",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact",
+ },
+ {
+ .spotlight_type = "3",
+ .type = kMDTypeMapNotSup,
+ .sparql_type = NULL, /*PrefPane*/
+ },
+ {
+ .spotlight_type = "4",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Font",
+ },
+ {
+ .spotlight_type = "5",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark",
+ },
+ {
+ .spotlight_type = "6",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact",
+ },
+ {
+ .spotlight_type = "7",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video",
+ },
+ {
+ .spotlight_type = "8",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Executable",
+ },
+ {
+ .spotlight_type = "9",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Folder",
+ },
+ {
+ .spotlight_type = "10",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio",
+ },
+ {
+ .spotlight_type = "11",
+ .type = kMDTypeMapMime,
+ .sparql_type = "application/pdf",
+ },
+ {
+ .spotlight_type = "12",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Presentation",
+ },
+ {
+ .spotlight_type = "13",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image",
+ },
+ {
+ .spotlight_type = "public.jpeg",
+ .type = kMDTypeMapMime,
+ .sparql_type = "image/jpeg",
+ },
+ {
+ .spotlight_type = "public.tiff",
+ .type = kMDTypeMapMime,
+ .sparql_type = "image/tiff",
+ },
+ {
+ .spotlight_type = "com.compuserve.gif",
+ .type = kMDTypeMapMime,
+ .sparql_type = "image/gif",
+ },
+ {
+ .spotlight_type = "public.png",
+ .type = kMDTypeMapMime,
+ .sparql_type = "image/png",
+ },
+ {
+ .spotlight_type = "com.microsoft.bmp",
+ .type = kMDTypeMapMime,
+ .sparql_type = "image/bmp",
+ },
+ {
+ .spotlight_type = "public.content",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Document",
+ },
+ {
+ .spotlight_type = "public.mp3",
+ .type = kMDTypeMapMime,
+ .sparql_type = "audio/mpeg",
+ },
+ {
+ .spotlight_type = "public.mpeg-4-audio",
+ .type = kMDTypeMapMime,
+ .sparql_type = "audio/x-aac",
+ },
+ {
+ .spotlight_type = "com.apple.application",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Software",
+ },
+ {
+ .spotlight_type = "public.text",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#TextDocument",
+ },
+ {
+ .spotlight_type = "public.plain-text",
+ .type = kMDTypeMapMime,
+ .sparql_type = "text/plain",
+ },
+ {
+ .spotlight_type = "public.rtf",
+ .type = kMDTypeMapMime,
+ .sparql_type = "text/rtf",
+ },
+ {
+ .spotlight_type = "public.html",
+ .type = kMDTypeMapMime,
+ .sparql_type = "text/html",
+ },
+ {
+ .spotlight_type = "public.xml",
+ .type = kMDTypeMapMime,
+ .sparql_type = "text/xml",
+ },
+ {
+ .spotlight_type = "public.source-code",
+ .type = kMDTypeMapRDF,
+ .sparql_type = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SourceCode",
+ },
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(spotlight_sparql_type_map); i++) {
+ const struct sl_type_map *m = &spotlight_sparql_type_map[i];
+ int cmp;
+
+ cmp = strcmp(m->spotlight_type, sl_type);
+ if (cmp == 0) {
+ return m;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source3/rpc_server/mdssvc/sparql_mapping.h b/source3/rpc_server/mdssvc/sparql_mapping.h
new file mode 100644
index 0000000..14fab67
--- /dev/null
+++ b/source3/rpc_server/mdssvc/sparql_mapping.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2012 Ralph Boehme
+
+ 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 2 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.
+*/
+
+#ifndef SPOTLIGHT_SPARQL_MAP_H
+#define SPOTLIGHT_SPARQL_MAP_H
+
+enum ssm_type {
+ ssmt_bool, /* a boolean value that doesn't requires a SPARQL FILTER */
+ ssmt_num, /* a numeric value that requires a SPARQL FILTER */
+ ssmt_str, /* a string value that requires a SPARQL FILTER */
+ ssmt_fts, /* a string value that will be queried with SPARQL 'fts:match' */
+ ssmt_date, /* date values are handled in a special map function map_daterange() */
+ ssmt_type /* kMDItemContentType, requires special mapping */
+};
+
+struct sl_attr_map {
+ const char *spotlight_attr;
+ enum ssm_type type;
+ const char *sparql_attr;
+};
+
+enum kMDTypeMap {
+ kMDTypeMapNotSup, /* not supported */
+ kMDTypeMapRDF, /* query with rdf:type */
+ kMDTypeMapMime /* query with nie:mimeType */
+};
+
+struct sl_type_map {
+ /*
+ * MD query value of attributes '_kMDItemGroupId' and
+ * 'kMDItemContentTypeTree
+ */
+ const char *spotlight_type;
+
+ /*
+ * Whether SPARQL query must search attribute rdf:type or
+ * nie:mime_Type
+ */
+ enum kMDTypeMap type;
+
+ /* the SPARQL query match string */
+ const char *sparql_type;
+};
+
+const struct sl_attr_map *sl_attr_map_by_spotlight(const char *sl_attr);
+const struct sl_type_map *sl_type_map_by_spotlight(const char *sl_type);
+#endif
diff --git a/source3/rpc_server/mdssvc/sparql_parser.y b/source3/rpc_server/mdssvc/sparql_parser.y
new file mode 100644
index 0000000..68d4d87
--- /dev/null
+++ b/source3/rpc_server/mdssvc/sparql_parser.y
@@ -0,0 +1,483 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main metadata server / Spotlight routines
+
+ Copyright (C) Ralph Boehme 2012-2014
+
+ 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/mdssvc/mdssvc.h"
+ #include "rpc_server/mdssvc/mdssvc_tracker.h"
+ #include "rpc_server/mdssvc/sparql_parser.tab.h"
+ #include "rpc_server/mdssvc/sparql_mapping.h"
+
+ #define YYMALLOC SMB_MALLOC
+ #define YYREALLOC SMB_REALLOC
+
+ struct yy_buffer_state;
+ typedef struct yy_buffer_state *YY_BUFFER_STATE;
+ extern int mdsyylex (void);
+ extern void mdsyyerror (char const *);
+ extern void *mdsyyterminate(void);
+ extern YY_BUFFER_STATE mdsyy_scan_string( const char *str);
+ extern void mdsyy_delete_buffer ( YY_BUFFER_STATE buffer );
+
+ /* forward declarations */
+ static const char *map_expr(const char *attr, char op, const char *val);
+ static const char *map_daterange(const char *dateattr,
+ time_t date1, time_t date2);
+ static time_t isodate2unix(const char *s);
+
+ /* global vars, eg needed by the lexer */
+ struct sparql_parser_state {
+ TALLOC_CTX *frame;
+ YY_BUFFER_STATE s;
+ char var;
+ const char *result;
+ } *global_sparql_parser_state;
+%}
+
+%code provides {
+ #include <stdbool.h>
+ #include "rpc_server/mdssvc/mdssvc.h"
+ #define SPRAW_TIME_OFFSET 978307200
+ extern int mdsyywrap(void);
+ extern bool map_spotlight_to_sparql_query(struct sl_query *slq);
+}
+
+%union {
+ int ival;
+ const char *sval;
+ bool bval;
+ time_t tval;
+}
+
+%name-prefix "mdsyy"
+%expect 5
+%error-verbose
+
+%type <sval> match expr line function
+%type <tval> date
+
+%token <sval> WORD
+%token <bval> BOOL
+%token FUNC_INRANGE
+%token DATE_ISO
+%token OBRACE CBRACE EQUAL UNEQUAL GT LT COMMA QUOTE
+%left AND
+%left OR
+%%
+
+input:
+/* empty */
+| input line
+;
+
+line:
+expr {
+ global_sparql_parser_state->result = $1;
+}
+;
+
+expr:
+BOOL {
+ /*
+ * We can't properly handle these in expressions, fortunately this
+ * is probably only ever used by OS X as sole element in an
+ * expression ie "False" (when Finder window selected our share
+ * but no search string entered yet). Packet traces showed that OS
+ * X Spotlight server then returns a failure (ie -1) which is what
+ * we do here too by calling YYABORT.
+ */
+ YYABORT;
+}
+/*
+ * We have "match OR match" and "expr OR expr", because the former is
+ * supposed to catch and coalesque expressions of the form
+ *
+ * MDSattribute1="hello"||MDSattribute2="hello"
+ *
+ * into a single SPARQL expression for the case where both
+ * MDSattribute1 and MDSattribute2 map to the same SPARQL attribute,
+ * which is eg the case for "*" and "kMDItemTextContent" which both
+ * map to SPARQL "fts:match".
+ */
+
+| match OR match {
+ if (strcmp($1, $3) != 0) {
+ $$ = talloc_asprintf(talloc_tos(), "{ %s } UNION { %s }", $1, $3);
+ } else {
+ $$ = talloc_asprintf(talloc_tos(), "%s", $1);
+ }
+}
+| match {
+ $$ = $1;
+}
+| function {
+ $$ = $1;
+}
+| OBRACE expr CBRACE {
+ $$ = talloc_asprintf(talloc_tos(), "%s", $2);
+}
+| expr AND expr {
+ $$ = talloc_asprintf(talloc_tos(), "%s . %s", $1, $3);
+}
+| expr OR expr {
+ if (strcmp($1, $3) != 0) {
+ $$ = talloc_asprintf(talloc_tos(), "{ %s } UNION { %s }", $1, $3);
+ } else {
+ $$ = talloc_asprintf(talloc_tos(), "%s", $1);
+ }
+}
+;
+
+match:
+WORD EQUAL QUOTE WORD QUOTE {
+ $$ = map_expr($1, '=', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD UNEQUAL QUOTE WORD QUOTE {
+ $$ = map_expr($1, '!', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD LT QUOTE WORD QUOTE {
+ $$ = map_expr($1, '<', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD GT QUOTE WORD QUOTE {
+ $$ = map_expr($1, '>', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD EQUAL QUOTE WORD QUOTE WORD {
+ $$ = map_expr($1, '=', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD UNEQUAL QUOTE WORD QUOTE WORD {
+ $$ = map_expr($1, '!', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD LT QUOTE WORD QUOTE WORD {
+ $$ = map_expr($1, '<', $4);
+ if ($$ == NULL) YYABORT;
+}
+| WORD GT QUOTE WORD QUOTE WORD {
+ $$ = map_expr($1, '>', $4);
+ if ($$ == NULL) YYABORT;
+}
+;
+
+function:
+FUNC_INRANGE OBRACE WORD COMMA date COMMA date CBRACE {
+ $$ = map_daterange($3, $5, $7);
+ if ($$ == NULL) YYABORT;
+}
+;
+
+date:
+DATE_ISO OBRACE WORD CBRACE {$$ = isodate2unix($3);}
+| WORD {$$ = atoi($1) + SPRAW_TIME_OFFSET;}
+;
+
+%%
+
+static time_t isodate2unix(const char *s)
+{
+ struct tm tm = {};
+ const char *p;
+
+ p = strptime(s, "%Y-%m-%dT%H:%M:%SZ", &tm);
+ if (p == NULL) {
+ return (time_t)-1;
+ }
+ return mktime(&tm);
+}
+
+static const char *map_daterange(const char *dateattr,
+ time_t date1, time_t date2)
+{
+ struct sparql_parser_state *s = global_sparql_parser_state;
+ int result = 0;
+ char *sparql = NULL;
+ const struct sl_attr_map *p;
+ struct tm *tmp;
+ char buf1[64], buf2[64];
+
+ if (s->var == 'z') {
+ return NULL;
+ }
+
+ tmp = localtime(&date1);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ result = strftime(buf1, sizeof(buf1), "%Y-%m-%dT%H:%M:%SZ", tmp);
+ if (result == 0) {
+ return NULL;
+ }
+
+ tmp = localtime(&date2);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ result = strftime(buf2, sizeof(buf2), "%Y-%m-%dT%H:%M:%SZ", tmp);
+ if (result == 0) {
+ return NULL;
+ }
+
+ p = sl_attr_map_by_spotlight(dateattr);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ sparql = talloc_asprintf(talloc_tos(),
+ "?obj %s ?%c FILTER (?%c > '%s' && ?%c < '%s')",
+ p->sparql_attr,
+ s->var,
+ s->var,
+ buf1,
+ s->var,
+ buf2);
+ if (sparql == NULL) {
+ return NULL;
+ }
+
+ s->var++;
+ return sparql;
+}
+
+static char *map_type_search(const char *attr, char op, const char *val)
+{
+ char *result = NULL;
+ const char *sparqlAttr;
+ const struct sl_type_map *p;
+
+ p = sl_type_map_by_spotlight(val);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ switch (p->type) {
+ case kMDTypeMapRDF:
+ sparqlAttr = "rdf:type";
+ break;
+ case kMDTypeMapMime:
+ sparqlAttr = "nie:mimeType";
+ break;
+ default:
+ return NULL;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
+ sparqlAttr,
+ p->sparql_type);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ return result;
+}
+
+static const char *map_expr(const char *attr, char op, const char *val)
+{
+ struct sparql_parser_state *s = global_sparql_parser_state;
+ int result = 0;
+ char *sparql = NULL;
+ const struct sl_attr_map *p;
+ time_t t;
+ struct tm *tmp;
+ char buf1[64];
+ char *q;
+ const char *start;
+
+ if (s->var == 'z') {
+ return NULL;
+ }
+
+ p = sl_attr_map_by_spotlight(attr);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ if ((p->type != ssmt_type) && (p->sparql_attr == NULL)) {
+ yyerror("unsupported Spotlight attribute");
+ return NULL;
+ }
+
+ switch (p->type) {
+ case ssmt_bool:
+ sparql = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
+ p->sparql_attr, val);
+ if (sparql == NULL) {
+ return NULL;
+ }
+ break;
+
+ case ssmt_num:
+ sparql = talloc_asprintf(talloc_tos(),
+ "?obj %s ?%c FILTER(?%c %c%c '%s')",
+ p->sparql_attr,
+ s->var,
+ s->var,
+ op,
+ /* append '=' to '!' */
+ op == '!' ? '=' : ' ',
+ val);
+ if (sparql == NULL) {
+ return NULL;
+ }
+ s->var++;
+ break;
+
+ case ssmt_str:
+ q = talloc_strdup(talloc_tos(), "");
+ if (q == NULL) {
+ return NULL;
+ }
+ start = val;
+ while (*val) {
+ if (*val != '*') {
+ val++;
+ continue;
+ }
+ if (val > start) {
+ q = talloc_strndup_append(q, start, val - start);
+ if (q == NULL) {
+ return NULL;
+ }
+ }
+ q = talloc_strdup_append(q, ".*");
+ if (q == NULL) {
+ return NULL;
+ }
+ val++;
+ start = val;
+ }
+ if (val > start) {
+ q = talloc_strndup_append(q, start, val - start);
+ if (q == NULL) {
+ return NULL;
+ }
+ }
+ sparql = talloc_asprintf(talloc_tos(),
+ "?obj %s ?%c "
+ "FILTER(regex(?%c, '^%s$', 'i'))",
+ p->sparql_attr,
+ s->var,
+ s->var,
+ q);
+ TALLOC_FREE(q);
+ if (sparql == NULL) {
+ return NULL;
+ }
+ s->var++;
+ break;
+
+ case ssmt_fts:
+ sparql = talloc_asprintf(talloc_tos(), "?obj %s '%s'",
+ p->sparql_attr, val);
+ if (sparql == NULL) {
+ return NULL;
+ }
+ break;
+
+ case ssmt_date:
+ t = atoi(val) + SPRAW_TIME_OFFSET;
+ tmp = localtime(&t);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ result = strftime(buf1, sizeof(buf1),
+ "%Y-%m-%dT%H:%M:%SZ", tmp);
+ if (result == 0) {
+ return NULL;
+ }
+ sparql = talloc_asprintf(talloc_tos(),
+ "?obj %s ?%c FILTER(?%c %c '%s')",
+ p->sparql_attr,
+ s->var,
+ s->var,
+ op,
+ buf1);
+ if (sparql == NULL) {
+ return NULL;
+ }
+ s->var++;
+ break;
+
+ case ssmt_type:
+ sparql = map_type_search(attr, op, val);
+ if (sparql == NULL) {
+ return NULL;
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return sparql;
+}
+
+void mdsyyerror(const char *str)
+{
+ DEBUG(1, ("mdsyyerror: %s\n", str));
+}
+
+int mdsyywrap(void)
+{
+ return 1;
+}
+
+/**
+ * Map a Spotlight RAW query string to a SPARQL query string
+ **/
+bool map_spotlight_to_sparql_query(struct sl_query *slq)
+{
+ struct sl_tracker_query *tq = talloc_get_type_abort(
+ slq->backend_private, struct sl_tracker_query);
+ struct sparql_parser_state s = {
+ .frame = talloc_stackframe(),
+ .var = 'a',
+ };
+ int result;
+
+ s.s = mdsyy_scan_string(slq->query_string);
+ if (s.s == NULL) {
+ TALLOC_FREE(s.frame);
+ return false;
+ }
+ global_sparql_parser_state = &s;
+ result = mdsyyparse();
+ global_sparql_parser_state = NULL;
+ mdsyy_delete_buffer(s.s);
+
+ if (result != 0) {
+ TALLOC_FREE(s.frame);
+ return false;
+ }
+
+ tq->sparql_query = talloc_asprintf(slq,
+ "SELECT ?url WHERE { %s . ?obj nie:url ?url . "
+ "FILTER(tracker:uri-is-descendant('file://%s/', ?url)) }",
+ s.result, tq->path_scope);
+ TALLOC_FREE(s.frame);
+ if (tq->sparql_query == NULL) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/rpc_server/mdssvc/sparql_parser_test.c b/source3/rpc_server/mdssvc/sparql_parser_test.c
new file mode 100644
index 0000000..0a0f625
--- /dev/null
+++ b/source3/rpc_server/mdssvc/sparql_parser_test.c
@@ -0,0 +1,47 @@
+#include "includes.h"
+#include "mdssvc.h"
+#include "rpc_server/mdssvc/sparql_parser.tab.h"
+#include "rpc_server/mdssvc/mdssvc_tracker.h"
+
+/*
+ * Examples:
+ *
+ * $ ./spotlight2sparql '_kMDItemGroupId=="11"'
+ * ...
+ * $ ./spotlight2sparql '*=="test*"cwd||kMDItemTextContent=="test*"cwd'
+ * ...
+ */
+
+int main(int argc, char **argv)
+{
+ struct sl_tracker_query *tq = NULL;
+ bool ok;
+ struct sl_query *slq;
+
+ if (argc != 2) {
+ printf("usage: %s QUERY\n", argv[0]);
+ return 1;
+ }
+
+ slq = talloc_zero(NULL, struct sl_query);
+ if (slq == NULL) {
+ printf("talloc error\n");
+ return 1;
+ }
+
+ slq->query_string = argv[1];
+ slq->path_scope = "/foo/bar";
+
+ tq = talloc_zero(slq, struct sl_tracker_query);
+ if (tq == NULL) {
+ printf("talloc error\n");
+ return 1;
+ }
+ slq->backend_private = tq;
+
+ ok = map_spotlight_to_sparql_query(slq);
+ printf("%s\n", ok ? tq->sparql_query : "*mapping failed*");
+
+ talloc_free(slq);
+ return ok ? 0 : 1;
+}
diff --git a/source3/rpc_server/mdssvc/srv_mdssvc_nt.c b/source3/rpc_server/mdssvc/srv_mdssvc_nt.c
new file mode 100644
index 0000000..9a16624
--- /dev/null
+++ b/source3/rpc_server/mdssvc/srv_mdssvc_nt.c
@@ -0,0 +1,319 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines for mdssvc
+ * Copyright (C) Ralph Boehme 2014
+ *
+ * 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 "messages.h"
+#include "ntdomain.h"
+#include "rpc_server/rpc_server.h"
+#include "rpc_server/rpc_config.h"
+#include "rpc_server/mdssvc/srv_mdssvc_nt.h"
+#include "libcli/security/security_token.h"
+#include "libcli/security/dom_sid.h"
+#include "gen_ndr/auth.h"
+#include "mdssvc.h"
+#include "smbd/globals.h"
+
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_mdssvc.h"
+#include "librpc/gen_ndr/ndr_mdssvc_scompat.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static NTSTATUS create_mdssvc_policy_handle(TALLOC_CTX *mem_ctx,
+ struct pipes_struct *p,
+ int snum,
+ const char *sharename,
+ const char *path,
+ struct policy_handle *handle)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct mds_ctx *mds_ctx;
+ NTSTATUS status;
+
+ ZERO_STRUCTP(handle);
+
+ status = mds_init_ctx(mem_ctx,
+ messaging_tevent_context(p->msg_ctx),
+ p->msg_ctx,
+ session_info,
+ snum,
+ sharename,
+ path,
+ &mds_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("mds_init_ctx() path [%s] failed: %s\n",
+ path, nt_errstr(status));
+ return status;
+ }
+
+ if (!create_policy_hnd(p, handle, 0, mds_ctx)) {
+ talloc_free(mds_ctx);
+ ZERO_STRUCTP(handle);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+void _mdssvc_open(struct pipes_struct *p, struct mdssvc_open *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int snum;
+ char *outpath = discard_const_p(char, r->out.share_path);
+ char *fake_path = NULL;
+ char *path;
+ NTSTATUS status;
+
+ DBG_DEBUG("[%s]\n", r->in.share_name);
+
+ *r->out.device_id = *r->in.device_id;
+ *r->out.unkn2 = *r->in.unkn2;
+ *r->out.unkn3 = *r->in.unkn3;
+ outpath[0] = '\0';
+
+ snum = lp_servicenumber(r->in.share_name);
+ if (!VALID_SNUM(snum)) {
+ return;
+ }
+
+ path = lp_path(talloc_tos(), lp_sub, snum);
+ if (path == NULL) {
+ DBG_ERR("Couldn't create path for %s\n",
+ r->in.share_name);
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ fake_path = talloc_asprintf(p->mem_ctx, "/%s", r->in.share_name);
+ if (fake_path == NULL) {
+ DBG_ERR("Couldn't create fake share path for %s\n",
+ r->in.share_name);
+ talloc_free(path);
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ status = create_mdssvc_policy_handle(p->mem_ctx, p,
+ snum,
+ r->in.share_name,
+ path,
+ r->out.handle);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_VOLUME)) {
+ ZERO_STRUCTP(r->out.handle);
+ talloc_free(path);
+ talloc_free(fake_path);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Couldn't create policy handle for %s\n",
+ r->in.share_name);
+ talloc_free(path);
+ talloc_free(fake_path);
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ strlcpy(outpath, fake_path, 1024);
+ talloc_free(path);
+ talloc_free(fake_path);
+ return;
+}
+
+void _mdssvc_unknown1(struct pipes_struct *p, struct mdssvc_unknown1 *r)
+{
+ struct mds_ctx *mds_ctx;
+ NTSTATUS status;
+
+ mds_ctx = find_policy_by_hnd(p,
+ r->in.handle,
+ DCESRV_HANDLE_ANY,
+ struct mds_ctx,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (ndr_policy_handle_empty(r->in.handle)) {
+ p->fault_state = 0;
+ } else {
+ p->fault_state = DCERPC_NCA_S_PROTO_ERROR;
+ }
+ *r->out.status = 0;
+ *r->out.flags = 0;
+ *r->out.unkn7 = 0;
+ return;
+ }
+
+ DEBUG(10, ("%s: path: %s\n", __func__, mds_ctx->spath));
+
+ *r->out.status = 0;
+ *r->out.flags = 0x6b000001;
+ *r->out.unkn7 = 0;
+
+ return;
+}
+
+void _mdssvc_cmd(struct pipes_struct *p, struct mdssvc_cmd *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ bool ok;
+ struct mds_ctx *mds_ctx;
+ NTSTATUS status;
+
+ mds_ctx = find_policy_by_hnd(p,
+ r->in.handle,
+ DCESRV_HANDLE_ANY,
+ struct mds_ctx,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (ndr_policy_handle_empty(r->in.handle)) {
+ p->fault_state = 0;
+ } else {
+ p->fault_state = DCERPC_NCA_S_PROTO_ERROR;
+ }
+ r->out.response_blob->size = 0;
+ *r->out.fragment = 0;
+ *r->out.unkn9 = 0;
+ return;
+ }
+
+ DEBUG(10, ("%s: path: %s\n", __func__, mds_ctx->spath));
+
+ ok = security_token_is_sid(session_info->security_token,
+ &mds_ctx->sid);
+ if (!ok) {
+ struct dom_sid_buf buf;
+ DBG_WARNING("not the same sid: %s\n",
+ dom_sid_str_buf(&mds_ctx->sid, &buf));
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return;
+ }
+
+ if (geteuid() != mds_ctx->uid) {
+ DEBUG(0, ("uid mismatch: %d/%d\n", geteuid(), mds_ctx->uid));
+ smb_panic("uid mismatch");
+ }
+
+ if (r->in.request_blob.size > MAX_SL_FRAGMENT_SIZE) {
+ DEBUG(1, ("%s: request size too large\n", __func__));
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ if (r->in.request_blob.length > MAX_SL_FRAGMENT_SIZE) {
+ DEBUG(1, ("%s: request length too large\n", __func__));
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ if (r->in.max_fragment_size1 > MAX_SL_FRAGMENT_SIZE) {
+ DEBUG(1, ("%s: request fragment size too large: %u\n",
+ __func__, (unsigned)r->in.max_fragment_size1));
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return;
+ }
+
+ /* We currently don't use fragmentation at the mdssvc RPC layer */
+ *r->out.fragment = 0;
+
+ ok = mds_dispatch(mds_ctx,
+ &r->in.request_blob,
+ r->out.response_blob,
+ r->in.max_fragment_size1);
+ if (ok) {
+ *r->out.unkn9 = 0;
+ } else {
+ /* FIXME: just interpolating from AFP, needs verification */
+ *r->out.unkn9 = UINT32_MAX;
+ }
+
+ return;
+}
+
+void _mdssvc_close(struct pipes_struct *p, struct mdssvc_close *r)
+{
+ struct mds_ctx *mds_ctx;
+ NTSTATUS status;
+
+ mds_ctx = find_policy_by_hnd(p,
+ r->in.in_handle,
+ DCESRV_HANDLE_ANY,
+ struct mds_ctx,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("invalid handle\n");
+ if (ndr_policy_handle_empty(r->in.in_handle)) {
+ p->fault_state = 0;
+ } else {
+ p->fault_state = DCERPC_NCA_S_PROTO_ERROR;
+ }
+ return;
+ }
+
+ DBG_DEBUG("Close mdssvc handle for path: %s\n", mds_ctx->spath);
+ TALLOC_FREE(mds_ctx);
+
+ *r->out.out_handle = *r->in.in_handle;
+ close_policy_hnd(p, r->in.in_handle);
+
+ *r->out.status = 0;
+
+ return;
+}
+
+static NTSTATUS mdssvc__op_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+static NTSTATUS mdssvc__op_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+#define DCESRV_INTERFACE_MDSSVC_INIT_SERVER \
+ mdssvc_init_server
+
+#define DCESRV_INTERFACE_MDSSVC_SHUTDOWN_SERVER \
+ mdssvc_shutdown_server
+
+static NTSTATUS mdssvc_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ bool ok;
+
+ ok = mds_init(msg_ctx);
+ if (!ok) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return mdssvc__op_init_server(dce_ctx, ep_server);
+}
+
+static NTSTATUS mdssvc_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ mds_shutdown();
+
+ return mdssvc__op_shutdown_server(dce_ctx, ep_server);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_mdssvc_scompat.c"
diff --git a/source3/rpc_server/mdssvc/srv_mdssvc_nt.h b/source3/rpc_server/mdssvc/srv_mdssvc_nt.h
new file mode 100644
index 0000000..8b78f5e
--- /dev/null
+++ b/source3/rpc_server/mdssvc/srv_mdssvc_nt.h
@@ -0,0 +1,27 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * MDSSVC RPC pipe initialisation routines
+ *
+ * Copyright (C) Ralph Boehme 2014
+ *
+ * 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 _SRV_MDSSVC_NT_H
+#define _SRV_MDSSVC_NT_H
+
+bool init_service_mdssvc(struct messaging_context *msg_ctx);
+bool shutdown_service_mdssvc(void);
+
+#endif /* _SRV_MDSSVC_NT_H */
diff --git a/source3/rpc_server/mdssvc/test_mdsparser_es.c b/source3/rpc_server/mdssvc/test_mdsparser_es.c
new file mode 100644
index 0000000..02270a9
--- /dev/null
+++ b/source3/rpc_server/mdssvc/test_mdsparser_es.c
@@ -0,0 +1,304 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Ralph Boehme 2019
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include <setjmp.h>
+#include <cmocka.h>
+#include <jansson.h>
+#include <talloc.h>
+#include "lib/cmdline/cmdline.h"
+#include "libcli/util/ntstatus.h"
+#include "lib/util/samba_util.h"
+#include "lib/torture/torture.h"
+#include "lib/param/param.h"
+#include "rpc_server/mdssvc/es_parser.tab.h"
+
+#define PATH_QUERY_SUBEXPR \
+ " AND path.real.fulltext:\\\"/foo/bar\\\""
+
+static struct {
+ const char *mds;
+ const char *es;
+} map[] = {
+ {
+ "*==\"samba\"",
+ "(samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemTextContent==\"samba\"",
+ "(content:samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "_kMDItemGroupId==\"11\"",
+ "(file.content_type:(application\\\\/pdf))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemContentType==\"1\"",
+ "(file.content_type:(message\\\\/rfc822))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemContentType==\"public.content\"",
+ "(file.content_type:(message\\\\/rfc822 application\\\\/pdf application\\\\/vnd.oasis.opendocument.presentation image\\\\/* text\\\\/*))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemContentTypeTree==\"1\"",
+ "(file.content_type:(message\\\\/rfc822))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSContentChangeDate==$time.iso(2018-10-01T10:00:00Z)",
+ "(file.last_modified:2018\\\\-10\\\\-01T10\\\\:00\\\\:00Z)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSContentChangeDate==\"1\"",
+ "(file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSCreationDate==\"1\"",
+ "(file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSName==\"samba*\"",
+ "(file.filename:samba*)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSOwnerGroupID==\"0\"",
+ "(attributes.owner:0)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSOwnerUserID==\"0\"",
+ "(attributes.group:0)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSSize==\"1\"",
+ "(file.filesize:1)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemPath==\"/foo/bar\"",
+ "(path.real:\\\\/foo\\\\/bar)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemAttributeChangeDate==\"1\"",
+ "(file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemAuthors==\"Chouka\"",
+ "(meta.author:Chouka)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemContentCreationDate==\"1\"",
+ "(file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemContentModificationDate==\"1\"",
+ "(file.last_modified:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemCreator==\"Chouka\"",
+ "(meta.raw.creator:Chouka)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemDescription==\"Dog\"",
+ "(meta.raw.description:Dog)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemDisplayName==\"Samba\"",
+ "(file.filename:Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemDurationSeconds==\"1\"",
+ "(meta.raw.xmpDM\\\\:duration:1)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemNumberOfPages==\"1\"",
+ "(meta.raw.xmpTPg\\\\:NPages:1)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemTitle==\"Samba\"",
+ "(meta.title:Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemAlbum==\"Red Roses for Me\"",
+ "(meta.raw.xmpDM\\\\:album:Red\\\\ Roses\\\\ for\\\\ Me)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemBitsPerSample==\"1\"",
+ "(meta.raw.tiff\\\\:BitsPerSample:1)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemPixelHeight==\"1\"",
+ "(meta.raw.Image\\\\ Height:1)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemPixelWidth==\"1\"",
+ "(meta.raw.Image\\\\ Width:1)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemResolutionHeightDPI==\"72\"",
+ "(meta.raw.Y\\\\ Resolution:72)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemResolutionWidthDPI==\"72\"",
+ "(meta.raw.X\\\\ Resolution:72)" PATH_QUERY_SUBEXPR
+ },{
+ "*!=\"samba\"",
+ "((NOT samba))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSSize!=\"1\"",
+ "((NOT file.filesize:1))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSSize>\"1\"",
+ "(file.filesize:{1 TO *})" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSSize<\"1\"",
+ "(file.filesize:{* TO 1})" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSCreationDate!=\"1\"",
+ "((NOT file.created:2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z))" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSCreationDate>\"1\"",
+ "(file.created:{2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z TO *})" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSCreationDate<\"1\"",
+ "(file.created:{* TO 2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z})" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSName==\"Samba\"||kMDItemTextContent==\"Samba\"",
+ "(file.filename:Samba OR content:Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "kMDItemFSName==\"Samba\"&&kMDItemTextContent==\"Samba\"",
+ "((file.filename:Samba) AND (content:Samba))" PATH_QUERY_SUBEXPR
+ }, {
+ "InRange(kMDItemFSCreationDate,1,2)",
+ "(file.created:[2001\\\\-01\\\\-01T00\\\\:00\\\\:01Z TO 2001\\\\-01\\\\-01T00\\\\:00\\\\:02Z])" PATH_QUERY_SUBEXPR
+ }, {
+ "InRange(kMDItemFSSize,1,2)",
+ "(file.filesize:[1 TO 2])" PATH_QUERY_SUBEXPR
+ }
+};
+
+static struct {
+ const char *mds;
+ const char *es;
+} map_ignore_failures[] = {
+ {
+ "*==\"Samba\"||foo==\"bar\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"&&foo==\"bar\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"||kMDItemContentType==\"666\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"&&kMDItemContentType==\"666\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"||foo==\"bar\"||kMDItemContentType==\"666\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"&&foo==\"bar\"&&kMDItemContentType==\"666\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "foo==\"bar\"||kMDItemContentType==\"666\"||*==\"Samba\"||x!=\"6\"",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"||InRange(foo,1,2)",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }, {
+ "*==\"Samba\"||foo==$time.iso(2018-10-01T10:00:00Z)",
+ "(Samba)" PATH_QUERY_SUBEXPR
+ }
+};
+
+static void test_mdsparser_es(void **state)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *path_scope = "/foo/bar";
+ char *es_query = NULL;
+ const char *path = NULL;
+ json_t *mappings = NULL;
+ json_error_t json_error;
+ int i;
+ bool ok;
+
+ path = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+ "elasticsearch",
+ "mappings",
+ NULL);
+ assert_non_null(path);
+
+ mappings = json_load_file(path, 0, &json_error);
+ assert_non_null(mappings);
+
+ for (i = 0; i < ARRAY_SIZE(map); i++) {
+ DBG_DEBUG("Mapping: %s\n", map[i].mds);
+ ok = map_spotlight_to_es_query(frame,
+ mappings,
+ path_scope,
+ map[i].mds,
+ &es_query);
+ assert_true(ok);
+ assert_string_equal(es_query, map[i].es);
+ }
+
+ if (!lp_parm_bool(GLOBAL_SECTION_SNUM,
+ "elasticsearch",
+ "test mapping failures",
+ false))
+ {
+ goto done;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(map_ignore_failures); i++) {
+ DBG_DEBUG("Mapping: %s\n", map_ignore_failures[i].mds);
+ ok = map_spotlight_to_es_query(frame,
+ mappings,
+ path_scope,
+ map_ignore_failures[i].mds,
+ &es_query);
+ assert_true(ok);
+ assert_string_equal(es_query, map_ignore_failures[i].es);
+ }
+
+done:
+ json_decref(mappings);
+ TALLOC_FREE(frame);
+}
+
+int main(int argc, const char *argv[])
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_mdsparser_es),
+ };
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ poptContext pc;
+ int opt;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "1");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "Unknown Option: %c\n", opt);
+ exit(1);
+ }
+ }
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c
new file mode 100644
index 0000000..fa3e597
--- /dev/null
+++ b/source3/rpc_server/netlogon/srv_netlog_nt.c
@@ -0,0 +1,2937 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997.
+ * Copyright (C) Jeremy Allison 1998-2001.
+ * Copyright (C) Andrew Bartlett 2001.
+ * Copyright (C) Guenther Deschner 2008-2009.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This is the implementation of the netlogon pipe. */
+
+#include "includes.h"
+#include "system/passwd.h" /* uid_wrapper */
+#include "ntdomain.h"
+#include "../libcli/auth/schannel.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_scompat.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "rpc_client/init_lsa.h"
+#include "rpc_client/init_samr.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "../libcli/security/security.h"
+#include "../libcli/security/dom_sid.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "lib/crypto/md4.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "../libcli/registry/util_reg.h"
+#include "passdb.h"
+#include "auth.h"
+#include "messages.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/param/param.h"
+#include "libsmb/dsgetdcname.h"
+#include "lib/util/util_str_escape.h"
+#include "source3/lib/substitute.h"
+#include "librpc/rpc/server/netlogon/schannel_util.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ _netr_LogonControl
+ *************************************************************************/
+
+WERROR _netr_LogonControl(struct pipes_struct *p,
+ struct netr_LogonControl *r)
+{
+ struct netr_LogonControl2Ex l;
+
+ switch (r->in.level) {
+ case 1:
+ break;
+ case 2:
+ return WERR_NOT_SUPPORTED;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ switch (r->in.function_code) {
+ case NETLOGON_CONTROL_QUERY:
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_BREAKPOINT:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ break;
+ default:
+ return WERR_NOT_SUPPORTED;
+ }
+
+ l.in.logon_server = r->in.logon_server;
+ l.in.function_code = r->in.function_code;
+ l.in.level = r->in.level;
+ l.in.data = NULL;
+ l.out.query = r->out.query;
+
+ return _netr_LogonControl2Ex(p, &l);
+}
+
+/*************************************************************************
+ _netr_LogonControl2
+ *************************************************************************/
+
+WERROR _netr_LogonControl2(struct pipes_struct *p,
+ struct netr_LogonControl2 *r)
+{
+ struct netr_LogonControl2Ex l;
+
+ l.in.logon_server = r->in.logon_server;
+ l.in.function_code = r->in.function_code;
+ l.in.level = r->in.level;
+ l.in.data = r->in.data;
+ l.out.query = r->out.query;
+
+ return _netr_LogonControl2Ex(p, &l);
+}
+
+/*************************************************************************
+ *************************************************************************/
+
+static bool wb_change_trust_creds(const char *domain, WERROR *tc_status)
+{
+ wbcErr result;
+ struct wbcAuthErrorInfo *error = NULL;
+
+ result = wbcChangeTrustCredentials(domain, &error);
+ switch (result) {
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return false;
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ *tc_status = WERR_NO_SUCH_DOMAIN;
+ return true;
+ case WBC_ERR_SUCCESS:
+ *tc_status = WERR_OK;
+ return true;
+ default:
+ break;
+ }
+
+ if (error && error->nt_status != 0) {
+ *tc_status = ntstatus_to_werror(NT_STATUS(error->nt_status));
+ } else {
+ *tc_status = WERR_TRUST_FAILURE;
+ }
+ wbcFreeMemory(error);
+ return true;
+}
+
+/*************************************************************************
+ *************************************************************************/
+
+static bool wb_check_trust_creds(const char *domain, WERROR *tc_status)
+{
+ wbcErr result;
+ struct wbcAuthErrorInfo *error = NULL;
+
+ result = wbcCheckTrustCredentials(domain, &error);
+ switch (result) {
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return false;
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ *tc_status = WERR_NO_SUCH_DOMAIN;
+ return true;
+ case WBC_ERR_SUCCESS:
+ *tc_status = WERR_OK;
+ return true;
+ default:
+ break;
+ }
+
+ if (error && error->nt_status != 0) {
+ *tc_status = ntstatus_to_werror(NT_STATUS(error->nt_status));
+ } else {
+ *tc_status = WERR_TRUST_FAILURE;
+ }
+ wbcFreeMemory(error);
+ return true;
+}
+
+/****************************************************************
+ _netr_LogonControl2Ex
+****************************************************************/
+
+WERROR _netr_LogonControl2Ex(struct pipes_struct *p,
+ struct netr_LogonControl2Ex *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ uint32_t flags = 0x0;
+ WERROR pdc_connection_status = WERR_OK;
+ uint32_t logon_attempts = 0x0;
+ WERROR tc_status;
+ fstring dc_name2;
+ const char *dc_name = NULL;
+ struct sockaddr_storage dc_ss;
+ const char *domain = NULL;
+ struct netr_NETLOGON_INFO_1 *info1;
+ struct netr_NETLOGON_INFO_2 *info2;
+ struct netr_NETLOGON_INFO_3 *info3;
+ struct netr_NETLOGON_INFO_4 *info4;
+ const char *fn;
+ NTSTATUS status;
+ struct netr_DsRGetDCNameInfo *dc_info;
+
+ switch (dce_call->pkt.u.request.opnum) {
+ case NDR_NETR_LOGONCONTROL:
+ fn = "_netr_LogonControl";
+ break;
+ case NDR_NETR_LOGONCONTROL2:
+ fn = "_netr_LogonControl2";
+ break;
+ case NDR_NETR_LOGONCONTROL2EX:
+ fn = "_netr_LogonControl2Ex";
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ switch (r->in.function_code) {
+ case NETLOGON_CONTROL_QUERY:
+ break;
+ default:
+ if ((geteuid() != sec_initial_uid()) &&
+ !nt_token_check_domain_rid(
+ session_info->security_token, DOMAIN_RID_ADMINS) &&
+ !nt_token_check_sid(
+ &global_sid_Builtin_Administrators,
+ session_info->security_token))
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ break;
+ }
+
+ tc_status = WERR_NO_SUCH_DOMAIN;
+
+ switch (r->in.function_code) {
+ case NETLOGON_CONTROL_QUERY:
+ switch (r->in.level) {
+ case 1:
+ case 3:
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ tc_status = WERR_OK;
+ break;
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ case NETLOGON_CONTROL_BREAKPOINT:
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ case NETLOGON_CONTROL_TRANSPORT_NOTIFY:
+ case NETLOGON_CONTROL_FORCE_DNS_REG:
+ case NETLOGON_CONTROL_QUERY_DNS_REG:
+ return WERR_NOT_SUPPORTED;
+
+ case NETLOGON_CONTROL_FIND_USER:
+ if (!r->in.data || !r->in.data->user) {
+ return WERR_NOT_SUPPORTED;
+ }
+ break;
+ case NETLOGON_CONTROL_SET_DBFLAG:
+ if (!r->in.data) {
+ return WERR_NOT_SUPPORTED;
+ }
+ break;
+ case NETLOGON_CONTROL_TC_VERIFY:
+ if (!r->in.data || !r->in.data->domain) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ if (!wb_check_trust_creds(r->in.data->domain, &tc_status)) {
+ return WERR_NOT_SUPPORTED;
+ }
+ break;
+ case NETLOGON_CONTROL_TC_QUERY:
+ if (!r->in.data || !r->in.data->domain) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ domain = r->in.data->domain;
+
+ if (!is_trusted_domain(domain)) {
+ break;
+ }
+
+ if (!get_dc_name(domain, NULL, dc_name2, &dc_ss)) {
+ tc_status = WERR_NO_LOGON_SERVERS;
+ break;
+ }
+
+ dc_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dc_name2);
+ if (!dc_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ tc_status = WERR_OK;
+
+ break;
+
+ case NETLOGON_CONTROL_REDISCOVER:
+ if (!r->in.data || !r->in.data->domain) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ domain = r->in.data->domain;
+
+ if (!is_trusted_domain(domain)) {
+ break;
+ }
+
+ status = dsgetdcname(p->mem_ctx, p->msg_ctx, domain, NULL, NULL,
+ DS_FORCE_REDISCOVERY | DS_RETURN_FLAT_NAME,
+ &dc_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ tc_status = WERR_NO_LOGON_SERVERS;
+ break;
+ }
+
+ dc_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dc_info->dc_unc);
+ if (!dc_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ tc_status = WERR_OK;
+
+ break;
+
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ if (!r->in.data || !r->in.data->domain) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ if (!wb_change_trust_creds(r->in.data->domain, &tc_status)) {
+ return WERR_NOT_SUPPORTED;
+ }
+ break;
+
+ default:
+ /* no idea what this should be */
+ DEBUG(0,("%s: unimplemented function level [%d]\n",
+ fn, r->in.function_code));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ /* prepare the response */
+
+ switch (r->in.level) {
+ case 1:
+ info1 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_1);
+ W_ERROR_HAVE_NO_MEMORY(info1);
+
+ info1->flags = flags;
+ info1->pdc_connection_status = pdc_connection_status;
+
+ r->out.query->info1 = info1;
+ break;
+ case 2:
+ info2 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_2);
+ W_ERROR_HAVE_NO_MEMORY(info2);
+
+ info2->flags = flags;
+ info2->pdc_connection_status = pdc_connection_status;
+ info2->trusted_dc_name = dc_name;
+ info2->tc_connection_status = tc_status;
+
+ r->out.query->info2 = info2;
+ break;
+ case 3:
+ info3 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_3);
+ W_ERROR_HAVE_NO_MEMORY(info3);
+
+ info3->flags = flags;
+ info3->logon_attempts = logon_attempts;
+
+ r->out.query->info3 = info3;
+ break;
+ case 4:
+ info4 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_4);
+ W_ERROR_HAVE_NO_MEMORY(info4);
+
+ info4->trusted_dc_name = dc_name;
+ info4->trusted_domain_name = r->in.data->domain;
+
+ r->out.query->info4 = info4;
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/*************************************************************************
+ _netr_NetrEnumerateTrustedDomains
+ *************************************************************************/
+
+NTSTATUS _netr_NetrEnumerateTrustedDomains(struct pipes_struct *p,
+ struct netr_NetrEnumerateTrustedDomains *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_OK;
+ DATA_BLOB blob;
+ size_t num_domains = 0;
+ const char **trusted_domains = NULL;
+ struct lsa_DomainList domain_list;
+ struct dcerpc_binding_handle *h = NULL;
+ struct policy_handle pol;
+ uint32_t enum_ctx = 0;
+ uint32_t max_size = (uint32_t)-1;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ ZERO_STRUCT(pol);
+ DEBUG(6,("_netr_NetrEnumerateTrustedDomains: %d\n", __LINE__));
+
+ status = rpcint_binding_handle(p->mem_ctx,
+ &ndr_table_lsarpc,
+ remote_address,
+ local_address,
+ session_info,
+ p->msg_ctx,
+ &h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_lsa_open_policy_fallback(
+ h,
+ p->mem_ctx,
+ NULL,
+ true,
+ LSA_POLICY_VIEW_LOCAL_INFORMATION,
+ &out_version,
+ &out_revision_info,
+ &pol,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ do {
+ uint32_t i;
+
+ /* Lookup list of trusted domains */
+ status = dcerpc_lsa_EnumTrustDom(h,
+ p->mem_ctx,
+ &pol,
+ &enum_ctx,
+ &domain_list,
+ max_size,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_NO_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+ status = result;
+ goto out;
+ }
+
+ for (i = 0; i < domain_list.count; i++) {
+ if (!add_string_to_array(p->mem_ctx, domain_list.domains[i].name.string,
+ &trusted_domains, &num_domains)) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ if (num_domains > 0) {
+ /* multi sz terminate */
+ trusted_domains = talloc_realloc(p->mem_ctx, trusted_domains, const char *, num_domains + 1);
+ if (trusted_domains == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ trusted_domains[num_domains] = NULL;
+ }
+
+ if (!push_reg_multi_sz(trusted_domains, &blob, trusted_domains)) {
+ TALLOC_FREE(trusted_domains);
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ r->out.trusted_domains_blob->data = blob.data;
+ r->out.trusted_domains_blob->length = blob.length;
+
+ DEBUG(6,("_netr_NetrEnumerateTrustedDomains: %d\n", __LINE__));
+
+ status = NT_STATUS_OK;
+
+ out:
+ if (is_valid_policy_hnd(&pol)) {
+ dcerpc_lsa_Close(h, p->mem_ctx, &pol, &result);
+ }
+
+ return status;
+}
+
+/*************************************************************************
+ *************************************************************************/
+
+static NTSTATUS samr_find_machine_account(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *account_name,
+ uint32_t access_mask,
+ struct dom_sid2 **domain_sid_p,
+ uint32_t *user_rid_p,
+ struct policy_handle *user_handle)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_OK;
+ struct policy_handle connect_handle;
+ struct policy_handle domain_handle = { 0, };
+ struct lsa_String domain_name;
+ struct dom_sid2 *domain_sid;
+ struct lsa_String names;
+ struct samr_Ids rids;
+ struct samr_Ids types;
+ uint32_t rid;
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ lp_netbios_name(),
+ SAMR_ACCESS_CONNECT_TO_SERVER |
+ SAMR_ACCESS_ENUM_DOMAINS |
+ SAMR_ACCESS_LOOKUP_DOMAIN,
+ &connect_handle,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ init_lsa_String(&domain_name, get_global_sam_name());
+
+ status = dcerpc_samr_LookupDomain(b, mem_ctx,
+ &connect_handle,
+ &domain_name,
+ &domain_sid,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_handle,
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ domain_sid,
+ &domain_handle,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ init_lsa_String(&names, account_name);
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_handle,
+ 1,
+ &names,
+ &rids,
+ &types,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ if (rids.count != 1) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto out;
+ }
+ if (types.count != 1) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+ if (types.ids[0] != SID_NAME_USER) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto out;
+ }
+
+ rid = rids.ids[0];
+
+ status = dcerpc_samr_OpenUser(b, mem_ctx,
+ &domain_handle,
+ access_mask,
+ rid,
+ user_handle,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ if (user_rid_p) {
+ *user_rid_p = rid;
+ }
+
+ if (domain_sid_p) {
+ *domain_sid_p = domain_sid;
+ }
+
+ out:
+ if (is_valid_policy_hnd(&domain_handle)) {
+ dcerpc_samr_Close(b, mem_ctx, &domain_handle, &result);
+ }
+ if (is_valid_policy_hnd(&connect_handle)) {
+ dcerpc_samr_Close(b, mem_ctx, &connect_handle, &result);
+ }
+
+ return status;
+}
+
+/******************************************************************
+ gets a machine password entry. checks access rights of the host.
+ ******************************************************************/
+
+static NTSTATUS get_md4pw(struct samr_Password *md4pw, const char *mach_acct,
+ enum netr_SchannelType sec_chan_type,
+ struct dom_sid *sid,
+ struct messaging_context *msg_ctx)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_OK;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct dcerpc_binding_handle *h = NULL;
+ struct tsocket_address *local = NULL;
+ struct policy_handle user_handle = { .handle_type = 0 };
+ uint32_t user_rid = UINT32_MAX;
+ struct dom_sid *domain_sid = NULL;
+ uint32_t acct_ctrl = 0;
+ union samr_UserInfo *info = NULL;
+ struct auth_session_info *session_info = NULL;
+ int rc;
+
+#if 0
+
+ /*
+ * Currently this code is redundant as we already have a filter
+ * by hostname list. What this code really needs to do is to
+ * get a hosts allowed/hosts denied list from the SAM database
+ * on a per user basis, and make the access decision there.
+ * I will leave this code here for now as a reminder to implement
+ * this at a later date. JRA.
+ */
+
+ if (!allow_access(lp_domain_hostsdeny(), lp_domain_hostsallow(),
+ p->client_id.name,
+ p->client_id.addr)) {
+ DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct));
+ return False;
+ }
+#endif /* 0 */
+
+ mem_ctx = talloc_stackframe();
+
+ status = make_session_info_system(mem_ctx, &session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ ZERO_STRUCT(user_handle);
+
+ rc = tsocket_address_inet_from_strings(mem_ctx,
+ "ip",
+ "127.0.0.1",
+ 0,
+ &local);
+ if (rc < 0) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ status = rpcint_binding_handle(mem_ctx,
+ &ndr_table_samr,
+ local,
+ NULL,
+ session_info,
+ msg_ctx,
+ &h);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = samr_find_machine_account(mem_ctx, h, mach_acct,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &domain_sid, &user_rid,
+ &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcerpc_samr_QueryUserInfo2(h,
+ mem_ctx,
+ &user_handle,
+ UserControlInformation,
+ &info,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ acct_ctrl = info->info16.acct_flags;
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("get_md4pw: Workstation %s: account is disabled\n", mach_acct));
+ status = NT_STATUS_ACCOUNT_DISABLED;
+ goto out;
+ }
+
+ if (!(acct_ctrl & ACB_SVRTRUST) &&
+ !(acct_ctrl & ACB_WSTRUST) &&
+ !(acct_ctrl & ACB_DOMTRUST))
+ {
+ DEBUG(0,("get_md4pw: Workstation %s: account is not a trust account\n", mach_acct));
+ status = NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ goto out;
+ }
+
+ switch (sec_chan_type) {
+ case SEC_CHAN_BDC:
+ if (!(acct_ctrl & ACB_SVRTRUST)) {
+ DEBUG(0,("get_md4pw: Workstation %s: BDC secure channel requested "
+ "but not a server trust account\n", mach_acct));
+ status = NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ goto out;
+ }
+ break;
+ case SEC_CHAN_WKSTA:
+ if (!(acct_ctrl & ACB_WSTRUST)) {
+ DEBUG(0,("get_md4pw: Workstation %s: WORKSTATION secure channel requested "
+ "but not a workstation trust account\n", mach_acct));
+ status = NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ goto out;
+ }
+ break;
+ case SEC_CHAN_DOMAIN:
+ if (!(acct_ctrl & ACB_DOMTRUST)) {
+ DEBUG(0,("get_md4pw: Workstation %s: DOMAIN secure channel requested "
+ "but not a interdomain trust account\n", mach_acct));
+ status = NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ goto out;
+ }
+ break;
+ default:
+ break;
+ }
+
+ become_root();
+ status = dcerpc_samr_QueryUserInfo2(h,
+ mem_ctx,
+ &user_handle,
+ UserInternal1Information,
+ &info,
+ &result);
+ unbecome_root();
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ if (info->info18.nt_pwd_active == 0) {
+ DEBUG(0,("get_md4pw: Workstation %s: account does not have a password\n", mach_acct));
+ status = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ /* samr gives out nthash unencrypted (!) */
+ memcpy(md4pw->hash, info->info18.nt_pwd.hash, 16);
+
+ sid_compose(sid, domain_sid, user_rid);
+
+ out:
+ if (h && is_valid_policy_hnd(&user_handle)) {
+ dcerpc_samr_Close(h, mem_ctx, &user_handle, &result);
+ }
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+/*************************************************************************
+ _netr_ServerReqChallenge
+ *************************************************************************/
+
+NTSTATUS _netr_ServerReqChallenge(struct pipes_struct *p,
+ struct netr_ServerReqChallenge *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct netlogon_server_pipe_state *pipe_state = NULL;
+ NTSTATUS status;
+
+ pipe_state = dcesrv_iface_state_find_conn(
+ dce_call,
+ NETLOGON_SERVER_PIPE_STATE_MAGIC,
+ struct netlogon_server_pipe_state);
+
+ if (pipe_state) {
+ DEBUG(10,("_netr_ServerReqChallenge: new challenge requested. Clearing old state.\n"));
+ talloc_free(pipe_state);
+ }
+
+ pipe_state = talloc(p->mem_ctx, struct netlogon_server_pipe_state);
+ NT_STATUS_HAVE_NO_MEMORY(pipe_state);
+
+ pipe_state->client_challenge = *r->in.credentials;
+
+ netlogon_creds_random_challenge(&pipe_state->server_challenge);
+
+ *r->out.return_credentials = pipe_state->server_challenge;
+
+ status = dcesrv_iface_state_store_conn(
+ dce_call,
+ NETLOGON_SERVER_PIPE_STATE_MAGIC,
+ pipe_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ _netr_ServerAuthenticate
+ Create the initial credentials.
+ *************************************************************************/
+
+NTSTATUS _netr_ServerAuthenticate(struct pipes_struct *p,
+ struct netr_ServerAuthenticate *r)
+{
+ struct netr_ServerAuthenticate3 a;
+ uint32_t negotiate_flags = 0;
+ uint32_t rid;
+
+ a.in.server_name = r->in.server_name;
+ a.in.account_name = r->in.account_name;
+ a.in.secure_channel_type = r->in.secure_channel_type;
+ a.in.computer_name = r->in.computer_name;
+ a.in.credentials = r->in.credentials;
+ a.in.negotiate_flags = &negotiate_flags;
+
+ a.out.return_credentials = r->out.return_credentials;
+ a.out.rid = &rid;
+ a.out.negotiate_flags = &negotiate_flags;
+
+ return _netr_ServerAuthenticate3(p, &a);
+
+}
+
+/*************************************************************************
+ _netr_ServerAuthenticate3
+ *************************************************************************/
+
+NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p,
+ struct netr_ServerAuthenticate3 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ NTSTATUS status;
+ uint32_t srv_flgs;
+ /* r->in.negotiate_flags is an aliased pointer to r->out.negotiate_flags,
+ * so use a copy to avoid destroying the client values. */
+ uint32_t in_neg_flags = *r->in.negotiate_flags;
+ const char *fn;
+ struct loadparm_context *lp_ctx = p->dce_call->conn->dce_ctx->lp_ctx;
+ struct dom_sid sid;
+ struct samr_Password mach_pwd;
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_server_pipe_state *pipe_state = NULL;
+
+ /* According to Microsoft (see bugid #6099)
+ * Windows 7 looks at the negotiate_flags
+ * returned in this structure *even if the
+ * call fails with access denied* ! So in order
+ * to allow Win7 to connect to a Samba NT style
+ * PDC we set the flags before we know if it's
+ * an error or not.
+ */
+
+ /* 0x000001ff */
+ srv_flgs = NETLOGON_NEG_ACCOUNT_LOCKOUT |
+ NETLOGON_NEG_PERSISTENT_SAMREPL |
+ NETLOGON_NEG_ARCFOUR |
+ NETLOGON_NEG_PROMOTION_COUNT |
+ NETLOGON_NEG_CHANGELOG_BDC |
+ NETLOGON_NEG_FULL_SYNC_REPL |
+ NETLOGON_NEG_MULTIPLE_SIDS |
+ NETLOGON_NEG_REDO |
+ NETLOGON_NEG_PASSWORD_CHANGE_REFUSAL |
+ NETLOGON_NEG_PASSWORD_SET2;
+
+ /* Ensure we support strong (128-bit) keys. */
+ if (in_neg_flags & NETLOGON_NEG_STRONG_KEYS) {
+ srv_flgs |= NETLOGON_NEG_STRONG_KEYS;
+ }
+
+ if (in_neg_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ srv_flgs |= NETLOGON_NEG_SUPPORTS_AES;
+ }
+
+ if (in_neg_flags & NETLOGON_NEG_SCHANNEL) {
+ srv_flgs |= NETLOGON_NEG_SCHANNEL;
+ }
+
+ /*
+ * Support authentication of trusted domains.
+ *
+ * These flags are the minimum required set which works with win2k3
+ * and win2k8.
+ */
+ if (pdb_capabilities() & PDB_CAP_TRUSTED_DOMAINS_EX) {
+ srv_flgs |= NETLOGON_NEG_TRANSITIVE_TRUSTS |
+ NETLOGON_NEG_DNS_DOMAIN_TRUSTS |
+ NETLOGON_NEG_CROSS_FOREST_TRUSTS |
+ NETLOGON_NEG_NEUTRALIZE_NT4_EMULATION;
+ }
+
+ /*
+ * If weak crypto is disabled, do not announce that we support RC4.
+ */
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ srv_flgs &= ~NETLOGON_NEG_ARCFOUR;
+ }
+
+ switch (dce_call->pkt.u.request.opnum) {
+ case NDR_NETR_SERVERAUTHENTICATE:
+ fn = "_netr_ServerAuthenticate";
+ break;
+ case NDR_NETR_SERVERAUTHENTICATE2:
+ fn = "_netr_ServerAuthenticate2";
+ break;
+ case NDR_NETR_SERVERAUTHENTICATE3:
+ fn = "_netr_ServerAuthenticate3";
+ break;
+ default:
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* We use this as the key to store the creds: */
+ /* r->in.computer_name */
+
+ pipe_state = dcesrv_iface_state_find_conn(
+ dce_call,
+ NETLOGON_SERVER_PIPE_STATE_MAGIC,
+ struct netlogon_server_pipe_state);
+
+ if (!pipe_state) {
+ DEBUG(0,("%s: no challenge sent to client %s\n", fn,
+ r->in.computer_name));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto out;
+ }
+
+ status = get_md4pw(&mach_pwd,
+ r->in.account_name,
+ r->in.secure_channel_type,
+ &sid, p->msg_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: failed to get machine password for "
+ "account %s: %s\n",
+ fn, r->in.account_name, nt_errstr(status) ));
+ /* always return NT_STATUS_ACCESS_DENIED */
+ status = NT_STATUS_ACCESS_DENIED;
+ goto out;
+ }
+
+ /* From the client / server challenges and md4 password, generate sess key */
+ /* Check client credentials are valid. */
+ creds = netlogon_creds_server_init(p->mem_ctx,
+ r->in.account_name,
+ r->in.computer_name,
+ r->in.secure_channel_type,
+ &pipe_state->client_challenge,
+ &pipe_state->server_challenge,
+ &mach_pwd,
+ r->in.credentials,
+ r->out.return_credentials,
+ srv_flgs);
+ if (!creds) {
+ DEBUG(0,("%s: netlogon_creds_server_check failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ fn, r->in.computer_name,
+ r->in.account_name));
+ status = NT_STATUS_ACCESS_DENIED;
+ goto out;
+ }
+
+ creds->sid = dom_sid_dup(creds, &sid);
+ if (!creds->sid) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /* Store off the state so we can continue after client disconnect. */
+ become_root();
+ status = schannel_save_creds_state(p->mem_ctx, lp_ctx, creds);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(r->out.return_credentials);
+ goto out;
+ }
+
+ sid_peek_rid(&sid, r->out.rid);
+
+ status = NT_STATUS_OK;
+
+ out:
+
+ *r->out.negotiate_flags = srv_flgs;
+ return status;
+}
+
+/*************************************************************************
+ _netr_ServerAuthenticate2
+ *************************************************************************/
+
+NTSTATUS _netr_ServerAuthenticate2(struct pipes_struct *p,
+ struct netr_ServerAuthenticate2 *r)
+{
+ struct netr_ServerAuthenticate3 a;
+ uint32_t rid;
+
+ a.in.server_name = r->in.server_name;
+ a.in.account_name = r->in.account_name;
+ a.in.secure_channel_type = r->in.secure_channel_type;
+ a.in.computer_name = r->in.computer_name;
+ a.in.credentials = r->in.credentials;
+ a.in.negotiate_flags = r->in.negotiate_flags;
+
+ a.out.return_credentials = r->out.return_credentials;
+ a.out.rid = &rid;
+ a.out.negotiate_flags = r->out.negotiate_flags;
+
+ return _netr_ServerAuthenticate3(p, &a);
+}
+
+/*************************************************************************
+ *************************************************************************/
+
+static NTSTATUS samr_open_machine_account(
+ struct dcerpc_binding_handle *b,
+ const struct dom_sid *machine_sid,
+ uint32_t access_mask,
+ struct policy_handle *machine_handle)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct policy_handle connect_handle = { .handle_type = 0 };
+ struct policy_handle domain_handle = { .handle_type = 0 };
+ struct dom_sid domain_sid = *machine_sid;
+ uint32_t machine_rid;
+ NTSTATUS result = NT_STATUS_OK;
+ NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+ bool ok;
+
+ ok = sid_split_rid(&domain_sid, &machine_rid);
+ if (!ok) {
+ goto out;
+ }
+
+ status = dcerpc_samr_Connect2(
+ b,
+ frame,
+ lp_netbios_name(),
+ SAMR_ACCESS_CONNECT_TO_SERVER |
+ SAMR_ACCESS_ENUM_DOMAINS |
+ SAMR_ACCESS_LOOKUP_DOMAIN,
+ &connect_handle,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ status = dcerpc_samr_OpenDomain(
+ b,
+ frame,
+ &connect_handle,
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ &domain_sid,
+ &domain_handle,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ status = dcerpc_samr_OpenUser(
+ b,
+ frame,
+ &domain_handle,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ machine_rid,
+ machine_handle,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+out:
+ if ((b != NULL) && is_valid_policy_hnd(&domain_handle)) {
+ dcerpc_samr_Close(b, frame, &domain_handle, &result);
+ }
+ if ((b != NULL) && is_valid_policy_hnd(&connect_handle)) {
+ dcerpc_samr_Close(b, frame, &connect_handle, &result);
+ }
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct _samr_Credentials_t {
+ enum {
+ CRED_TYPE_NT_HASH,
+ CRED_TYPE_PLAIN_TEXT,
+ } cred_type;
+ union {
+ struct samr_Password *nt_hash;
+ const char *password;
+ } creds;
+};
+
+
+static NTSTATUS netr_set_machine_account_password(
+ TALLOC_CTX *mem_ctx,
+ struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const struct dom_sid *machine_sid,
+ struct _samr_Credentials_t *cr)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_OK;
+ struct dcerpc_binding_handle *h = NULL;
+ struct tsocket_address *local;
+ struct policy_handle user_handle = { .handle_type = 0 };
+ uint32_t acct_ctrl;
+ union samr_UserInfo *info;
+ struct samr_UserInfo18 info18;
+ struct samr_UserInfo26 info26;
+ DATA_BLOB in,out;
+ int rc;
+ DATA_BLOB session_key;
+ enum samr_UserInfoLevel infolevel;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ status = session_extract_session_key(session_info,
+ &session_key,
+ KEY_USE_16BYTES);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ rc = tsocket_address_inet_from_strings(frame,
+ "ip",
+ "127.0.0.1",
+ 0,
+ &local);
+ if (rc < 0) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ status = rpcint_binding_handle(frame,
+ &ndr_table_samr,
+ local,
+ NULL,
+ get_session_info_system(),
+ msg_ctx,
+ &h);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = samr_open_machine_account(
+ h, machine_sid, SEC_FLAG_MAXIMUM_ALLOWED, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcerpc_samr_QueryUserInfo2(h,
+ frame,
+ &user_handle,
+ UserControlInformation,
+ &info,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ acct_ctrl = info->info16.acct_flags;
+
+ if (!(acct_ctrl & ACB_WSTRUST ||
+ acct_ctrl & ACB_SVRTRUST ||
+ acct_ctrl & ACB_DOMTRUST)) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto out;
+ }
+
+ if (acct_ctrl & ACB_DISABLED) {
+ status = NT_STATUS_ACCOUNT_DISABLED;
+ goto out;
+ }
+
+ switch(cr->cred_type) {
+ case CRED_TYPE_NT_HASH:
+ ZERO_STRUCT(info18);
+
+ infolevel = UserInternal1Information;
+
+ in = data_blob_const(cr->creds.nt_hash, 16);
+ out = data_blob_talloc_zero(frame, 16);
+ if (out.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ rc = sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ if (rc != 0) {
+ status = gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ goto out;
+ }
+ memcpy(info18.nt_pwd.hash, out.data, out.length);
+
+ info18.nt_pwd_active = true;
+
+ info->info18 = info18;
+ break;
+ case CRED_TYPE_PLAIN_TEXT:
+ ZERO_STRUCT(info26);
+
+ infolevel = UserInternal5InformationNew;
+
+ status = init_samr_CryptPasswordEx(cr->creds.password,
+ &session_key,
+ &info26.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ info26.password_expired = PASS_DONT_CHANGE_AT_NEXT_LOGON;
+ info->info26 = info26;
+ break;
+ default:
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ break;
+ }
+
+ status = dcerpc_samr_SetUserInfo2(h,
+ frame,
+ &user_handle,
+ infolevel,
+ info,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto out;
+ }
+
+ out:
+ if (h && is_valid_policy_hnd(&user_handle)) {
+ dcerpc_samr_Close(h, frame, &user_handle, &result);
+ }
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+/*************************************************************************
+ _netr_ServerPasswordSet
+ *************************************************************************/
+
+NTSTATUS _netr_ServerPasswordSet(struct pipes_struct *p,
+ struct netr_ServerPasswordSet *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status = NT_STATUS_OK;
+ size_t i;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ struct _samr_Credentials_t cr = { CRED_TYPE_NT_HASH, {0}};
+
+ DEBUG(5,("_netr_ServerPasswordSet: %d\n", __LINE__));
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *computer_name = "<unknown>";
+
+ if (creds != NULL && creds->computer_name != NULL) {
+ computer_name = creds->computer_name;
+ }
+ DEBUG(2,("_netr_ServerPasswordSet: netlogon_creds_server_step failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ r->in.computer_name, computer_name));
+ TALLOC_FREE(creds);
+ return status;
+ }
+
+ DEBUG(3,("_netr_ServerPasswordSet: Server Password Set by remote machine:[%s] on account [%s]\n",
+ r->in.computer_name, creds->computer_name));
+
+ status = netlogon_creds_des_decrypt(creds, r->in.new_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(100,("_netr_ServerPasswordSet: new given value was :\n"));
+ for(i = 0; i < sizeof(r->in.new_password->hash); i++)
+ DEBUG(100,("%02X ", r->in.new_password->hash[i]));
+ DEBUG(100,("\n"));
+
+ cr.creds.nt_hash = r->in.new_password;
+ status = netr_set_machine_account_password(p->mem_ctx,
+ session_info,
+ p->msg_ctx,
+ creds->sid,
+ &cr);
+ return status;
+}
+
+/****************************************************************
+ _netr_ServerPasswordSet2
+****************************************************************/
+
+NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p,
+ struct netr_ServerPasswordSet2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ DATA_BLOB plaintext = data_blob_null;
+ DATA_BLOB new_password = data_blob_null;
+ size_t confounder_len;
+ DATA_BLOB dec_blob = data_blob_null;
+ DATA_BLOB enc_blob = data_blob_null;
+ struct samr_CryptPassword password_buf;
+ struct _samr_Credentials_t cr = { CRED_TYPE_PLAIN_TEXT, {0}};
+ bool ok;
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("netlogon_creds_server_step failed. "
+ "Rejecting auth request from client %s\n",
+ r->in.computer_name);
+ TALLOC_FREE(creds);
+ return status;
+ }
+
+ DBG_NOTICE("Server Password Set2 by remote "
+ "machine:[%s] on account [%s]\n",
+ r->in.computer_name,
+ creds->computer_name != NULL ?
+ creds->computer_name : "<unknown>");
+
+ memcpy(password_buf.data, r->in.new_password->data, 512);
+ SIVAL(password_buf.data, 512, r->in.new_password->length);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ status = netlogon_creds_aes_decrypt(creds,
+ password_buf.data,
+ 516);
+ } else {
+ status = netlogon_creds_arcfour_crypt(creds,
+ password_buf.data,
+ 516);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(creds);
+ return status;
+ }
+
+ if (!extract_pw_from_buffer(p->mem_ctx, password_buf.data, &new_password)) {
+ DEBUG(2,("_netr_ServerPasswordSet2: unable to extract password "
+ "from a buffer. Rejecting auth request as a wrong password\n"));
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Make sure the length field was encrypted,
+ * otherwise we are under attack.
+ */
+ if (new_password.length == r->in.new_password->length) {
+ DBG_WARNING("Length[%zu] field not encrypted\n",
+ new_password.length);
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * We don't allow empty passwords for machine accounts.
+ */
+ if (new_password.length < 2) {
+ DBG_WARNING("Empty password Length[%zu]\n",
+ new_password.length);
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Make sure the confounder part of CryptPassword
+ * buffer was encrypted, otherwise we are under attack.
+ */
+ confounder_len = 512 - new_password.length;
+ enc_blob = data_blob_const(r->in.new_password->data, confounder_len);
+ dec_blob = data_blob_const(password_buf.data, confounder_len);
+ if (confounder_len > 0 && data_blob_equal_const_time(&dec_blob, &enc_blob)) {
+ DBG_WARNING("Confounder buffer not encrypted Length[%zu]\n",
+ confounder_len);
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Check that the password part was actually encrypted,
+ * otherwise we are under attack.
+ */
+ enc_blob = data_blob_const(r->in.new_password->data + confounder_len,
+ new_password.length);
+ dec_blob = data_blob_const(password_buf.data + confounder_len,
+ new_password.length);
+ if (data_blob_equal_const_time(&dec_blob, &enc_blob)) {
+ DBG_WARNING("Password buffer not encrypted Length[%zu]\n",
+ new_password.length);
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * don't allow zero buffers
+ */
+ if (all_zero(new_password.data, new_password.length)) {
+ DBG_WARNING("Password zero buffer Length[%zu]\n",
+ new_password.length);
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* Convert from UTF16 -> plaintext. */
+ ok = convert_string_talloc(p->mem_ctx,
+ CH_UTF16,
+ CH_UNIX,
+ new_password.data,
+ new_password.length,
+ &plaintext.data,
+ &plaintext.length);
+ if (!ok) {
+ DBG_WARNING("unable to extract password from a buffer. "
+ "Rejecting auth request as a wrong password\n");
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * We don't allow empty passwords for machine accounts.
+ */
+
+ cr.creds.password = (const char*) plaintext.data;
+ if (strlen(cr.creds.password) == 0) {
+ DBG_WARNING("Empty plaintext password\n");
+ TALLOC_FREE(creds);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ status = netr_set_machine_account_password(p->mem_ctx,
+ session_info,
+ p->msg_ctx,
+ creds->sid,
+ &cr);
+ TALLOC_FREE(creds);
+ return status;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogoff
+ *************************************************************************/
+
+NTSTATUS _netr_LogonSamLogoff(struct pipes_struct *p,
+ struct netr_LogonSamLogoff *r)
+{
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds;
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ unbecome_root();
+
+ return status;
+}
+
+static NTSTATUS _netr_LogonSamLogon_check(const struct netr_LogonSamLogonEx *r)
+{
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (r->in.logon->password == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (r->in.validation_level) {
+ case NetlogonValidationSamInfo: /* 2 */
+ case NetlogonValidationSamInfo2: /* 3 */
+ break;
+ case NetlogonValidationSamInfo4: /* 6 */
+ if ((pdb_capabilities() & PDB_CAP_ADS) == 0) {
+ DEBUG(10,("Not adding validation info level 6 "
+ "without ADS passdb backend\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ break;
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ if (r->in.logon->network == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (r->in.validation_level) {
+ case NetlogonValidationSamInfo: /* 2 */
+ case NetlogonValidationSamInfo2: /* 3 */
+ break;
+ case NetlogonValidationSamInfo4: /* 6 */
+ if ((pdb_capabilities() & PDB_CAP_ADS) == 0) {
+ DEBUG(10,("Not adding validation info level 6 "
+ "without ADS passdb backend\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ break;
+
+ case NetlogonGenericInformation:
+ if (r->in.logon->generic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we don't support this here */
+ return NT_STATUS_INVALID_PARAMETER;
+#if 0
+ switch (r->in.validation_level) {
+ /* TODO: case NetlogonValidationGenericInfo: 4 */
+ case NetlogonValidationGenericInfo2: /* 5 */
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ break;
+#endif
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogon_base
+ *************************************************************************/
+
+static NTSTATUS _netr_LogonSamLogon_base(struct pipes_struct *p,
+ struct netr_LogonSamLogonEx *r,
+ struct netlogon_creds_CredentialState *creds)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ NTSTATUS status = NT_STATUS_OK;
+ union netr_LogonLevel *logon = r->in.logon;
+ const char *nt_username, *nt_domain, *nt_workstation;
+ char *sanitized_username = NULL;
+ struct auth_usersupplied_info *user_info = NULL;
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_context *auth_context = NULL;
+ const char *fn;
+ enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ dcesrv_call_auth_info(dce_call, &auth_type, &auth_level);
+
+#ifdef DEBUG_PASSWORD
+ logon = netlogon_creds_shallow_copy_logon(p->mem_ctx,
+ r->in.logon_level,
+ r->in.logon);
+ if (logon == NULL) {
+ logon = r->in.logon;
+ }
+#endif
+
+ switch (opnum) {
+ case NDR_NETR_LOGONSAMLOGON:
+ fn = "_netr_LogonSamLogon";
+ /*
+ * Already called netr_check_schannel() via
+ * netr_creds_server_step_check()
+ */
+ break;
+ case NDR_NETR_LOGONSAMLOGONWITHFLAGS:
+ fn = "_netr_LogonSamLogonWithFlags";
+ /*
+ * Already called netr_check_schannel() via
+ * netr_creds_server_step_check()
+ */
+ break;
+ case NDR_NETR_LOGONSAMLOGONEX:
+ fn = "_netr_LogonSamLogonEx";
+
+ if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = dcesrv_netr_check_schannel(p->dce_call,
+ creds,
+ auth_type,
+ auth_level,
+ opnum);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ break;
+ default:
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *r->out.authoritative = 1; /* authoritative response */
+
+ switch (r->in.validation_level) {
+ case 2:
+ r->out.validation->sam2 = talloc_zero(p->mem_ctx, struct netr_SamInfo2);
+ if (!r->out.validation->sam2) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ case 3:
+ r->out.validation->sam3 = talloc_zero(p->mem_ctx, struct netr_SamInfo3);
+ if (!r->out.validation->sam3) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ case 6:
+ r->out.validation->sam6 = talloc_zero(p->mem_ctx, struct netr_SamInfo6);
+ if (!r->out.validation->sam6) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ default:
+ DEBUG(0,("%s: bad validation_level value %d.\n",
+ fn, (int)r->in.validation_level));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ nt_username = logon->password->identity_info.account_name.string ?
+ logon->password->identity_info.account_name.string : "";
+ nt_domain = logon->password->identity_info.domain_name.string ?
+ logon->password->identity_info.domain_name.string : "";
+ nt_workstation = logon->password->identity_info.workstation.string ?
+ logon->password->identity_info.workstation.string : "";
+
+ DEBUG(3,("SAM Logon (Interactive). Domain:[%s]. ", lp_workgroup()));
+ break;
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ nt_username = logon->network->identity_info.account_name.string ?
+ logon->network->identity_info.account_name.string : "";
+ nt_domain = logon->network->identity_info.domain_name.string ?
+ logon->network->identity_info.domain_name.string : "";
+ nt_workstation = logon->network->identity_info.workstation.string ?
+ logon->network->identity_info.workstation.string : "";
+
+ DEBUG(3,("SAM Logon (Network). Domain:[%s]. ", lp_workgroup()));
+ break;
+ default:
+ DEBUG(2,("SAM Logon: unsupported switch value\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ } /* end switch */
+
+ DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username, nt_workstation, nt_domain));
+
+ DEBUG(5,("Attempting validation level %d for unmapped username %s.\n",
+ r->in.validation_level, nt_username));
+
+ status = netlogon_creds_decrypt_samlogon_logon(creds,
+ r->in.logon_level,
+ logon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = make_auth3_context_for_netlogon(talloc_tos(), &auth_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (r->in.logon_level) {
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ {
+ const char *wksname = nt_workstation;
+ const char *workgroup = lp_workgroup();
+ bool ok;
+
+ ok = auth3_context_set_challenge(
+ auth_context, logon->network->challenge, "fixed");
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* For a network logon, the workstation name comes in with two
+ * backslashes in the front. Strip them if they are there. */
+
+ if (*wksname == '\\') wksname++;
+ if (*wksname == '\\') wksname++;
+
+ /* Standard challenge/response authentication */
+ if (!make_user_info_netlogon_network(talloc_tos(),
+ &user_info,
+ nt_username, nt_domain,
+ wksname,
+ remote_address,
+ local_address,
+ logon->network->identity_info.parameter_control,
+ logon->network->lm.data,
+ logon->network->lm.length,
+ logon->network->nt.data,
+ logon->network->nt.length)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = NTLMv2_RESPONSE_verify_netlogon_creds(
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ user_info->password.response.nt,
+ creds, workgroup);
+ }
+ break;
+ }
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+
+ /* 'Interactive' authentication, supplies the password in its
+ MD4 form, encrypted with the session key. We will convert
+ this to challenge/response for the auth subsystem to chew
+ on */
+ {
+ uint8_t chal[8];
+
+#ifdef DEBUG_PASSWORD
+ if (logon != r->in.logon) {
+ DEBUG(100,("lm owf password:"));
+ dump_data(100,
+ r->in.logon->password->lmpassword.hash, 16);
+
+ DEBUG(100,("nt owf password:"));
+ dump_data(100,
+ r->in.logon->password->ntpassword.hash, 16);
+ }
+
+ DEBUG(100,("decrypt of lm owf password:"));
+ dump_data(100, logon->password->lmpassword.hash, 16);
+
+ DEBUG(100,("decrypt of nt owf password:"));
+ dump_data(100, logon->password->ntpassword.hash, 16);
+#endif
+
+ auth_get_ntlm_challenge(auth_context, chal);
+
+ if (!make_user_info_netlogon_interactive(talloc_tos(),
+ &user_info,
+ nt_username, nt_domain,
+ nt_workstation,
+ remote_address,
+ local_address,
+ logon->password->identity_info.parameter_control,
+ chal,
+ logon->password->lmpassword.hash,
+ logon->password->ntpassword.hash)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ default:
+ DEBUG(2,("SAM Logon: unsupported switch value\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ } /* end switch */
+
+ if ( NT_STATUS_IS_OK(status) ) {
+ status = auth_check_ntlm_password(p->mem_ctx,
+ auth_context,
+ user_info,
+ &server_info,
+ r->out.authoritative);
+ }
+
+ TALLOC_FREE(auth_context);
+ TALLOC_FREE(user_info);
+
+ DEBUG(5,("%s: check_password returned status %s\n",
+ fn, nt_errstr(status)));
+
+ /* Check account and password */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(server_info);
+ return status;
+ }
+
+ if (server_info->guest) {
+ /* We don't like guest domain logons... */
+ DEBUG(5,("%s: Attempted domain logon as GUEST "
+ "denied.\n", fn));
+ TALLOC_FREE(server_info);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ sanitized_username = talloc_alpha_strcpy(talloc_tos(),
+ nt_username,
+ SAFE_NETBIOS_CHARS "$");
+ if (sanitized_username == NULL) {
+ TALLOC_FREE(server_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ set_current_user_info(sanitized_username,
+ server_info->unix_name,
+ server_info->info3->base.logon_domain.string);
+ TALLOC_FREE(sanitized_username);
+
+ /* This is the point at which, if the login was successful, that
+ the SAM Local Security Authority should record that the user is
+ logged in to the domain. */
+
+ switch (r->in.validation_level) {
+ case 2:
+ status = serverinfo_to_SamInfo2(server_info,
+ r->out.validation->sam2);
+ break;
+ case 3:
+ status = serverinfo_to_SamInfo3(server_info,
+ r->out.validation->sam3);
+ break;
+ case 6: {
+ /* Only allow this if the pipe is protected. */
+ if (auth_level < DCERPC_AUTH_LEVEL_PRIVACY) {
+ DEBUG(0,("netr_Validation6: client %s not using privacy for netlogon\n",
+ get_remote_machine_name()));
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ status = serverinfo_to_SamInfo6(server_info,
+ r->out.validation->sam6);
+ break;
+ }
+ }
+
+ TALLOC_FREE(server_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = netlogon_creds_encrypt_samlogon_validation(creds,
+ r->in.validation_level,
+ r->out.validation);
+
+ return status;
+}
+
+/****************************************************************
+ _netr_LogonSamLogonWithFlags
+****************************************************************/
+
+NTSTATUS _netr_LogonSamLogonWithFlags(struct pipes_struct *p,
+ struct netr_LogonSamLogonWithFlags *r)
+{
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_LogonSamLogonEx r2;
+ struct netr_Authenticator return_authenticator;
+
+ *r->out.authoritative = true;
+
+ 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;
+ r2.out.validation = r->out.validation;
+ r2.out.authoritative = r->out.authoritative;
+ r2.out.flags = r->out.flags;
+
+ status = _netr_LogonSamLogon_check(&r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ &return_authenticator,
+ &creds);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = _netr_LogonSamLogon_base(p, &r2, creds);
+
+ *r->out.return_authenticator = return_authenticator;
+
+ return status;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogon
+ *************************************************************************/
+
+NTSTATUS _netr_LogonSamLogon(struct pipes_struct *p,
+ struct netr_LogonSamLogon *r)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogonWithFlags r2;
+ uint32_t flags = 0;
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.computer_name = r->in.computer_name;
+ r2.in.credential = r->in.credential;
+ r2.in.logon_level = r->in.logon_level;
+ r2.in.logon = r->in.logon;
+ r2.in.validation_level = r->in.validation_level;
+ r2.in.return_authenticator = r->in.return_authenticator;
+ r2.in.flags = &flags;
+ r2.out.validation = r->out.validation;
+ r2.out.authoritative = r->out.authoritative;
+ r2.out.flags = &flags;
+ r2.out.return_authenticator = r->out.return_authenticator;
+
+ status = _netr_LogonSamLogonWithFlags(p, &r2);
+
+ return status;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogonEx
+ - no credential chaining. Map into net sam logon.
+ *************************************************************************/
+
+NTSTATUS _netr_LogonSamLogonEx(struct pipes_struct *p,
+ struct netr_LogonSamLogonEx *r)
+{
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ struct loadparm_context *lp_ctx = p->dce_call->conn->dce_ctx->lp_ctx;
+
+ *r->out.authoritative = true;
+
+ status = _netr_LogonSamLogon_check(r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ become_root();
+ status = schannel_get_creds_state(p->mem_ctx, lp_ctx,
+ r->in.computer_name, &creds);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = _netr_LogonSamLogon_base(p, r, creds);
+ TALLOC_FREE(creds);
+
+ return status;
+}
+
+/*************************************************************************
+ _ds_enum_dom_trusts
+ *************************************************************************/
+#if 0 /* JERRY -- not correct */
+ NTSTATUS _ds_enum_dom_trusts(struct pipes_struct *p, DS_Q_ENUM_DOM_TRUSTS *q_u,
+ DS_R_ENUM_DOM_TRUSTS *r_u)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ /* TODO: According to MSDN, the can only be executed against a
+ DC or domain member running Windows 2000 or later. Need
+ to test against a standalone 2k server and see what it
+ does. A windows 2000 DC includes its own domain in the
+ list. --jerry */
+
+ return status;
+}
+#endif /* JERRY */
+
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_LogonUasLogon(struct pipes_struct *p,
+ struct netr_LogonUasLogon *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_LogonUasLogoff(struct pipes_struct *p,
+ struct netr_LogonUasLogoff *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseDeltas(struct pipes_struct *p,
+ struct netr_DatabaseDeltas *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseSync(struct pipes_struct *p,
+ struct netr_DatabaseSync *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_AccountDeltas(struct pipes_struct *p,
+ struct netr_AccountDeltas *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_AccountSync(struct pipes_struct *p,
+ struct netr_AccountSync *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool wb_getdcname(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char **dcname,
+ uint32_t flags,
+ WERROR *werr)
+{
+ wbcErr result;
+ struct wbcDomainControllerInfo *dc_info = NULL;
+
+ result = wbcLookupDomainController(domain,
+ flags,
+ &dc_info);
+ switch (result) {
+ case WBC_ERR_SUCCESS:
+ break;
+ case WBC_ERR_WINBIND_NOT_AVAILABLE:
+ return false;
+ case WBC_ERR_DOMAIN_NOT_FOUND:
+ *werr = WERR_NO_SUCH_DOMAIN;
+ return true;
+ default:
+ *werr = WERR_DOMAIN_CONTROLLER_NOT_FOUND;
+ return true;
+ }
+
+ *dcname = talloc_strdup(mem_ctx, dc_info->dc_name);
+ wbcFreeMemory(dc_info);
+ if (!*dcname) {
+ *werr = WERR_NOT_ENOUGH_MEMORY;
+ return false;
+ }
+
+ *werr = WERR_OK;
+
+ return true;
+}
+
+/****************************************************************
+ _netr_GetDcName
+****************************************************************/
+
+WERROR _netr_GetDcName(struct pipes_struct *p,
+ struct netr_GetDcName *r)
+{
+ NTSTATUS status;
+ WERROR werr;
+ uint32_t flags;
+ struct netr_DsRGetDCNameInfo *info;
+ bool ret;
+
+ ret = wb_getdcname(p->mem_ctx,
+ r->in.domainname,
+ r->out.dcname,
+ WBC_LOOKUP_DC_IS_FLAT_NAME |
+ WBC_LOOKUP_DC_RETURN_FLAT_NAME |
+ WBC_LOOKUP_DC_PDC_REQUIRED,
+ &werr);
+ if (ret == true) {
+ return werr;
+ }
+
+ flags = DS_PDC_REQUIRED | DS_IS_FLAT_NAME | DS_RETURN_FLAT_NAME;
+
+ status = dsgetdcname(p->mem_ctx,
+ p->msg_ctx,
+ r->in.domainname,
+ NULL,
+ NULL,
+ flags,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ *r->out.dcname = talloc_strdup(p->mem_ctx, info->dc_unc);
+ talloc_free(info);
+ if (!*r->out.dcname) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _netr_GetAnyDCName
+****************************************************************/
+
+WERROR _netr_GetAnyDCName(struct pipes_struct *p,
+ struct netr_GetAnyDCName *r)
+{
+ NTSTATUS status;
+ WERROR werr;
+ uint32_t flags;
+ struct netr_DsRGetDCNameInfo *info;
+ bool ret;
+
+ ret = wb_getdcname(p->mem_ctx,
+ r->in.domainname,
+ r->out.dcname,
+ WBC_LOOKUP_DC_IS_FLAT_NAME |
+ WBC_LOOKUP_DC_RETURN_FLAT_NAME,
+ &werr);
+ if (ret == true) {
+ return werr;
+ }
+
+ flags = DS_IS_FLAT_NAME | DS_RETURN_FLAT_NAME;
+
+ status = dsgetdcname(p->mem_ctx,
+ p->msg_ctx,
+ r->in.domainname,
+ NULL,
+ NULL,
+ flags,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ *r->out.dcname = talloc_strdup(p->mem_ctx, info->dc_unc);
+ talloc_free(info);
+ if (!*r->out.dcname) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseSync2(struct pipes_struct *p,
+ struct netr_DatabaseSync2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseRedo(struct pipes_struct *p,
+ struct netr_DatabaseRedo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetDCName(struct pipes_struct *p,
+ struct netr_DsRGetDCName *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_LogonGetCapabilities(struct pipes_struct *p,
+ struct netr_LogonGetCapabilities *r)
+{
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS status;
+
+ switch (r->in.query_level) {
+ case 1:
+ break;
+ case 2:
+ /*
+ * Until we know the details behind KB5028166
+ * just return DCERPC_NCA_S_FAULT_INVALID_TAG
+ * like an unpatched Windows Server.
+ */
+ FALL_THROUGH;
+ default:
+ /*
+ * There would not be a way to marshall the
+ * the response. Which would mean our final
+ * ndr_push would fail an we would return
+ * an RPC-level fault with DCERPC_FAULT_BAD_STUB_DATA.
+ *
+ * But it's important to match a Windows server
+ * especially before KB5028166, see also our bug #15418
+ * Otherwise Windows client would stop talking to us.
+ */
+ p->fault_state = DCERPC_NCA_S_FAULT_INVALID_TAG;
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.capabilities->server_capabilities = creds->negotiate_flags;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONSETSERVICEBITS(struct pipes_struct *p,
+ struct netr_NETRLOGONSETSERVICEBITS *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_LogonGetTrustRid(struct pipes_struct *p,
+ struct netr_LogonGetTrustRid *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONCOMPUTESERVERDIGEST(struct pipes_struct *p,
+ struct netr_NETRLOGONCOMPUTESERVERDIGEST *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONCOMPUTECLIENTDIGEST(struct pipes_struct *p,
+ struct netr_NETRLOGONCOMPUTECLIENTDIGEST *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetDCNameEx(struct pipes_struct *p,
+ struct netr_DsRGetDCNameEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetSiteName(struct pipes_struct *p,
+ struct netr_DsRGetSiteName *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_LogonGetDomainInfo(struct pipes_struct *p,
+ struct netr_LogonGetDomainInfo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_ServerPasswordGet(struct pipes_struct *p,
+ struct netr_ServerPasswordGet *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_NetrLogonSendToSam(struct pipes_struct *p,
+ struct netr_NetrLogonSendToSam *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRAddressToSitenamesW(struct pipes_struct *p,
+ struct netr_DsRAddressToSitenamesW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetDCNameEx2(struct pipes_struct *p,
+ struct netr_DsRGetDCNameEx2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN(struct pipes_struct *p,
+ struct netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NetrEnumerateTrustedDomainsEx(struct pipes_struct *p,
+ struct netr_NetrEnumerateTrustedDomainsEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRAddressToSitenamesExW(struct pipes_struct *p,
+ struct netr_DsRAddressToSitenamesExW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsrGetDcSiteCoverageW(struct pipes_struct *p,
+ struct netr_DsrGetDcSiteCoverageW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsrEnumerateDomainTrusts(struct pipes_struct *p,
+ struct netr_DsrEnumerateDomainTrusts *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsrDeregisterDNSHostRecords(struct pipes_struct *p,
+ struct netr_DsrDeregisterDNSHostRecords *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_ServerTrustPasswordsGet(struct pipes_struct *p,
+ struct netr_ServerTrustPasswordsGet *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS fill_forest_trust_array(TALLOC_CTX *mem_ctx,
+ struct lsa_ForestTrustInformation *info)
+{
+ struct lsa_ForestTrustRecord *e;
+ struct pdb_domain_info *dom_info;
+ struct lsa_ForestTrustDomainInfo *domain_info;
+ char **upn_suffixes = NULL;
+ uint32_t num_suffixes = 0;
+ uint32_t i = 0;
+ NTSTATUS status;
+
+ dom_info = pdb_get_domain_info(mem_ctx);
+ if (dom_info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->count = 2;
+
+ become_root();
+ status = pdb_enum_upn_suffixes(info, &num_suffixes, &upn_suffixes);
+ unbecome_root();
+ if (NT_STATUS_IS_OK(status) && (num_suffixes > 0)) {
+ info->count += num_suffixes;
+ }
+
+ info->entries = talloc_array(info, struct lsa_ForestTrustRecord *, info->count);
+ if (info->entries == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ e = talloc(info, struct lsa_ForestTrustRecord);
+ if (e == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ e->flags = 0;
+ e->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+ e->time = 0; /* so far always 0 in trces. */
+ e->forest_trust_data.top_level_name.string = talloc_steal(info,
+ dom_info->dns_forest);
+
+ info->entries[0] = e;
+
+ if (num_suffixes > 0) {
+ for (i = 0; i < num_suffixes ; i++) {
+ e = talloc(info, struct lsa_ForestTrustRecord);
+ if (e == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ e->flags = 0;
+ e->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+ e->time = 0; /* so far always 0 in traces. */
+ e->forest_trust_data.top_level_name.string = upn_suffixes[i];
+ info->entries[1 + i] = e;
+ }
+ }
+
+ e = talloc(info, struct lsa_ForestTrustRecord);
+ if (e == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* TODO: check if disabled and set flags accordingly */
+ e->flags = 0;
+ e->type = LSA_FOREST_TRUST_DOMAIN_INFO;
+ e->time = 0; /* so far always 0 in traces. */
+
+ domain_info = &e->forest_trust_data.domain_info;
+ domain_info->domain_sid = dom_sid_dup(info, &dom_info->sid);
+
+ domain_info->dns_domain_name.string = talloc_steal(info,
+ dom_info->dns_domain);
+ domain_info->netbios_domain_name.string = talloc_steal(info,
+ dom_info->name);
+
+ info->entries[info->count - 1] = e;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetForestTrustInformation(struct pipes_struct *p,
+ struct netr_DsRGetForestTrustInformation *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ struct lsa_ForestTrustInformation *info, **info_ptr;
+ enum security_user_level security_level;
+
+ security_level = security_session_user_level(session_info, NULL);
+ if (security_level < SECURITY_USER) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.flags & (~DS_GFTI_UPDATE_TDO)) {
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_INVALID_FLAGS;
+ }
+
+ if ((r->in.flags & DS_GFTI_UPDATE_TDO) && (lp_server_role() != ROLE_DOMAIN_PDC)) {
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NERR_NOTPRIMARY;
+ }
+
+ if ((r->in.trusted_domain_name == NULL) && (r->in.flags & DS_GFTI_UPDATE_TDO)) {
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* retrieve forest trust information and stop further processing */
+ if (r->in.trusted_domain_name == NULL) {
+ info_ptr = talloc(p->mem_ctx, struct lsa_ForestTrustInformation *);
+ if (info_ptr == NULL) {
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ info = talloc_zero(info_ptr, struct lsa_ForestTrustInformation);
+ if (info == NULL) {
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Fill forest trust information and expand UPN suffixes list */
+ status = fill_forest_trust_array(p->mem_ctx, info);
+ if (!NT_STATUS_IS_OK(status)) {
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *info_ptr = info;
+ r->out.forest_trust_info = info_ptr;
+
+ return WERR_OK;
+
+ }
+
+ /* TODO: implement remaining parts of DsrGetForestTrustInformation (opnum 43)
+ * when trusted_domain_name is not NULL */
+
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _netr_GetForestTrustInformation
+****************************************************************/
+
+NTSTATUS _netr_GetForestTrustInformation(struct pipes_struct *p,
+ struct netr_GetForestTrustInformation *r)
+{
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds;
+ struct lsa_ForestTrustInformation *info, **info_ptr;
+
+ /* TODO: check server name */
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ((creds->secure_channel_type != SEC_CHAN_DNS_DOMAIN) &&
+ (creds->secure_channel_type != SEC_CHAN_DOMAIN)) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ info_ptr = talloc(p->mem_ctx, struct lsa_ForestTrustInformation *);
+ if (!info_ptr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info = talloc_zero(info_ptr, struct lsa_ForestTrustInformation);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Fill forest trust information, do expand UPN suffixes list */
+ status = fill_forest_trust_array(p->mem_ctx, info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *info_ptr = info;
+ r->out.forest_trust_info = info_ptr;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS get_password_from_trustAuth(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *trustAuth_blob,
+ struct netlogon_creds_CredentialState *creds,
+ struct samr_Password *current_pw_enc,
+ struct samr_Password *previous_pw_enc)
+{
+ enum ndr_err_code ndr_err;
+ struct trustAuthInOutBlob trustAuth;
+ NTSTATUS status;
+
+ ndr_err = ndr_pull_struct_blob_all(trustAuth_blob, mem_ctx, &trustAuth,
+ (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (trustAuth.count != 0 && trustAuth.current.count != 0 &&
+ trustAuth.current.array[0].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+ mdfour(current_pw_enc->hash,
+ trustAuth.current.array[0].AuthInfo.clear.password,
+ trustAuth.current.array[0].AuthInfo.clear.size);
+ status = netlogon_creds_des_encrypt(creds, current_pw_enc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+
+ if (trustAuth.previous.count != 0 &&
+ trustAuth.previous.array[0].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+ mdfour(previous_pw_enc->hash,
+ trustAuth.previous.array[0].AuthInfo.clear.password,
+ trustAuth.previous.array[0].AuthInfo.clear.size);
+ status = netlogon_creds_des_encrypt(creds, previous_pw_enc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ ZERO_STRUCTP(previous_pw_enc);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+ _netr_ServerGetTrustInfo
+****************************************************************/
+
+NTSTATUS _netr_ServerGetTrustInfo(struct pipes_struct *p,
+ struct netr_ServerGetTrustInfo *r)
+{
+ NTSTATUS status;
+ struct netlogon_creds_CredentialState *creds;
+ char *account_name;
+ size_t account_name_last;
+ bool trusted;
+ struct netr_TrustInfo *trust_info;
+ struct pdb_trusted_domain *td;
+
+ /* TODO: check server name */
+
+ become_root();
+ status = dcesrv_netr_creds_server_step_check(p->dce_call,
+ p->mem_ctx,
+ r->in.computer_name,
+ r->in.credential,
+ r->out.return_authenticator,
+ &creds);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ account_name = talloc_strdup(p->mem_ctx, r->in.account_name);
+ if (account_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ account_name_last = strlen(account_name);
+ if (account_name_last == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ account_name_last--;
+ if (account_name[account_name_last] == '.') {
+ account_name[account_name_last] = '\0';
+ }
+
+ if ((creds->secure_channel_type != SEC_CHAN_DNS_DOMAIN) &&
+ (creds->secure_channel_type != SEC_CHAN_DOMAIN)) {
+ trusted = false;
+ } else {
+ trusted = true;
+ }
+
+
+ if (trusted) {
+ account_name_last = strlen(account_name);
+ if (account_name_last == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ account_name_last--;
+ if (account_name[account_name_last] == '$') {
+ account_name[account_name_last] = '\0';
+ }
+
+ status = pdb_get_trusted_domain(p->mem_ctx, account_name, &td);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->out.trust_info != NULL) {
+ trust_info = talloc_zero(p->mem_ctx, struct netr_TrustInfo);
+ if (trust_info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trust_info->count = 1;
+
+ trust_info->data = talloc_array(trust_info, uint32_t, 1);
+ if (trust_info->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trust_info->data[0] = td->trust_attributes;
+
+ *r->out.trust_info = trust_info;
+ }
+
+ if (td->trust_auth_incoming.data == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = get_password_from_trustAuth(p->mem_ctx,
+ &td->trust_auth_incoming,
+ creds,
+ r->out.new_owf_password,
+ r->out.old_owf_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ } else {
+/* TODO: look for machine password */
+ ZERO_STRUCTP(r->out.new_owf_password);
+ ZERO_STRUCTP(r->out.old_owf_password);
+
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_Unused47(struct pipes_struct *p,
+ struct netr_Unused47 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DsrUpdateReadOnlyServerDnsRecords(struct pipes_struct *p,
+ struct netr_DsrUpdateReadOnlyServerDnsRecords *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * Define the bind function that will be used by ndr_netlogon_scompat.c,
+ * included at the bottom of this file.
+ */
+#define DCESRV_INTERFACE_NETLOGON_BIND(context, iface) \
+ dcesrv_interface_netlogon_bind(context, iface)
+
+static NTSTATUS dcesrv_interface_netlogon_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ struct loadparm_context *lp_ctx = context->conn->dce_ctx->lp_ctx;
+ int schannel = lpcfg_server_schannel(lp_ctx);
+ bool schannel_global_required = (schannel == true);
+ bool global_require_seal = lpcfg_server_schannel_require_seal(lp_ctx);
+ static bool warned_global_schannel_once = false;
+ static bool warned_global_seal_once = false;
+
+ if (!schannel_global_required && !warned_global_schannel_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2020-1472(ZeroLogon): "
+ "Please configure 'server schannel = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=14497\n");
+ warned_global_schannel_once = true;
+ }
+
+ if (!global_require_seal && !warned_global_seal_once) {
+ /*
+ * We want admins to notice their misconfiguration!
+ */
+ D_ERR("CVE-2022-38023 (and others): "
+ "Please configure 'server schannel require seal = yes' (the default), "
+ "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+ warned_global_seal_once = true;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_netlogon_scompat.c"
diff --git a/source3/rpc_server/ntsvcs/srv_ntsvcs_nt.c b/source3/rpc_server/ntsvcs/srv_ntsvcs_nt.c
new file mode 100644
index 0000000..cfa2336
--- /dev/null
+++ b/source3/rpc_server/ntsvcs/srv_ntsvcs_nt.c
@@ -0,0 +1,810 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ * Copyright (C) Guenther Deschner 2008,2009.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_ntsvcs.h"
+#include "librpc/gen_ndr/ndr_ntsvcs_scompat.h"
+#include "services/svc_winreg_glue.h"
+#include "../libcli/registry/util_reg.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+********************************************************************/
+
+static char* get_device_path(TALLOC_CTX *mem_ctx, const char *device )
+{
+ return talloc_asprintf(mem_ctx, "ROOT\\Legacy_%s\\0000", device);
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_GetVersion(struct pipes_struct *p,
+ struct PNP_GetVersion *r)
+{
+ *r->out.version = 0x0400; /* no idea what this means */
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_GetDeviceListSize(struct pipes_struct *p,
+ struct PNP_GetDeviceListSize *r)
+{
+ char *devicepath;
+
+ if ((r->in.flags & CM_GETIDLIST_FILTER_SERVICE) &&
+ (!r->in.devicename)) {
+ return WERR_CM_INVALID_POINTER;
+ }
+
+ if (!(devicepath = get_device_path(p->mem_ctx, r->in.devicename))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *r->out.size = strlen(devicepath) + 2;
+
+ TALLOC_FREE(devicepath);
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _PNP_GetDeviceList
+****************************************************************/
+
+WERROR _PNP_GetDeviceList(struct pipes_struct *p,
+ struct PNP_GetDeviceList *r)
+{
+ char *devicepath;
+ uint32_t size = 0;
+ const char **multi_sz = NULL;
+ DATA_BLOB blob;
+
+ if ((r->in.flags & CM_GETIDLIST_FILTER_SERVICE) &&
+ (!r->in.filter)) {
+ return WERR_CM_INVALID_POINTER;
+ }
+
+ if (!(devicepath = get_device_path(p->mem_ctx, r->in.filter))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ size = strlen(devicepath) + 2;
+
+ if (*r->in.length < size) {
+ return WERR_CM_BUFFER_SMALL;
+ }
+
+ multi_sz = talloc_zero_array(p->mem_ctx, const char *, 2);
+ if (!multi_sz) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ multi_sz[0] = devicepath;
+
+ if (!push_reg_multi_sz(multi_sz, &blob, multi_sz)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (*r->in.length < blob.length/2) {
+ return WERR_CM_BUFFER_SMALL;
+ }
+
+ memcpy(r->out.buffer, blob.data, blob.length);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+_PNP_GetDeviceRegProp
+********************************************************************/
+
+WERROR _PNP_GetDeviceRegProp(struct pipes_struct *p,
+ struct PNP_GetDeviceRegProp *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ char *ptr;
+ const char *result;
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ switch( r->in.property ) {
+ case DEV_REGPROP_DESC:
+
+ /* just parse the service name from the device path and then
+ lookup the display name */
+ if ( !(ptr = strrchr_m( r->in.devicepath, '\\' )) )
+ return WERR_GEN_FAILURE;
+ *ptr = '\0';
+
+ if ( !(ptr = strrchr_m( r->in.devicepath, '_' )) )
+ return WERR_GEN_FAILURE;
+ ptr++;
+
+ mem_ctx = talloc_stackframe();
+
+ result = svcctl_lookup_dispname(mem_ctx,
+ p->msg_ctx,
+ session_info,
+ ptr);
+ if (result == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!push_reg_sz(mem_ctx, &blob, result)) {
+ talloc_free(mem_ctx);
+ return WERR_GEN_FAILURE;
+ }
+
+ if (*r->in.buffer_size < blob.length) {
+ *r->out.needed = blob.length;
+ *r->out.buffer_size = 0;
+ talloc_free(mem_ctx);
+ return WERR_CM_BUFFER_SMALL;
+ }
+
+ r->out.buffer = (uint8_t *)talloc_memdup(p->mem_ctx, blob.data, blob.length);
+ talloc_free(mem_ctx);
+ if (!r->out.buffer) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *r->out.reg_data_type = REG_SZ; /* always 1...tested using a remove device manager connection */
+ *r->out.buffer_size = blob.length;
+ *r->out.needed = blob.length;
+
+ break;
+
+ default:
+ *r->out.reg_data_type = 0x00437c98; /* ??? */
+ return WERR_CM_NO_SUCH_VALUE;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_ValidateDeviceInstance(struct pipes_struct *p,
+ struct PNP_ValidateDeviceInstance *r)
+{
+ /* whatever dude */
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_GetHwProfInfo(struct pipes_struct *p,
+ struct PNP_GetHwProfInfo *r)
+{
+ /* steal the incoming buffer */
+
+ r->out.info = r->in.info;
+
+ /* Take the 5th Ammentment */
+
+ return WERR_CM_NO_MORE_HW_PROFILES;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_HwProfFlags(struct pipes_struct *p,
+ struct PNP_HwProfFlags *r)
+{
+ /* just nod your head */
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_Disconnect(struct pipes_struct *p,
+ struct PNP_Disconnect *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_Connect(struct pipes_struct *p,
+ struct PNP_Connect *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetGlobalState(struct pipes_struct *p,
+ struct PNP_GetGlobalState *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_InitDetection(struct pipes_struct *p,
+ struct PNP_InitDetection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_ReportLogOn(struct pipes_struct *p,
+ struct PNP_ReportLogOn *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetRootDeviceInstance(struct pipes_struct *p,
+ struct PNP_GetRootDeviceInstance *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetRelatedDeviceInstance(struct pipes_struct *p,
+ struct PNP_GetRelatedDeviceInstance *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_EnumerateSubKeys(struct pipes_struct *p,
+ struct PNP_EnumerateSubKeys *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetDepth(struct pipes_struct *p,
+ struct PNP_GetDepth *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetDeviceRegProp(struct pipes_struct *p,
+ struct PNP_SetDeviceRegProp *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassInstance(struct pipes_struct *p,
+ struct PNP_GetClassInstance *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_CreateKey(struct pipes_struct *p,
+ struct PNP_CreateKey *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DeleteRegistryKey(struct pipes_struct *p,
+ struct PNP_DeleteRegistryKey *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassCount(struct pipes_struct *p,
+ struct PNP_GetClassCount *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassName(struct pipes_struct *p,
+ struct PNP_GetClassName *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DeleteClassKey(struct pipes_struct *p,
+ struct PNP_DeleteClassKey *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetInterfaceDeviceAlias(struct pipes_struct *p,
+ struct PNP_GetInterfaceDeviceAlias *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetInterfaceDeviceList(struct pipes_struct *p,
+ struct PNP_GetInterfaceDeviceList *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetInterfaceDeviceListSize(struct pipes_struct *p,
+ struct PNP_GetInterfaceDeviceListSize *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RegisterDeviceClassAssociation(struct pipes_struct *p,
+ struct PNP_RegisterDeviceClassAssociation *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_UnregisterDeviceClassAssociation(struct pipes_struct *p,
+ struct PNP_UnregisterDeviceClassAssociation *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassRegProp(struct pipes_struct *p,
+ struct PNP_GetClassRegProp *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetClassRegProp(struct pipes_struct *p,
+ struct PNP_SetClassRegProp *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_CreateDevInst(struct pipes_struct *p,
+ struct PNP_CreateDevInst *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DeviceInstanceAction(struct pipes_struct *p,
+ struct PNP_DeviceInstanceAction *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetDeviceStatus(struct pipes_struct *p,
+ struct PNP_GetDeviceStatus *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetDeviceProblem(struct pipes_struct *p,
+ struct PNP_SetDeviceProblem *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DisableDevInst(struct pipes_struct *p,
+ struct PNP_DisableDevInst *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_UninstallDevInst(struct pipes_struct *p,
+ struct PNP_UninstallDevInst *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_AddID(struct pipes_struct *p,
+ struct PNP_AddID *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RegisterDriver(struct pipes_struct *p,
+ struct PNP_RegisterDriver *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryRemove(struct pipes_struct *p,
+ struct PNP_QueryRemove *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RequestDeviceEject(struct pipes_struct *p,
+ struct PNP_RequestDeviceEject *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_IsDockStationPresent(struct pipes_struct *p,
+ struct PNP_IsDockStationPresent *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RequestEjectPC(struct pipes_struct *p,
+ struct PNP_RequestEjectPC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_AddEmptyLogConf(struct pipes_struct *p,
+ struct PNP_AddEmptyLogConf *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_FreeLogConf(struct pipes_struct *p,
+ struct PNP_FreeLogConf *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetFirstLogConf(struct pipes_struct *p,
+ struct PNP_GetFirstLogConf *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetNextLogConf(struct pipes_struct *p,
+ struct PNP_GetNextLogConf *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetLogConfPriority(struct pipes_struct *p,
+ struct PNP_GetLogConfPriority *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_AddResDes(struct pipes_struct *p,
+ struct PNP_AddResDes *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_FreeResDes(struct pipes_struct *p,
+ struct PNP_FreeResDes *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetNextResDes(struct pipes_struct *p,
+ struct PNP_GetNextResDes *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetResDesData(struct pipes_struct *p,
+ struct PNP_GetResDesData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetResDesDataSize(struct pipes_struct *p,
+ struct PNP_GetResDesDataSize *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_ModifyResDes(struct pipes_struct *p,
+ struct PNP_ModifyResDes *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DetectResourceLimit(struct pipes_struct *p,
+ struct PNP_DetectResourceLimit *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryResConfList(struct pipes_struct *p,
+ struct PNP_QueryResConfList *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetHwProf(struct pipes_struct *p,
+ struct PNP_SetHwProf *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryArbitratorFreeData(struct pipes_struct *p,
+ struct PNP_QueryArbitratorFreeData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryArbitratorFreeSize(struct pipes_struct *p,
+ struct PNP_QueryArbitratorFreeSize *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RunDetection(struct pipes_struct *p,
+ struct PNP_RunDetection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RegisterNotification(struct pipes_struct *p,
+ struct PNP_RegisterNotification *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_UnregisterNotification(struct pipes_struct *p,
+ struct PNP_UnregisterNotification *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetCustomDevProp(struct pipes_struct *p,
+ struct PNP_GetCustomDevProp *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetVersionInternal(struct pipes_struct *p,
+ struct PNP_GetVersionInternal *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetBlockedDriverInfo(struct pipes_struct *p,
+ struct PNP_GetBlockedDriverInfo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetServerSideDeviceInstallFlags(struct pipes_struct *p,
+ struct PNP_GetServerSideDeviceInstallFlags *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_ntsvcs_scompat.c"
diff --git a/source3/rpc_server/rpc_config.c b/source3/rpc_server/rpc_config.c
new file mode 100644
index 0000000..af167d8
--- /dev/null
+++ b/source3/rpc_server/rpc_config.c
@@ -0,0 +1,77 @@
+/*
+ Unix SMB/Netbios implementation.
+ Generic infrastructure for RPC Daemons
+ Copyright (C) Simo Sorce 2011
+ Copyright (C) Andreas Schneider 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/rpc_config.h"
+#include "rpc_server/rpc_server.h"
+#include "lib/param/param.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static struct dcesrv_context_callbacks srv_callbacks = {
+ .log.successful_authz = dcesrv_log_successful_authz,
+ .auth.gensec_prepare = dcesrv_auth_gensec_prepare,
+ .auth.become_root = become_root,
+ .auth.unbecome_root = unbecome_root,
+ .assoc_group.find = dcesrv_assoc_group_find,
+};
+
+static struct dcesrv_context *global_dcesrv_ctx = NULL;
+
+struct dcesrv_context *global_dcesrv_context(void)
+{
+ NTSTATUS status;
+
+ if (global_dcesrv_ctx == NULL) {
+ struct loadparm_context *lp_ctx = NULL;
+
+ DBG_INFO("Initializing DCE/RPC server context\n");
+
+ lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ smb_panic("No memory");
+ }
+
+ /*
+ * Note we MUST use the NULL context here, not the
+ * autofree context, to avoid side effects in forked
+ * children exiting.
+ */
+ status = dcesrv_init_context(global_event_context(),
+ lp_ctx,
+ &srv_callbacks,
+ &global_dcesrv_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb_panic("Failed to init DCE/RPC context");
+ }
+
+ talloc_steal(global_dcesrv_ctx, lp_ctx);
+ }
+
+ return global_dcesrv_ctx;
+}
+
+void global_dcesrv_context_free(void)
+{
+ TALLOC_FREE(global_dcesrv_ctx);
+}
diff --git a/source3/rpc_server/rpc_config.h b/source3/rpc_server/rpc_config.h
new file mode 100644
index 0000000..40d5855
--- /dev/null
+++ b/source3/rpc_server/rpc_config.h
@@ -0,0 +1,30 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SMBD RPC service config
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ * Copyright (C) 2011 Simo Sorce <idra@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_CONFIG_H
+#define _RPC_CONFIG_H
+
+struct dcesrv_context;
+struct dcesrv_context *global_dcesrv_context(void);
+void global_dcesrv_context_free(void);
+
+#endif /* _RPC_CONFIG_H */
diff --git a/source3/rpc_server/rpc_handles.c b/source3/rpc_server/rpc_handles.c
new file mode 100644
index 0000000..928b10e
--- /dev/null
+++ b/source3/rpc_server/rpc_handles.c
@@ -0,0 +1,233 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Jeremy Allison 2001.
+ *
+ * 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/passwd.h" /* uid_wrapper */
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "../librpc/gen_ndr/ndr_samr.h"
+#include "auth.h"
+#include "rpc_server/rpc_pipes.h"
+#include "../libcli/security/security.h"
+#include "lib/tsocket/tsocket.h"
+#include "librpc/ndr/ndr_table.h"
+#include "librpc/rpc/dcesrv_core.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static size_t num_handles = 0;
+
+bool check_open_pipes(void)
+{
+ if (num_handles > 0) {
+ return true;
+ }
+
+ return false;
+}
+
+size_t num_pipe_handles(void)
+{
+ return num_handles;
+}
+
+/****************************************************************************
+ find first available policy slot. creates a policy handle for you.
+
+ If "data_ptr" is given, this must be a talloc'ed object, create_policy_hnd
+ talloc_moves this into the handle. If the policy_hnd is closed,
+ data_ptr is TALLOC_FREE()'ed
+****************************************************************************/
+
+struct hnd_cnt {
+ bool _dummy;
+};
+
+static int hnd_cnt_destructor(struct hnd_cnt *cnt)
+{
+ num_handles--;
+ return 0;
+}
+
+void *create_policy_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ uint8_t handle_type,
+ void *data_ptr)
+{
+ struct dcesrv_handle *rpc_hnd = NULL;
+ struct hnd_cnt *cnt = NULL;
+
+ rpc_hnd = dcesrv_handle_create(p->dce_call, handle_type);
+ if (rpc_hnd == NULL) {
+ return NULL;
+ }
+
+ cnt = talloc_zero(rpc_hnd, struct hnd_cnt);
+ if (cnt == NULL) {
+ TALLOC_FREE(rpc_hnd);
+ return NULL;
+ }
+ talloc_set_destructor(cnt, hnd_cnt_destructor);
+
+ if (data_ptr != NULL) {
+ rpc_hnd->data = talloc_move(rpc_hnd, &data_ptr);
+ }
+
+ *hnd = rpc_hnd->wire_handle;
+
+ num_handles++;
+
+ return rpc_hnd;
+}
+
+/****************************************************************************
+ find policy by handle - internal version.
+****************************************************************************/
+
+static struct dcesrv_handle *find_policy_by_hnd_internal(
+ struct pipes_struct *p,
+ const struct policy_handle *hnd,
+ uint8_t handle_type,
+ void **data_p)
+{
+ struct dcesrv_handle *h = NULL;
+
+ if (data_p) {
+ *data_p = NULL;
+ }
+
+ /*
+ * Do not pass an empty policy_handle to dcesrv_handle_lookup() or
+ * it will create a new empty handle
+ */
+ if (ndr_policy_handle_empty(hnd)) {
+ p->fault_state = DCERPC_FAULT_CONTEXT_MISMATCH;
+ return NULL;
+ }
+
+ /*
+ * Do not pass handle_type to avoid setting the fault_state in the
+ * pipes_struct if the handle type does not match
+ */
+ h = dcesrv_handle_lookup(p->dce_call, hnd, DCESRV_HANDLE_ANY);
+ if (h == NULL) {
+ p->fault_state = DCERPC_FAULT_CONTEXT_MISMATCH;
+ return NULL;
+ }
+
+ if (handle_type != DCESRV_HANDLE_ANY &&
+ h->wire_handle.handle_type != handle_type) {
+ /* Just return NULL, do not set a fault
+ * state in pipes_struct */
+ return NULL;
+ }
+
+ if (data_p) {
+ *data_p = h->data;
+ }
+
+ return h;
+}
+
+/****************************************************************************
+ find policy by handle
+****************************************************************************/
+
+void *_find_policy_by_hnd(struct pipes_struct *p,
+ const struct policy_handle *hnd,
+ uint8_t handle_type,
+ NTSTATUS *pstatus)
+{
+ struct dcesrv_handle *rpc_hnd = NULL;
+ void *data = NULL;
+
+ rpc_hnd = find_policy_by_hnd_internal(p, hnd, handle_type, &data);
+ if (rpc_hnd == NULL) {
+ *pstatus = NT_STATUS_INVALID_HANDLE;
+ return NULL;
+ }
+
+ *pstatus = NT_STATUS_OK;
+ return data;
+}
+
+/****************************************************************************
+ Close a policy.
+****************************************************************************/
+
+bool close_policy_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd)
+{
+ struct dcesrv_handle *rpc_hnd = NULL;
+
+ rpc_hnd = find_policy_by_hnd_internal(p, hnd, DCESRV_HANDLE_ANY, NULL);
+ if (rpc_hnd == NULL) {
+ DEBUG(3, ("Error closing policy (policy not found)\n"));
+ return false;
+ }
+
+ TALLOC_FREE(rpc_hnd);
+
+ return true;
+}
+
+/*******************************************************************
+Shall we allow access to this rpc? Currently this function
+implements the 'restrict anonymous' setting by denying access to
+anonymous users if the restrict anonymous level is > 0. Further work
+will be checking a security descriptor to determine whether a user
+token has enough access to access the pipe.
+********************************************************************/
+
+bool pipe_access_check(struct pipes_struct *p)
+{
+ /* Don't let anonymous users access this RPC if restrict
+ anonymous > 0 */
+
+ if (lp_restrict_anonymous() > 0) {
+
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_auth *auth_state = dce_call->auth_state;
+ enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+ struct auth_session_info *session_info = NULL;
+ enum security_user_level user_level;
+
+ if (!auth_state->auth_finished) {
+ return false;
+ }
+
+ dcesrv_call_auth_info(dce_call, &auth_type, NULL);
+
+ /* schannel, so we must be ok */
+ if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+ return True;
+ }
+
+ session_info = dcesrv_call_session_info(dce_call);
+ user_level = security_session_user_level(session_info, NULL);
+
+ if (user_level < SECURITY_USER) {
+ return False;
+ }
+ }
+
+ return True;
+}
diff --git a/source3/rpc_server/rpc_host.c b/source3/rpc_server/rpc_host.c
new file mode 100644
index 0000000..1e891b4
--- /dev/null
+++ b/source3/rpc_server/rpc_host.c
@@ -0,0 +1,2984 @@
+/*
+ * RPC host
+ *
+ * Implements samba-dcerpcd service.
+ *
+ * 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 binary has two usage modes:
+ *
+ * In the normal case when invoked from smbd or winbind it is given a
+ * directory to scan via --libexec-rpcds and will invoke on demand any
+ * binaries it finds there starting with rpcd_ when a named pipe
+ * connection is requested.
+ *
+ * In the second mode it can be started explicitly from system startup
+ * scripts.
+ *
+ * When Samba is set up as an Active Directory Domain Controller the
+ * normal samba binary overrides and provides DCERPC services, whilst
+ * allowing samba-dcerpcd to provide the services that smbd used to
+ * provide in that set-up, such as SRVSVC.
+ *
+ * The second mode can also be useful for use outside of the Samba framework,
+ * for example, use with the Linux kernel SMB2 server ksmbd. In this mode
+ * it behaves like inetd and listens on sockets on behalf of RPC server
+ * implementations.
+ */
+
+#include "replace.h"
+#include <fnmatch.h>
+#include "lib/cmdline/cmdline.h"
+#include "lib/cmdline/closefrom_except.h"
+#include "source3/include/includes.h"
+#include "source3/include/auth.h"
+#include "rpc_sock_helper.h"
+#include "messages.h"
+#include "lib/util_file.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/util/debug.h"
+#include "lib/util/server_id.h"
+#include "lib/util/util_tdb.h"
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "lib/async_req/async_sock.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/named_pipe_auth/npa_tstream.h"
+#include "librpc/gen_ndr/ndr_rpc_host.h"
+#include "source3/param/loadparm.h"
+#include "source3/lib/global_contexts.h"
+#include "lib/util/strv.h"
+#include "lib/util/pidfile.h"
+#include "source3/rpc_client/cli_pipe.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "nsswitch/winbind_client.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/security_token.h"
+
+extern bool override_logfile;
+
+struct rpc_server;
+struct rpc_work_process;
+
+/*
+ * samba-dcerpcd state to keep track of rpcd_* servers.
+ */
+struct rpc_host {
+ struct messaging_context *msg_ctx;
+ struct rpc_server **servers;
+ struct tdb_wrap *epmdb;
+
+ int worker_stdin[2];
+
+ bool np_helper;
+
+ /*
+ * If we're started with --np-helper but nobody contacts us,
+ * we need to exit after a while. This will be deleted once
+ * the first real client connects and our self-exit mechanism
+ * when we don't have any worker processes left kicks in.
+ */
+ struct tevent_timer *np_helper_shutdown;
+};
+
+/*
+ * Map a RPC interface to a name. Used when filling the endpoint
+ * mapper database
+ */
+struct rpc_host_iface_name {
+ struct ndr_syntax_id iface;
+ char *name;
+};
+
+/*
+ * rpc_host representation for listening sockets. ncacn_ip_tcp might
+ * listen on multiple explicit IPs, all with the same port.
+ */
+struct rpc_host_endpoint {
+ struct rpc_server *server;
+ struct dcerpc_binding *binding;
+ struct ndr_syntax_id *interfaces;
+ int *fds;
+ size_t num_fds;
+};
+
+/*
+ * Staging area until we sent the socket plus bind to the helper
+ */
+struct rpc_host_pending_client {
+ struct rpc_host_pending_client *prev, *next;
+
+ /*
+ * Pointer for the destructor to remove us from the list of
+ * pending clients
+ */
+ struct rpc_server *server;
+
+ /*
+ * Waiter for client exit before a helper accepted the request
+ */
+ struct tevent_req *hangup_wait;
+
+ /*
+ * Info to pick the worker
+ */
+ struct ncacn_packet *bind_pkt;
+
+ /*
+ * This is what we send down to the worker
+ */
+ int sock;
+ struct rpc_host_client *client;
+};
+
+/*
+ * Representation of one worker process. For each rpcd_* executable
+ * there will be more of than one of these.
+ */
+struct rpc_work_process {
+ pid_t pid;
+
+ /*
+ * !available means:
+ *
+ * Worker forked but did not send its initial status yet (not
+ * yet initialized)
+ *
+ * Worker died, but we did not receive SIGCHLD yet. We noticed
+ * it because we couldn't send it a message.
+ */
+ bool available;
+
+ /*
+ * Incremented by us when sending a client, decremented by
+ * MSG_RPC_HOST_WORKER_STATUS sent by workers whenever a
+ * client exits.
+ */
+ uint32_t num_associations;
+ uint32_t num_connections;
+
+ /*
+ * Send SHUTDOWN to an idle child after a while
+ */
+ struct tevent_timer *exit_timer;
+};
+
+/*
+ * State for a set of running instances of an rpcd_* server executable
+ */
+struct rpc_server {
+ struct rpc_host *host;
+ /*
+ * Index into the rpc_host_state->servers array
+ */
+ uint32_t server_index;
+
+ const char *rpc_server_exe;
+
+ struct rpc_host_endpoint **endpoints;
+ struct rpc_host_iface_name *iface_names;
+
+ size_t max_workers;
+ size_t idle_seconds;
+
+ /*
+ * "workers" can be larger than "max_workers": Internal
+ * connections require an idle worker to avoid deadlocks
+ * between RPC servers: netlogon requires samr, everybody
+ * requires winreg. And if a deep call in netlogon asks for a
+ * samr connection, this must never end up in the same
+ * process. named_pipe_auth_req_info8->need_idle_server is set
+ * in those cases.
+ */
+ struct rpc_work_process *workers;
+
+ struct rpc_host_pending_client *pending_clients;
+};
+
+struct rpc_server_get_endpoints_state {
+ char **argl;
+ char *ncalrpc_endpoint;
+ enum dcerpc_transport_t only_transport;
+
+ struct rpc_host_iface_name *iface_names;
+ struct rpc_host_endpoint **endpoints;
+
+ unsigned long num_workers;
+ unsigned long idle_seconds;
+};
+
+static void rpc_server_get_endpoints_done(struct tevent_req *subreq);
+
+/**
+ * @brief Query interfaces from an rpcd helper
+ *
+ * Spawn a rpcd helper, ask it for the interfaces it serves via
+ * --list-interfaces, parse the output
+ *
+ * @param[in] mem_ctx Memory context for the tevent_req
+ * @param[in] ev Event context to run this on
+ * @param[in] rpc_server_exe Binary to ask with --list-interfaces
+ * @param[in] only_transport Filter out anything but this
+ * @return The tevent_req representing this process
+ */
+
+static struct tevent_req *rpc_server_get_endpoints_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *rpc_server_exe,
+ enum dcerpc_transport_t only_transport)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct rpc_server_get_endpoints_state *state = NULL;
+ const char *progname = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct rpc_server_get_endpoints_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->only_transport = only_transport;
+
+ progname = strrchr(rpc_server_exe, '/');
+ if (progname != NULL) {
+ progname += 1;
+ } else {
+ progname = rpc_server_exe;
+ }
+
+ state->ncalrpc_endpoint = talloc_strdup(state, progname);
+ if (tevent_req_nomem(state->ncalrpc_endpoint, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->argl = talloc_array(state, char *, 4);
+ if (tevent_req_nomem(state->argl, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->argl = str_list_make_empty(state);
+ str_list_add_printf(&state->argl, "%s", rpc_server_exe);
+ str_list_add_printf(&state->argl, "--list-interfaces");
+ str_list_add_printf(
+ &state->argl, "--configfile=%s", get_dyn_CONFIGFILE());
+
+ if (tevent_req_nomem(state->argl, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = file_ploadv_send(state, ev, state->argl, 65536);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_server_get_endpoints_done, req);
+ return req;
+}
+
+/*
+ * Parse a line of format
+ *
+ * 338cd001-2244-31f1-aaaa-900038001003/0x00000001 winreg
+ *
+ * and add it to the "piface_names" array.
+ */
+
+static struct rpc_host_iface_name *rpc_exe_parse_iface_line(
+ TALLOC_CTX *mem_ctx,
+ struct rpc_host_iface_name **piface_names,
+ const char *line)
+{
+ struct rpc_host_iface_name *iface_names = *piface_names;
+ struct rpc_host_iface_name *tmp = NULL, *result = NULL;
+ size_t i, num_ifaces = talloc_array_length(iface_names);
+ struct ndr_syntax_id iface;
+ char *name = NULL;
+ bool ok;
+
+ ok = ndr_syntax_id_from_string(line, &iface);
+ if (!ok) {
+ DBG_WARNING("ndr_syntax_id_from_string() failed for: [%s]\n",
+ line);
+ return NULL;
+ }
+
+ name = strchr(line, ' ');
+ if (name == NULL) {
+ return NULL;
+ }
+ name += 1;
+
+ for (i=0; i<num_ifaces; i++) {
+ result = &iface_names[i];
+
+ if (ndr_syntax_id_equal(&result->iface, &iface)) {
+ return result;
+ }
+ }
+
+ if (num_ifaces + 1 < num_ifaces) {
+ return NULL;
+ }
+
+ name = talloc_strdup(mem_ctx, name);
+ if (name == NULL) {
+ return NULL;
+ }
+
+ tmp = talloc_realloc(
+ mem_ctx,
+ iface_names,
+ struct rpc_host_iface_name,
+ num_ifaces + 1);
+ if (tmp == NULL) {
+ TALLOC_FREE(name);
+ return NULL;
+ }
+ iface_names = tmp;
+
+ result = &iface_names[num_ifaces];
+
+ *result = (struct rpc_host_iface_name) {
+ .iface = iface,
+ .name = talloc_move(iface_names, &name),
+ };
+
+ *piface_names = iface_names;
+
+ return result;
+}
+
+static struct rpc_host_iface_name *rpc_host_iface_names_find(
+ struct rpc_host_iface_name *iface_names,
+ const struct ndr_syntax_id *iface)
+{
+ size_t i, num_iface_names = talloc_array_length(iface_names);
+
+ for (i=0; i<num_iface_names; i++) {
+ struct rpc_host_iface_name *iface_name = &iface_names[i];
+
+ if (ndr_syntax_id_equal(iface, &iface_name->iface)) {
+ return iface_name;
+ }
+ }
+
+ return NULL;
+}
+
+static bool dcerpc_binding_same_endpoint(
+ const struct dcerpc_binding *b1, const struct dcerpc_binding *b2)
+{
+ enum dcerpc_transport_t t1 = dcerpc_binding_get_transport(b1);
+ enum dcerpc_transport_t t2 = dcerpc_binding_get_transport(b2);
+ const char *e1 = NULL, *e2 = NULL;
+ int cmp;
+
+ if (t1 != t2) {
+ return false;
+ }
+
+ e1 = dcerpc_binding_get_string_option(b1, "endpoint");
+ e2 = dcerpc_binding_get_string_option(b2, "endpoint");
+
+ if ((e1 == NULL) && (e2 == NULL)) {
+ return true;
+ }
+ if ((e1 == NULL) || (e2 == NULL)) {
+ return false;
+ }
+ cmp = strcmp(e1, e2);
+ return (cmp == 0);
+}
+
+/**
+ * @brief Filter whether we want to serve an endpoint
+ *
+ * samba-dcerpcd might want to serve all endpoints a rpcd reported to
+ * us via --list-interfaces.
+ *
+ * In member mode, we only serve named pipes. Indicated by NCACN_NP
+ * passed in via "only_transport".
+ *
+ * @param[in] binding Which binding is in question?
+ * @param[in] only_transport Exclusive transport to serve
+ * @return Do we want to serve "binding" from samba-dcerpcd?
+ */
+
+static bool rpc_host_serve_endpoint(
+ struct dcerpc_binding *binding,
+ enum dcerpc_transport_t only_transport)
+{
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(binding);
+
+ if (only_transport == NCA_UNKNOWN) {
+ /* no filter around */
+ return true;
+ }
+
+ if (transport != only_transport) {
+ /* filter out */
+ return false;
+ }
+
+ return true;
+}
+
+static struct rpc_host_endpoint *rpc_host_endpoint_find(
+ struct rpc_server_get_endpoints_state *state,
+ const char *binding_string)
+{
+ size_t i, num_endpoints = talloc_array_length(state->endpoints);
+ struct rpc_host_endpoint **tmp = NULL, *ep = NULL;
+ enum dcerpc_transport_t transport;
+ NTSTATUS status;
+ bool serve_this;
+
+ ep = talloc_zero(state, struct rpc_host_endpoint);
+ if (ep == NULL) {
+ goto fail;
+ }
+
+ status = dcerpc_parse_binding(ep, binding_string, &ep->binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_parse_binding(%s) failed: %s\n",
+ binding_string,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ serve_this = rpc_host_serve_endpoint(
+ ep->binding, state->only_transport);
+ if (!serve_this) {
+ goto fail;
+ }
+
+ transport = dcerpc_binding_get_transport(ep->binding);
+
+ if (transport == NCALRPC) {
+ const char *ncalrpc_sock = dcerpc_binding_get_string_option(
+ ep->binding, "endpoint");
+
+ if (ncalrpc_sock == NULL) {
+ /*
+ * generic ncalrpc:, set program-specific
+ * socket name. epmapper will redirect clients
+ * properly.
+ */
+ status = dcerpc_binding_set_string_option(
+ ep->binding,
+ "endpoint",
+ state->ncalrpc_endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_binding_set_string_option "
+ "failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+ }
+
+ for (i=0; i<num_endpoints; i++) {
+
+ bool ok = dcerpc_binding_same_endpoint(
+ ep->binding, state->endpoints[i]->binding);
+
+ if (ok) {
+ TALLOC_FREE(ep);
+ return state->endpoints[i];
+ }
+ }
+
+ if (num_endpoints + 1 < num_endpoints) {
+ goto fail;
+ }
+
+ tmp = talloc_realloc(
+ state,
+ state->endpoints,
+ struct rpc_host_endpoint *,
+ num_endpoints + 1);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ state->endpoints = tmp;
+ state->endpoints[num_endpoints] = talloc_move(state->endpoints, &ep);
+
+ return state->endpoints[num_endpoints];
+fail:
+ TALLOC_FREE(ep);
+ return NULL;
+}
+
+static bool ndr_interfaces_add_unique(
+ TALLOC_CTX *mem_ctx,
+ struct ndr_syntax_id **pifaces,
+ const struct ndr_syntax_id *iface)
+{
+ struct ndr_syntax_id *ifaces = *pifaces;
+ size_t i, num_ifaces = talloc_array_length(ifaces);
+
+ for (i=0; i<num_ifaces; i++) {
+ if (ndr_syntax_id_equal(iface, &ifaces[i])) {
+ return true;
+ }
+ }
+
+ if (num_ifaces + 1 < num_ifaces) {
+ return false;
+ }
+ ifaces = talloc_realloc(
+ mem_ctx,
+ ifaces,
+ struct ndr_syntax_id,
+ num_ifaces + 1);
+ if (ifaces == NULL) {
+ return false;
+ }
+ ifaces[num_ifaces] = *iface;
+
+ *pifaces = ifaces;
+ return true;
+}
+
+/*
+ * Read the text reply from the rpcd_* process telling us what
+ * endpoints it will serve when asked with --list-interfaces.
+ */
+static void rpc_server_get_endpoints_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_server_get_endpoints_state *state = tevent_req_data(
+ req, struct rpc_server_get_endpoints_state);
+ struct rpc_host_iface_name *iface = NULL;
+ uint8_t *buf = NULL;
+ size_t buflen;
+ char **lines = NULL;
+ int ret, i, num_lines;
+
+ ret = file_ploadv_recv(subreq, state, &buf);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ buflen = talloc_get_size(buf);
+ if (buflen == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ lines = file_lines_parse((char *)buf, buflen, &num_lines, state);
+ if (tevent_req_nomem(lines, req)) {
+ return;
+ }
+
+ if (num_lines < 2) {
+ DBG_DEBUG("Got %d lines, expected at least 2\n", num_lines);
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ state->num_workers = smb_strtoul(
+ lines[0], NULL, 10, &ret, SMB_STR_FULL_STR_CONV);
+ if (ret != 0) {
+ DBG_DEBUG("Could not parse num_workers(%s): %s\n",
+ lines[0],
+ strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+ /*
+ * We need to limit the number of workers in order
+ * to put the worker index into a 16-bit space,
+ * in order to use a 16-bit association group space
+ * per worker.
+ */
+ if (state->num_workers > 65536) {
+ state->num_workers = 65536;
+ }
+
+ state->idle_seconds = smb_strtoul(
+ lines[1], NULL, 10, &ret, SMB_STR_FULL_STR_CONV);
+ if (ret != 0) {
+ DBG_DEBUG("Could not parse idle_seconds (%s): %s\n",
+ lines[1],
+ strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DBG_DEBUG("num_workers=%lu, idle_seconds=%lu for %s\n",
+ state->num_workers,
+ state->idle_seconds,
+ state->argl[0]);
+
+ for (i=2; i<num_lines; i++) {
+ char *line = lines[i];
+ struct rpc_host_endpoint *endpoint = NULL;
+ bool ok;
+
+ if (line[0] != ' ') {
+ iface = rpc_exe_parse_iface_line(
+ state, &state->iface_names, line);
+ if (iface == NULL) {
+ DBG_WARNING(
+ "rpc_exe_parse_iface_line failed "
+ "for: [%s] from %s\n",
+ line,
+ state->argl[0]);
+ tevent_req_oom(req);
+ return;
+ }
+ continue;
+ }
+
+ if (iface == NULL) {
+ DBG_DEBUG("Interface GUID line missing\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ endpoint = rpc_host_endpoint_find(state, line+1);
+ if (endpoint == NULL) {
+ DBG_DEBUG("rpc_host_endpoint_find for %s failed\n",
+ line+1);
+ continue;
+ }
+
+ ok = ndr_interfaces_add_unique(
+ endpoint,
+ &endpoint->interfaces,
+ &iface->iface);
+ if (!ok) {
+ DBG_DEBUG("ndr_interfaces_add_unique failed\n");
+ tevent_req_oom(req);
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+/**
+ * @brief Receive output from --list-interfaces
+ *
+ * @param[in] req The async req that just finished
+ * @param[in] mem_ctx Where to put the output on
+ * @param[out] endpoints The endpoints to be listened on
+ * @param[out] iface_names Annotation for epm_Lookup's epm_entry_t
+ * @return 0/errno
+ */
+static int rpc_server_get_endpoints_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_host_endpoint ***endpoints,
+ struct rpc_host_iface_name **iface_names,
+ size_t *num_workers,
+ size_t *idle_seconds)
+{
+ struct rpc_server_get_endpoints_state *state = tevent_req_data(
+ req, struct rpc_server_get_endpoints_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *endpoints = talloc_move(mem_ctx, &state->endpoints);
+ *iface_names = talloc_move(mem_ctx, &state->iface_names);
+ *num_workers = state->num_workers;
+ *idle_seconds = state->idle_seconds;
+ tevent_req_received(req);
+ return 0;
+}
+
+/*
+ * For NCACN_NP we get the named pipe auth info from smbd, if a client
+ * comes in via TCP or NCALPRC we need to invent it ourselves with
+ * anonymous session info.
+ */
+
+static NTSTATUS rpc_host_generate_npa_info8_from_sock(
+ TALLOC_CTX *mem_ctx,
+ enum dcerpc_transport_t transport,
+ int sock,
+ const struct samba_sockaddr *peer_addr,
+ struct named_pipe_auth_req_info8 **pinfo8)
+{
+ struct named_pipe_auth_req_info8 *info8 = NULL;
+ struct samba_sockaddr local_addr = {
+ .sa_socklen = sizeof(struct sockaddr_storage),
+ };
+ struct tsocket_address *taddr = NULL;
+ char *remote_client_name = NULL;
+ char *remote_client_addr = NULL;
+ char *local_server_name = NULL;
+ char *local_server_addr = NULL;
+ char *(*tsocket_address_to_name_fn)(
+ const struct tsocket_address *addr,
+ TALLOC_CTX *mem_ctx) = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ int ret;
+
+ /*
+ * For NCACN_NP we get the npa info from smbd
+ */
+ SMB_ASSERT((transport == NCACN_IP_TCP) || (transport == NCALRPC));
+
+ tsocket_address_to_name_fn = (transport == NCACN_IP_TCP) ?
+ tsocket_address_inet_addr_string : tsocket_address_unix_path;
+
+ info8 = talloc_zero(mem_ctx, struct named_pipe_auth_req_info8);
+ if (info8 == NULL) {
+ goto fail;
+ }
+ info8->session_info =
+ talloc_zero(info8, struct auth_session_info_transport);
+ if (info8->session_info == NULL) {
+ goto fail;
+ }
+
+ status = make_session_info_anonymous(
+ info8->session_info,
+ &info8->session_info->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("make_session_info_anonymous failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ret = tsocket_address_bsd_from_samba_sockaddr(info8,
+ peer_addr,
+ &taddr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("tsocket_address_bsd_from_samba_sockaddr failed: "
+ "%s\n",
+ strerror(errno));
+ goto fail;
+ }
+ remote_client_addr = tsocket_address_to_name_fn(taddr, info8);
+ if (remote_client_addr == NULL) {
+ DBG_DEBUG("tsocket_address_to_name_fn failed\n");
+ goto nomem;
+ }
+ TALLOC_FREE(taddr);
+
+ remote_client_name = talloc_strdup(info8, remote_client_addr);
+ if (remote_client_name == NULL) {
+ DBG_DEBUG("talloc_strdup failed\n");
+ goto nomem;
+ }
+
+ if (transport == NCACN_IP_TCP) {
+ bool ok = samba_sockaddr_get_port(peer_addr,
+ &info8->remote_client_port);
+ if (!ok) {
+ DBG_DEBUG("samba_sockaddr_get_port failed\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+
+ ret = getsockname(sock, &local_addr.u.sa, &local_addr.sa_socklen);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("getsockname failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ret = tsocket_address_bsd_from_samba_sockaddr(info8,
+ &local_addr,
+ &taddr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("tsocket_address_bsd_from_samba_sockaddr failed: "
+ "%s\n",
+ strerror(errno));
+ goto fail;
+ }
+ local_server_addr = tsocket_address_to_name_fn(taddr, info8);
+ if (local_server_addr == NULL) {
+ DBG_DEBUG("tsocket_address_to_name_fn failed\n");
+ goto nomem;
+ }
+ TALLOC_FREE(taddr);
+
+ local_server_name = talloc_strdup(info8, local_server_addr);
+ if (local_server_name == NULL) {
+ DBG_DEBUG("talloc_strdup failed\n");
+ goto nomem;
+ }
+
+ if (transport == NCACN_IP_TCP) {
+ bool ok = samba_sockaddr_get_port(&local_addr,
+ &info8->local_server_port);
+ if (!ok) {
+ DBG_DEBUG("samba_sockaddr_get_port failed\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+
+ if (transport == NCALRPC) {
+ uid_t uid;
+ gid_t gid;
+
+ ret = getpeereid(sock, &uid, &gid);
+ if (ret < 0) {
+ status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("getpeereid failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (uid == sec_initial_uid()) {
+
+ /*
+ * Indicate "root" to gensec
+ */
+
+ TALLOC_FREE(remote_client_addr);
+ TALLOC_FREE(remote_client_name);
+
+ ret = tsocket_address_unix_from_path(
+ info8,
+ AS_SYSTEM_MAGIC_PATH_TOKEN,
+ &taddr);
+ if (ret == -1) {
+ DBG_DEBUG("tsocket_address_unix_from_path "
+ "failed\n");
+ goto nomem;
+ }
+
+ remote_client_addr =
+ tsocket_address_unix_path(taddr, info8);
+ if (remote_client_addr == NULL) {
+ DBG_DEBUG("tsocket_address_unix_path "
+ "failed\n");
+ goto nomem;
+ }
+ remote_client_name =
+ talloc_strdup(info8, remote_client_addr);
+ if (remote_client_name == NULL) {
+ DBG_DEBUG("talloc_strdup failed\n");
+ goto nomem;
+ }
+ }
+ }
+
+ info8->remote_client_addr = remote_client_addr;
+ info8->remote_client_name = remote_client_name;
+ info8->local_server_addr = local_server_addr;
+ info8->local_server_name = local_server_name;
+
+ *pinfo8 = info8;
+ return NT_STATUS_OK;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+fail:
+ TALLOC_FREE(info8);
+ return status;
+}
+
+struct rpc_host_bind_read_state {
+ struct tevent_context *ev;
+
+ int sock;
+ struct tstream_context *plain;
+ struct tstream_context *npa_stream;
+
+ struct ncacn_packet *pkt;
+ struct rpc_host_client *client;
+};
+
+static void rpc_host_bind_read_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state);
+static void rpc_host_bind_read_got_npa(struct tevent_req *subreq);
+static void rpc_host_bind_read_got_bind(struct tevent_req *subreq);
+
+/*
+ * Wait for a bind packet from a client.
+ */
+static struct tevent_req *rpc_host_bind_read_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum dcerpc_transport_t transport,
+ int *psock,
+ const struct samba_sockaddr *peer_addr)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct rpc_host_bind_read_state *state = NULL;
+ int rc, sock_dup;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct rpc_host_bind_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ state->sock = *psock;
+ *psock = -1;
+
+ tevent_req_set_cleanup_fn(req, rpc_host_bind_read_cleanup);
+
+ state->client = talloc_zero(state, struct rpc_host_client);
+ if (tevent_req_nomem(state->client, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Dup the socket to read the first RPC packet:
+ * tstream_bsd_existing_socket() takes ownership with
+ * autoclose, but we need to send "sock" down to our worker
+ * process later.
+ */
+ sock_dup = dup(state->sock);
+ if (sock_dup == -1) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ rc = tstream_bsd_existing_socket(state, sock_dup, &state->plain);
+ if (rc == -1) {
+ DBG_DEBUG("tstream_bsd_existing_socket failed: %s\n",
+ strerror(errno));
+ tevent_req_error(req, errno);
+ close(sock_dup);
+ return tevent_req_post(req, ev);
+ }
+ /* as server we want to fail early */
+ tstream_bsd_fail_readv_first_error(state->plain, true);
+
+ if (transport == NCACN_NP) {
+ subreq = tstream_npa_accept_existing_send(
+ state,
+ ev,
+ state->plain,
+ FILE_TYPE_MESSAGE_MODE_PIPE,
+ 0xff | 0x0400 | 0x0100,
+ 4096);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, rpc_host_bind_read_got_npa, req);
+ return req;
+ }
+
+ status = rpc_host_generate_npa_info8_from_sock(
+ state->client,
+ transport,
+ state->sock,
+ peer_addr,
+ &state->client->npa_info8);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_read_ncacn_packet_send(state, ev, state->plain);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_host_bind_read_got_bind, req);
+ return req;
+}
+
+static void rpc_host_bind_read_cleanup(
+ struct tevent_req *req, enum tevent_req_state req_state)
+{
+ struct rpc_host_bind_read_state *state = tevent_req_data(
+ req, struct rpc_host_bind_read_state);
+
+ if ((req_state == TEVENT_REQ_RECEIVED) && (state->sock != -1)) {
+ close(state->sock);
+ state->sock = -1;
+ }
+}
+
+static void rpc_host_bind_read_got_npa(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_host_bind_read_state *state = tevent_req_data(
+ req, struct rpc_host_bind_read_state);
+ struct named_pipe_auth_req_info8 *info8 = NULL;
+ int ret, err;
+
+ ret = tstream_npa_accept_existing_recv(subreq,
+ &err,
+ state,
+ &state->npa_stream,
+ &info8,
+ NULL, /* transport */
+ NULL, /* remote_client_addr */
+ NULL, /* remote_client_name */
+ NULL, /* local_server_addr */
+ NULL, /* local_server_name */
+ NULL); /* session_info */
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ state->client->npa_info8 = talloc_move(state->client, &info8);
+
+ subreq = dcerpc_read_ncacn_packet_send(
+ state, state->ev, state->npa_stream);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, rpc_host_bind_read_got_bind, req);
+}
+
+static void rpc_host_bind_read_got_bind(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_host_bind_read_state *state = tevent_req_data(
+ req, struct rpc_host_bind_read_state);
+ struct ncacn_packet *pkt = NULL;
+ NTSTATUS status;
+
+ status = dcerpc_read_ncacn_packet_recv(
+ subreq,
+ state->client,
+ &pkt,
+ &state->client->bind_packet);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_read_ncacn_packet_recv failed: %s\n",
+ nt_errstr(status));
+ tevent_req_error(req, EINVAL); /* TODO */
+ return;
+ }
+ state->pkt = talloc_move(state, &pkt);
+
+ tevent_req_done(req);
+}
+
+static int rpc_host_bind_read_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ int *sock,
+ struct rpc_host_client **client,
+ struct ncacn_packet **bind_pkt)
+{
+ struct rpc_host_bind_read_state *state = tevent_req_data(
+ req, struct rpc_host_bind_read_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *sock = state->sock;
+ state->sock = -1;
+
+ *client = talloc_move(mem_ctx, &state->client);
+ *bind_pkt = talloc_move(mem_ctx, &state->pkt);
+ tevent_req_received(req);
+ return 0;
+}
+
+/*
+ * Start the given rpcd_* binary.
+ */
+static int rpc_host_exec_worker(struct rpc_server *server, size_t idx)
+{
+ struct rpc_work_process *worker = &server->workers[idx];
+ char **argv = NULL;
+ int ret = ENOMEM;
+
+ argv = str_list_make_empty(server);
+ str_list_add_printf(
+ &argv, "%s", server->rpc_server_exe);
+ str_list_add_printf(
+ &argv, "--configfile=%s", get_dyn_CONFIGFILE());
+ str_list_add_printf(
+ &argv, "--worker-group=%"PRIu32, server->server_index);
+ str_list_add_printf(
+ &argv, "--worker-index=%zu", idx);
+ str_list_add_printf(
+ &argv, "--debuglevel=%d", debuglevel_get_class(DBGC_RPC_SRV));
+ if (!is_default_dyn_LOGFILEBASE()) {
+ str_list_add_printf(
+ &argv, "--log-basename=%s", get_dyn_LOGFILEBASE());
+ }
+ if (argv == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ worker->pid = fork();
+ if (worker->pid == -1) {
+ ret = errno;
+ goto fail;
+ }
+ if (worker->pid == 0) {
+ /* Child. */
+ close(server->host->worker_stdin[1]);
+ ret = dup2(server->host->worker_stdin[0], 0);
+ if (ret != 0) {
+ exit(1);
+ }
+ execv(argv[0], argv);
+ _exit(1);
+ }
+
+ DBG_DEBUG("Creating worker %s for index %zu: pid=%d\n",
+ server->rpc_server_exe,
+ idx,
+ (int)worker->pid);
+
+ ret = 0;
+fail:
+ TALLOC_FREE(argv);
+ return ret;
+}
+
+/*
+ * Find an rpcd_* worker for an external client, respect server->max_workers
+ */
+static struct rpc_work_process *rpc_host_find_worker(struct rpc_server *server)
+{
+ struct rpc_work_process *worker = NULL;
+ struct rpc_work_process *perfect_worker = NULL;
+ struct rpc_work_process *best_worker = NULL;
+ size_t empty_slot = SIZE_MAX;
+ size_t i;
+
+ for (i=0; i<server->max_workers; i++) {
+ worker = &server->workers[i];
+
+ if (worker->pid == -1) {
+ empty_slot = MIN(empty_slot, i);
+ continue;
+ }
+ if (!worker->available) {
+ continue;
+ }
+ if (worker->num_associations == 0) {
+ /*
+ * We have an idle worker...
+ */
+ perfect_worker = worker;
+ break;
+ }
+ if (best_worker == NULL) {
+ /*
+ * It's busy, but the best so far...
+ */
+ best_worker = worker;
+ continue;
+ }
+ if (worker->num_associations < best_worker->num_associations) {
+ /*
+ * It's also busy, but has less association groups
+ * (logical clients)
+ */
+ best_worker = worker;
+ continue;
+ }
+ if (worker->num_associations > best_worker->num_associations) {
+ /*
+ * It's not better
+ */
+ continue;
+ }
+ /*
+ * Ok, with the same number of association groups
+ * we pick the one with the lowest number of connections
+ */
+ if (worker->num_connections < best_worker->num_connections) {
+ best_worker = worker;
+ continue;
+ }
+ }
+
+ if (perfect_worker != NULL) {
+ return perfect_worker;
+ }
+
+ if (empty_slot < SIZE_MAX) {
+ int ret = rpc_host_exec_worker(server, empty_slot);
+ if (ret != 0) {
+ DBG_WARNING("Could not fork worker: %s\n",
+ strerror(ret));
+ }
+ return NULL;
+ }
+
+ if (best_worker != NULL) {
+ return best_worker;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find an rpcd_* worker for an internal connection, possibly go beyond
+ * server->max_workers
+ */
+static struct rpc_work_process *rpc_host_find_idle_worker(
+ struct rpc_server *server)
+{
+ struct rpc_work_process *worker = NULL, *tmp = NULL;
+ size_t i, num_workers = talloc_array_length(server->workers);
+ size_t empty_slot = SIZE_MAX;
+ int ret;
+
+ for (i=server->max_workers; i<num_workers; i++) {
+ worker = &server->workers[i];
+
+ if (worker->pid == -1) {
+ empty_slot = MIN(empty_slot, i);
+ continue;
+ }
+ if (!worker->available) {
+ continue;
+ }
+ if (worker->num_associations == 0) {
+ return &server->workers[i];
+ }
+ }
+
+ if (empty_slot < SIZE_MAX) {
+ ret = rpc_host_exec_worker(server, empty_slot);
+ if (ret != 0) {
+ DBG_WARNING("Could not fork worker: %s\n",
+ strerror(ret));
+ }
+ return NULL;
+ }
+
+ /*
+ * All workers are busy. We need to expand the number of
+ * workers because we were asked for an idle worker.
+ */
+ if (num_workers >= UINT16_MAX) {
+ /*
+ * The worker index would not fit into 16-bits
+ */
+ return NULL;
+ }
+ tmp = talloc_realloc(
+ server,
+ server->workers,
+ struct rpc_work_process,
+ num_workers+1);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ server->workers = tmp;
+
+ server->workers[num_workers] = (struct rpc_work_process) { .pid=-1, };
+
+ ret = rpc_host_exec_worker(server, num_workers);
+ if (ret != 0) {
+ DBG_WARNING("Could not exec worker: %s\n", strerror(ret));
+ }
+
+ return NULL;
+}
+
+/*
+ * Find an rpcd_* process to talk to. Start a new one if necessary.
+ */
+static void rpc_host_distribute_clients(struct rpc_server *server)
+{
+ struct rpc_work_process *worker = NULL;
+ struct rpc_host_pending_client *pending_client = NULL;
+ uint32_t assoc_group_id;
+ DATA_BLOB blob;
+ struct iovec iov;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ const char *client_type = NULL;
+
+again:
+ pending_client = server->pending_clients;
+ if (pending_client == NULL) {
+ DBG_DEBUG("No pending clients\n");
+ return;
+ }
+
+ assoc_group_id = pending_client->bind_pkt->u.bind.assoc_group_id;
+
+ if (assoc_group_id != 0) {
+ size_t num_workers = talloc_array_length(server->workers);
+ uint16_t worker_index = assoc_group_id >> 16;
+
+ client_type = "associated";
+
+ if (worker_index >= num_workers) {
+ DBG_DEBUG("Invalid assoc group id %"PRIu32"\n",
+ assoc_group_id);
+ goto done;
+ }
+ worker = &server->workers[worker_index];
+
+ if ((worker->pid == -1) || !worker->available) {
+ DBG_DEBUG("Requested worker index %"PRIu16": "
+ "pid=%d, available=%d\n",
+ worker_index,
+ (int)worker->pid,
+ (int)worker->available);
+ /*
+ * Pick a random one for a proper bind nack
+ */
+ client_type = "associated+lost";
+ worker = rpc_host_find_worker(server);
+ }
+ } else {
+ struct auth_session_info_transport *session_info =
+ pending_client->client->npa_info8->session_info;
+ uint32_t flags = 0;
+ bool found;
+
+ client_type = "new";
+
+ found = security_token_find_npa_flags(
+ session_info->session_info->security_token,
+ &flags);
+
+ /* fresh assoc group requested */
+ if (found & (flags & SAMBA_NPA_FLAGS_NEED_IDLE)) {
+ client_type = "new+exclusive";
+ worker = rpc_host_find_idle_worker(server);
+ } else {
+ client_type = "new";
+ worker = rpc_host_find_worker(server);
+ }
+ }
+
+ if (worker == NULL) {
+ DBG_DEBUG("No worker found for %s client\n", client_type);
+ return;
+ }
+
+ DLIST_REMOVE(server->pending_clients, pending_client);
+
+ ndr_err = ndr_push_struct_blob(
+ &blob,
+ pending_client,
+ pending_client->client,
+ (ndr_push_flags_fn_t)ndr_push_rpc_host_client);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("ndr_push_rpc_host_client failed: %s\n",
+ ndr_errstr(ndr_err));
+ goto done;
+ }
+
+ DBG_INFO("Sending %s client %s to %d with "
+ "%"PRIu32" associations and %"PRIu32" connections\n",
+ client_type,
+ server->rpc_server_exe,
+ worker->pid,
+ worker->num_associations,
+ worker->num_connections);
+
+ iov = (struct iovec) {
+ .iov_base = blob.data, .iov_len = blob.length,
+ };
+
+ status = messaging_send_iov(
+ server->host->msg_ctx,
+ pid_to_procid(worker->pid),
+ MSG_RPC_HOST_NEW_CLIENT,
+ &iov,
+ 1,
+ &pending_client->sock,
+ 1);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DBG_DEBUG("worker %d died, sigchld not yet received?\n",
+ worker->pid);
+ DLIST_ADD(server->pending_clients, pending_client);
+ worker->available = false;
+ goto again;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("messaging_send_iov failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (assoc_group_id == 0) {
+ worker->num_associations += 1;
+ }
+ worker->num_connections += 1;
+ TALLOC_FREE(worker->exit_timer);
+
+ TALLOC_FREE(server->host->np_helper_shutdown);
+
+done:
+ TALLOC_FREE(pending_client);
+}
+
+static int rpc_host_pending_client_destructor(
+ struct rpc_host_pending_client *p)
+{
+ TALLOC_FREE(p->hangup_wait);
+ if (p->sock != -1) {
+ close(p->sock);
+ p->sock = -1;
+ }
+ DLIST_REMOVE(p->server->pending_clients, p);
+ return 0;
+}
+
+/*
+ * Exception condition handler before rpcd_* worker
+ * is handling the socket. Either the client exited or
+ * sent unexpected data after the initial bind.
+ */
+static void rpc_host_client_exited(struct tevent_req *subreq)
+{
+ struct rpc_host_pending_client *pending = tevent_req_callback_data(
+ subreq, struct rpc_host_pending_client);
+ bool ok;
+ int err;
+
+ ok = wait_for_read_recv(subreq, &err);
+
+ TALLOC_FREE(subreq);
+ pending->hangup_wait = NULL;
+
+ if (ok) {
+ DBG_DEBUG("client on sock %d sent data\n", pending->sock);
+ } else {
+ DBG_DEBUG("client exited with %s\n", strerror(err));
+ }
+ TALLOC_FREE(pending);
+}
+
+struct rpc_iface_binding_map {
+ struct ndr_syntax_id iface;
+ char *bindings;
+};
+
+static bool rpc_iface_binding_map_add_endpoint(
+ TALLOC_CTX *mem_ctx,
+ const struct rpc_host_endpoint *ep,
+ struct rpc_host_iface_name *iface_names,
+ struct rpc_iface_binding_map **pmaps)
+{
+ const struct ndr_syntax_id mgmt_iface = {
+ {0xafa8bd80,
+ 0x7d8a,
+ 0x11c9,
+ {0xbe,0xf4},
+ {0x08,0x00,0x2b,0x10,0x29,0x89}
+ },
+ 1.0};
+
+ struct rpc_iface_binding_map *maps = *pmaps;
+ size_t i, num_ifaces = talloc_array_length(ep->interfaces);
+ char *binding_string = NULL;
+ bool ok = false;
+
+ binding_string = dcerpc_binding_string(mem_ctx, ep->binding);
+ if (binding_string == NULL) {
+ return false;
+ }
+
+ for (i=0; i<num_ifaces; i++) {
+ const struct ndr_syntax_id *iface = &ep->interfaces[i];
+ size_t j, num_maps = talloc_array_length(maps);
+ struct rpc_iface_binding_map *map = NULL;
+ char *p = NULL;
+
+ if (ndr_syntax_id_equal(iface, &mgmt_iface)) {
+ /*
+ * mgmt is offered everywhere, don't put it
+ * into epmdb.tdb.
+ */
+ continue;
+ }
+
+ for (j=0; j<num_maps; j++) {
+ map = &maps[j];
+ if (ndr_syntax_id_equal(&map->iface, iface)) {
+ break;
+ }
+ }
+
+ if (j == num_maps) {
+ struct rpc_iface_binding_map *tmp = NULL;
+ struct rpc_host_iface_name *iface_name = NULL;
+
+ iface_name = rpc_host_iface_names_find(
+ iface_names, iface);
+ if (iface_name == NULL) {
+ goto fail;
+ }
+
+ tmp = talloc_realloc(
+ mem_ctx,
+ maps,
+ struct rpc_iface_binding_map,
+ num_maps+1);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ maps = tmp;
+
+ map = &maps[num_maps];
+ *map = (struct rpc_iface_binding_map) {
+ .iface = *iface,
+ .bindings = talloc_move(
+ maps, &iface_name->name),
+ };
+ }
+
+ p = strv_find(map->bindings, binding_string);
+ if (p == NULL) {
+ int ret = strv_add(
+ maps, &map->bindings, binding_string);
+ if (ret != 0) {
+ goto fail;
+ }
+ }
+ }
+
+ ok = true;
+fail:
+ *pmaps = maps;
+ return ok;
+}
+
+static bool rpc_iface_binding_map_add_endpoints(
+ TALLOC_CTX *mem_ctx,
+ struct rpc_host_endpoint **endpoints,
+ struct rpc_host_iface_name *iface_names,
+ struct rpc_iface_binding_map **pbinding_maps)
+{
+ size_t i, num_endpoints = talloc_array_length(endpoints);
+
+ for (i=0; i<num_endpoints; i++) {
+ bool ok = rpc_iface_binding_map_add_endpoint(
+ mem_ctx, endpoints[i], iface_names, pbinding_maps);
+ if (!ok) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool rpc_host_fill_epm_db(
+ struct tdb_wrap *db,
+ struct rpc_host_endpoint **endpoints,
+ struct rpc_host_iface_name *iface_names)
+{
+ struct rpc_iface_binding_map *maps = NULL;
+ size_t i, num_maps;
+ bool ret = false;
+ bool ok;
+
+ ok = rpc_iface_binding_map_add_endpoints(
+ talloc_tos(), endpoints, iface_names, &maps);
+ if (!ok) {
+ goto fail;
+ }
+
+ num_maps = talloc_array_length(maps);
+
+ for (i=0; i<num_maps; i++) {
+ struct rpc_iface_binding_map *map = &maps[i];
+ struct ndr_syntax_id_buf buf;
+ char *keystr = ndr_syntax_id_buf_string(&map->iface, &buf);
+ TDB_DATA value = {
+ .dptr = (uint8_t *)map->bindings,
+ .dsize = talloc_array_length(map->bindings),
+ };
+ int rc;
+
+ rc = tdb_store(
+ db->tdb, string_term_tdb_data(keystr), value, 0);
+ if (rc == -1) {
+ DBG_DEBUG("tdb_store() failed: %s\n",
+ tdb_errorstr(db->tdb));
+ goto fail;
+ }
+ }
+
+ ret = true;
+fail:
+ TALLOC_FREE(maps);
+ return ret;
+}
+
+struct rpc_server_setup_state {
+ struct rpc_server *server;
+};
+
+static void rpc_server_setup_got_endpoints(struct tevent_req *subreq);
+
+/*
+ * Async initialize state for all possible rpcd_* servers.
+ * Note this does not start them.
+ */
+static struct tevent_req *rpc_server_setup_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_host *host,
+ const char *rpc_server_exe)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct rpc_server_setup_state *state = NULL;
+ struct rpc_server *server = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct rpc_server_setup_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->server = talloc_zero(state, struct rpc_server);
+ if (tevent_req_nomem(state->server, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ server = state->server;
+
+ *server = (struct rpc_server) {
+ .host = host,
+ .server_index = UINT32_MAX,
+ .rpc_server_exe = talloc_strdup(server, rpc_server_exe),
+ };
+ if (tevent_req_nomem(server->rpc_server_exe, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = rpc_server_get_endpoints_send(
+ state,
+ ev,
+ rpc_server_exe,
+ host->np_helper ? NCACN_NP : NCA_UNKNOWN);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, rpc_server_setup_got_endpoints, req);
+ return req;
+}
+
+static void rpc_server_setup_got_endpoints(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_server_setup_state *state = tevent_req_data(
+ req, struct rpc_server_setup_state);
+ struct rpc_server *server = state->server;
+ int ret;
+ size_t i, num_endpoints;
+ bool ok;
+
+ ret = rpc_server_get_endpoints_recv(
+ subreq,
+ server,
+ &server->endpoints,
+ &server->iface_names,
+ &server->max_workers,
+ &server->idle_seconds);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+
+ server->workers = talloc_array(
+ server, struct rpc_work_process, server->max_workers);
+ if (tevent_req_nomem(server->workers, req)) {
+ return;
+ }
+
+ for (i=0; i<server->max_workers; i++) {
+ /* mark as not yet created */
+ server->workers[i] = (struct rpc_work_process) { .pid=-1, };
+ }
+
+ num_endpoints = talloc_array_length(server->endpoints);
+
+ for (i=0; i<num_endpoints; i++) {
+ struct rpc_host_endpoint *e = server->endpoints[i];
+ NTSTATUS status;
+ size_t j;
+
+ e->server = server;
+
+ status = dcesrv_create_binding_sockets(
+ e->binding, e, &e->num_fds, &e->fds);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ continue;
+ }
+ if (tevent_req_nterror(req, status)) {
+ DBG_DEBUG("dcesrv_create_binding_sockets failed: %s\n",
+ nt_errstr(status));
+ return;
+ }
+
+ for (j=0; j<e->num_fds; j++) {
+ ret = listen(e->fds[j], 256);
+ if (ret == -1) {
+ tevent_req_nterror(
+ req, map_nt_error_from_unix(errno));
+ return;
+ }
+ }
+ }
+
+ ok = rpc_host_fill_epm_db(
+ server->host->epmdb, server->endpoints, server->iface_names);
+ if (!ok) {
+ DBG_DEBUG("rpc_host_fill_epm_db failed\n");
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS rpc_server_setup_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, struct rpc_server **server)
+{
+ struct rpc_server_setup_state *state = tevent_req_data(
+ req, struct rpc_server_setup_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *server = talloc_move(mem_ctx, &state->server);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ * rpcd_* died. Called from SIGCHLD handler.
+ */
+static void rpc_worker_exited(struct rpc_host *host, pid_t pid)
+{
+ size_t i, num_servers = talloc_array_length(host->servers);
+ struct rpc_work_process *worker = NULL;
+ bool found_pid = false;
+ bool have_active_worker = false;
+
+ for (i=0; i<num_servers; i++) {
+ struct rpc_server *server = host->servers[i];
+ size_t j, num_workers;
+
+ if (server == NULL) {
+ /* SIGCHLD for --list-interfaces run */
+ continue;
+ }
+
+ num_workers = talloc_array_length(server->workers);
+
+ for (j=0; j<num_workers; j++) {
+ worker = &server->workers[j];
+ if (worker->pid == pid) {
+ found_pid = true;
+ worker->pid = -1;
+ worker->available = false;
+ }
+
+ if (worker->pid != -1) {
+ have_active_worker = true;
+ }
+ }
+ }
+
+ if (!found_pid) {
+ DBG_WARNING("No worker with PID %d\n", (int)pid);
+ return;
+ }
+
+ if (!have_active_worker && host->np_helper) {
+ /*
+ * We have nothing left to do as an np_helper.
+ * Terminate ourselves (samba-dcerpcd). We will
+ * be restarted on demand anyway.
+ */
+ DBG_DEBUG("Exiting idle np helper\n");
+ exit(0);
+ }
+}
+
+/*
+ * rpcd_* died.
+ */
+static void rpc_host_sigchld(
+ struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ struct rpc_host *state = talloc_get_type_abort(
+ private_data, struct rpc_host);
+ pid_t pid;
+ int wstatus;
+
+ while ((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
+ DBG_DEBUG("pid=%d, wstatus=%d\n", (int)pid, wstatus);
+ rpc_worker_exited(state, pid);
+ }
+}
+
+/*
+ * Idle timer fired for a rcpd_* worker. Ask it to terminate.
+ */
+static void rpc_host_exit_worker(
+ struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct rpc_server *server = talloc_get_type_abort(
+ private_data, struct rpc_server);
+ size_t i, num_workers = talloc_array_length(server->workers);
+
+ /*
+ * Scan for the right worker. We don't have too many of those,
+ * and maintaining an index would be more data structure effort.
+ */
+
+ for (i=0; i<num_workers; i++) {
+ struct rpc_work_process *w = &server->workers[i];
+ NTSTATUS status;
+
+ if (w->exit_timer != te) {
+ continue;
+ }
+ w->exit_timer = NULL;
+
+ SMB_ASSERT(w->num_associations == 0);
+
+ status = messaging_send(
+ server->host->msg_ctx,
+ pid_to_procid(w->pid),
+ MSG_SHUTDOWN,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not send SHUTDOWN msg: %s\n",
+ nt_errstr(status));
+ }
+
+ w->available = false;
+ break;
+ }
+}
+
+/*
+ * rcpd_* worker replied with its status.
+ */
+static void rpc_host_child_status_recv(
+ struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct rpc_host *host = talloc_get_type_abort(
+ private_data, struct rpc_host);
+ size_t num_servers = talloc_array_length(host->servers);
+ struct rpc_server *server = NULL;
+ size_t num_workers;
+ pid_t src_pid = procid_to_pid(&server_id);
+ struct rpc_work_process *worker = NULL;
+ struct rpc_worker_status status_message;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob_all_noalloc(
+ data,
+ &status_message,
+ (ndr_pull_flags_fn_t)ndr_pull_rpc_worker_status);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ struct server_id_buf buf;
+ DBG_WARNING("Got invalid message from pid %s\n",
+ server_id_str_buf(server_id, &buf));
+ return;
+ }
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(rpc_worker_status, &status_message);
+ }
+
+ if (status_message.server_index >= num_servers) {
+ DBG_WARNING("Got invalid server_index=%"PRIu32", "
+ "num_servers=%zu\n",
+ status_message.server_index,
+ num_servers);
+ return;
+ }
+
+ server = host->servers[status_message.server_index];
+
+ num_workers = talloc_array_length(server->workers);
+ if (status_message.worker_index >= num_workers) {
+ DBG_WARNING("Got invalid worker_index=%"PRIu32", "
+ "num_workers=%zu\n",
+ status_message.worker_index,
+ num_workers);
+ return;
+ }
+ worker = &server->workers[status_message.worker_index];
+
+ if (src_pid != worker->pid) {
+ DBG_WARNING("Got idx=%"PRIu32" from %d, expected %d\n",
+ status_message.worker_index,
+ (int)src_pid,
+ worker->pid);
+ return;
+ }
+
+ worker->available = true;
+ worker->num_associations = status_message.num_association_groups;
+ worker->num_connections = status_message.num_connections;
+
+ if (worker->num_associations != 0) {
+ TALLOC_FREE(worker->exit_timer);
+ } else {
+ worker->exit_timer = tevent_add_timer(
+ messaging_tevent_context(msg),
+ server->workers,
+ tevent_timeval_current_ofs(server->idle_seconds, 0),
+ rpc_host_exit_worker,
+ server);
+ /* No NULL check, it's not fatal if this does not work */
+ }
+
+ rpc_host_distribute_clients(server);
+}
+
+/*
+ * samba-dcerpcd has been asked to shutdown.
+ * Mark the initial tevent_req as done so we
+ * exit the event loop.
+ */
+static void rpc_host_msg_shutdown(
+ struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct tevent_req *req = talloc_get_type_abort(
+ private_data, struct tevent_req);
+ tevent_req_done(req);
+}
+
+/*
+ * Only match directory entries starting in rpcd_
+ */
+static int rpcd_filter(const struct dirent *d)
+{
+ int match = fnmatch("rpcd_*", d->d_name, 0);
+ return (match == 0) ? 1 : 0;
+}
+
+/*
+ * Scan the given libexecdir for rpcd_* services
+ * and return them as a strv list.
+ */
+static int rpc_host_list_servers(
+ const char *libexecdir, TALLOC_CTX *mem_ctx, char **pservers)
+{
+ char *servers = NULL;
+ struct dirent **namelist = NULL;
+ int i, num_servers;
+ int ret = ENOMEM;
+
+ num_servers = scandir(libexecdir, &namelist, rpcd_filter, alphasort);
+ if (num_servers == -1) {
+ DBG_DEBUG("scandir failed: %s\n", strerror(errno));
+ return errno;
+ }
+
+ for (i=0; i<num_servers; i++) {
+ char *exe = talloc_asprintf(
+ mem_ctx, "%s/%s", libexecdir, namelist[i]->d_name);
+ if (exe == NULL) {
+ goto fail;
+ }
+
+ ret = strv_add(mem_ctx, &servers, exe);
+ TALLOC_FREE(exe);
+ if (ret != 0) {
+ goto fail;
+ }
+ }
+fail:
+ for (i=0; i<num_servers; i++) {
+ SAFE_FREE(namelist[i]);
+ }
+ SAFE_FREE(namelist);
+
+ if (ret != 0) {
+ TALLOC_FREE(servers);
+ return ret;
+ }
+ *pservers = servers;
+ return 0;
+}
+
+struct rpc_host_endpoint_accept_state {
+ struct tevent_context *ev;
+ struct rpc_host_endpoint *endpoint;
+};
+
+static void rpc_host_endpoint_accept_accepted(struct tevent_req *subreq);
+static void rpc_host_endpoint_accept_got_bind(struct tevent_req *subreq);
+
+/*
+ * Asynchronously wait for a DCERPC connection from a client.
+ */
+static struct tevent_req *rpc_host_endpoint_accept_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_host_endpoint *endpoint)
+{
+ struct tevent_req *req = NULL;
+ struct rpc_host_endpoint_accept_state *state = NULL;
+ size_t i;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct rpc_host_endpoint_accept_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->endpoint = endpoint;
+
+ for (i=0; i<endpoint->num_fds; i++) {
+ struct tevent_req *subreq = NULL;
+
+ subreq = accept_send(state, ev, endpoint->fds[i]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, rpc_host_endpoint_accept_accepted, req);
+ }
+
+ return req;
+}
+
+/*
+ * Accept a DCERPC connection from a client.
+ */
+static void rpc_host_endpoint_accept_accepted(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_host_endpoint_accept_state *state = tevent_req_data(
+ req, struct rpc_host_endpoint_accept_state);
+ struct rpc_host_endpoint *endpoint = state->endpoint;
+ int sock, listen_sock, err;
+ struct samba_sockaddr peer_addr;
+
+ sock = accept_recv(subreq, &listen_sock, &peer_addr, &err);
+ TALLOC_FREE(subreq);
+ if (sock == -1) {
+ /* What to do here? Just ignore the error and retry? */
+ DBG_DEBUG("accept_recv failed: %s\n", strerror(err));
+ tevent_req_error(req, err);
+ return;
+ }
+
+ subreq = accept_send(state, state->ev, listen_sock);
+ if (tevent_req_nomem(subreq, req)) {
+ close(sock);
+ sock = -1;
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, rpc_host_endpoint_accept_accepted, req);
+
+ subreq = rpc_host_bind_read_send(
+ state,
+ state->ev,
+ dcerpc_binding_get_transport(endpoint->binding),
+ &sock,
+ &peer_addr);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, rpc_host_endpoint_accept_got_bind, req);
+}
+
+/*
+ * Client sent us a DCERPC bind packet.
+ */
+static void rpc_host_endpoint_accept_got_bind(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_host_endpoint_accept_state *state = tevent_req_data(
+ req, struct rpc_host_endpoint_accept_state);
+ struct rpc_host_endpoint *endpoint = state->endpoint;
+ struct rpc_server *server = endpoint->server;
+ struct rpc_host_pending_client *pending = NULL;
+ struct rpc_host_client *client = NULL;
+ struct ncacn_packet *bind_pkt = NULL;
+ int ret;
+ int sock=-1;
+
+ ret = rpc_host_bind_read_recv(
+ subreq, state, &sock, &client, &bind_pkt);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ DBG_DEBUG("rpc_host_bind_read_recv returned %s\n",
+ strerror(ret));
+ goto fail;
+ }
+
+ client->binding = dcerpc_binding_string(client, endpoint->binding);
+ if (client->binding == NULL) {
+ DBG_WARNING("dcerpc_binding_string failed, dropping client\n");
+ goto fail;
+ }
+
+ pending = talloc_zero(server, struct rpc_host_pending_client);
+ if (pending == NULL) {
+ DBG_WARNING("talloc failed, dropping client\n");
+ goto fail;
+ }
+ pending->server = server;
+ pending->sock = sock;
+ pending->bind_pkt = talloc_move(pending, &bind_pkt);
+ pending->client = talloc_move(pending, &client);
+ talloc_set_destructor(pending, rpc_host_pending_client_destructor);
+ sock = -1;
+
+ pending->hangup_wait = wait_for_read_send(
+ pending, state->ev, pending->sock, true);
+ if (pending->hangup_wait == NULL) {
+ DBG_WARNING("wait_for_read_send failed, dropping client\n");
+ TALLOC_FREE(pending);
+ return;
+ }
+ tevent_req_set_callback(
+ pending->hangup_wait, rpc_host_client_exited, pending);
+
+ DLIST_ADD_END(server->pending_clients, pending);
+ rpc_host_distribute_clients(server);
+ return;
+
+fail:
+ TALLOC_FREE(client);
+ if (sock != -1) {
+ close(sock);
+ }
+}
+
+static int rpc_host_endpoint_accept_recv(
+ struct tevent_req *req, struct rpc_host_endpoint **ep)
+{
+ struct rpc_host_endpoint_accept_state *state = tevent_req_data(
+ req, struct rpc_host_endpoint_accept_state);
+
+ *ep = state->endpoint;
+
+ return tevent_req_simple_recv_unix(req);
+}
+
+/*
+ * Full state for samba-dcerpcd. Everything else
+ * is hung off this.
+ */
+struct rpc_host_state {
+ struct tevent_context *ev;
+ struct rpc_host *host;
+
+ bool is_ready;
+ const char *daemon_ready_progname;
+ struct tevent_immediate *ready_signal_immediate;
+ int *ready_signal_fds;
+
+ size_t num_servers;
+ size_t num_prepared;
+};
+
+/*
+ * Tell whoever invoked samba-dcerpcd we're ready to
+ * serve.
+ */
+static void rpc_host_report_readiness(
+ struct tevent_context *ev,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct rpc_host_state *state = talloc_get_type_abort(
+ private_data, struct rpc_host_state);
+ size_t i, num_fds = talloc_array_length(state->ready_signal_fds);
+
+ if (!state->is_ready) {
+ DBG_DEBUG("Not yet ready\n");
+ return;
+ }
+
+ for (i=0; i<num_fds; i++) {
+ uint8_t byte = 0;
+ ssize_t nwritten;
+
+ do {
+ nwritten = write(
+ state->ready_signal_fds[i],
+ (void *)&byte,
+ sizeof(byte));
+ } while ((nwritten == -1) && (errno == EINTR));
+
+ close(state->ready_signal_fds[i]);
+ }
+
+ TALLOC_FREE(state->ready_signal_fds);
+}
+
+/*
+ * Respond to a "are you ready" message.
+ */
+static bool rpc_host_ready_signal_filter(
+ struct messaging_rec *rec, void *private_data)
+{
+ struct rpc_host_state *state = talloc_get_type_abort(
+ private_data, struct rpc_host_state);
+ size_t num_fds = talloc_array_length(state->ready_signal_fds);
+ int *tmp = NULL;
+
+ if (rec->msg_type != MSG_DAEMON_READY_FD) {
+ return false;
+ }
+ if (rec->num_fds != 1) {
+ DBG_DEBUG("Got %"PRIu8" fds\n", rec->num_fds);
+ return false;
+ }
+
+ if (num_fds + 1 < num_fds) {
+ return false;
+ }
+ tmp = talloc_realloc(state, state->ready_signal_fds, int, num_fds+1);
+ if (tmp == NULL) {
+ return false;
+ }
+ state->ready_signal_fds = tmp;
+
+ state->ready_signal_fds[num_fds] = rec->fds[0];
+ rec->fds[0] = -1;
+
+ tevent_schedule_immediate(
+ state->ready_signal_immediate,
+ state->ev,
+ rpc_host_report_readiness,
+ state);
+
+ return false;
+}
+
+/*
+ * Respond to a "what is your status" message.
+ */
+static bool rpc_host_dump_status_filter(
+ struct messaging_rec *rec, void *private_data)
+{
+ struct rpc_host_state *state = talloc_get_type_abort(
+ private_data, struct rpc_host_state);
+ struct rpc_host *host = state->host;
+ struct rpc_server **servers = host->servers;
+ size_t i, num_servers = talloc_array_length(servers);
+ FILE *f = NULL;
+ int fd;
+
+ if (rec->msg_type != MSG_RPC_DUMP_STATUS) {
+ return false;
+ }
+ if (rec->num_fds != 1) {
+ DBG_DEBUG("Got %"PRIu8" fds\n", rec->num_fds);
+ return false;
+ }
+
+ fd = dup(rec->fds[0]);
+ if (fd == -1) {
+ DBG_DEBUG("dup(%"PRIi64") failed: %s\n",
+ rec->fds[0],
+ strerror(errno));
+ return false;
+ }
+
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ DBG_DEBUG("fdopen failed: %s\n", strerror(errno));
+ close(fd);
+ return false;
+ }
+
+ for (i=0; i<num_servers; i++) {
+ struct rpc_server *server = servers[i];
+ size_t j, num_workers = talloc_array_length(server->workers);
+ size_t active_workers = 0;
+
+ for (j=0; j<num_workers; j++) {
+ if (server->workers[j].pid != -1) {
+ active_workers += 1;
+ }
+ }
+
+ fprintf(f,
+ "%s: active_workers=%zu\n",
+ server->rpc_server_exe,
+ active_workers);
+
+ for (j=0; j<num_workers; j++) {
+ struct rpc_work_process *w = &server->workers[j];
+
+ if (w->pid == (pid_t)-1) {
+ continue;
+ }
+
+ fprintf(f,
+ " worker[%zu]: pid=%d, num_associations=%"PRIu32", num_connections=%"PRIu32"\n",
+ j,
+ (int)w->pid,
+ w->num_associations,
+ w->num_connections);
+ }
+ }
+
+ fclose(f);
+
+ return false;
+}
+
+static void rpc_host_server_setup_done(struct tevent_req *subreq);
+static void rpc_host_endpoint_failed(struct tevent_req *subreq);
+
+/*
+ * Async startup for samba-dcerpcd.
+ */
+static struct tevent_req *rpc_host_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ char *servers,
+ int ready_signal_fd,
+ const char *daemon_ready_progname,
+ bool is_np_helper)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct rpc_host_state *state = NULL;
+ struct rpc_host *host = NULL;
+ struct tevent_signal *se = NULL;
+ char *epmdb_path = NULL;
+ char *exe = NULL;
+ size_t i, num_servers = strv_count(servers);
+ NTSTATUS status;
+ int ret;
+
+ req = tevent_req_create(req, &state, struct rpc_host_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->daemon_ready_progname = daemon_ready_progname;
+
+ state->ready_signal_immediate = tevent_create_immediate(state);
+ if (tevent_req_nomem(state->ready_signal_immediate, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (ready_signal_fd != -1) {
+ state->ready_signal_fds = talloc_array(state, int, 1);
+ if (tevent_req_nomem(state->ready_signal_fds, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->ready_signal_fds[0] = ready_signal_fd;
+ }
+
+ state->host = talloc_zero(state, struct rpc_host);
+ if (tevent_req_nomem(state->host, req)) {
+ return tevent_req_post(req, ev);
+ }
+ host = state->host;
+
+ host->msg_ctx = msg_ctx;
+ host->np_helper = is_np_helper;
+
+ ret = pipe(host->worker_stdin);
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+
+ host->servers = talloc_zero_array(
+ host, struct rpc_server *, num_servers);
+ if (tevent_req_nomem(host->servers, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ se = tevent_add_signal(ev, state, SIGCHLD, 0, rpc_host_sigchld, host);
+ if (tevent_req_nomem(se, req)) {
+ return tevent_req_post(req, ev);
+ }
+ BlockSignals(false, SIGCHLD);
+
+ status = messaging_register(
+ msg_ctx,
+ host,
+ MSG_RPC_WORKER_STATUS,
+ rpc_host_child_status_recv);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = messaging_register(
+ msg_ctx, req, MSG_SHUTDOWN, rpc_host_msg_shutdown);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = messaging_filtered_read_send(
+ state, ev, msg_ctx, rpc_host_ready_signal_filter, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = messaging_filtered_read_send(
+ state, ev, msg_ctx, rpc_host_dump_status_filter, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ epmdb_path = lock_path(state, "epmdb.tdb");
+ if (tevent_req_nomem(epmdb_path, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ host->epmdb = tdb_wrap_open(
+ host,
+ epmdb_path,
+ 0,
+ TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+ O_RDWR|O_CREAT,
+ 0644);
+ if (host->epmdb == NULL) {
+ DBG_DEBUG("tdb_wrap_open(%s) failed: %s\n",
+ epmdb_path,
+ strerror(errno));
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+ TALLOC_FREE(epmdb_path);
+
+ for (exe = strv_next(servers, exe), i = 0;
+ exe != NULL;
+ exe = strv_next(servers, exe), i++) {
+
+ DBG_DEBUG("server_setup for %s index %zu\n", exe, i);
+
+ subreq = rpc_server_setup_send(
+ state,
+ ev,
+ host,
+ exe);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, rpc_host_server_setup_done, req);
+ }
+
+ return req;
+}
+
+/*
+ * Timer function called after we were initialized but no one
+ * connected. Shutdown.
+ */
+static void rpc_host_shutdown(
+ struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct tevent_req *req = talloc_get_type_abort(
+ private_data, struct tevent_req);
+ DBG_DEBUG("Nobody connected -- shutting down\n");
+ tevent_req_done(req);
+}
+
+static void rpc_host_server_setup_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_host_state *state = tevent_req_data(
+ req, struct rpc_host_state);
+ struct rpc_server *server = NULL;
+ struct rpc_host *host = state->host;
+ size_t i, num_servers = talloc_array_length(host->servers);
+ NTSTATUS status;
+
+ status = rpc_server_setup_recv(subreq, host, &server);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_server_setup_recv returned %s, ignoring\n",
+ nt_errstr(status));
+ host->servers = talloc_realloc(
+ host,
+ host->servers,
+ struct rpc_server *,
+ num_servers-1);
+ return;
+ }
+
+ server->server_index = state->num_prepared;
+ host->servers[state->num_prepared] = server;
+
+ state->num_prepared += 1;
+
+ if (state->num_prepared < num_servers) {
+ return;
+ }
+
+ for (i=0; i<num_servers; i++) {
+ size_t j, num_endpoints;
+
+ server = host->servers[i];
+ num_endpoints = talloc_array_length(server->endpoints);
+
+ for (j=0; j<num_endpoints; j++) {
+ subreq = rpc_host_endpoint_accept_send(
+ state, state->ev, server->endpoints[j]);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, rpc_host_endpoint_failed, req);
+ }
+ }
+
+ state->is_ready = true;
+
+ if (state->daemon_ready_progname != NULL) {
+ daemon_ready(state->daemon_ready_progname);
+ }
+
+ if (host->np_helper) {
+ /*
+ * If we're started as an np helper, and no one talks to
+ * us within 10 seconds, just shut ourselves down.
+ */
+ host->np_helper_shutdown = tevent_add_timer(
+ state->ev,
+ state,
+ timeval_current_ofs(10, 0),
+ rpc_host_shutdown,
+ req);
+ if (tevent_req_nomem(host->np_helper_shutdown, req)) {
+ return;
+ }
+ }
+
+ tevent_schedule_immediate(
+ state->ready_signal_immediate,
+ state->ev,
+ rpc_host_report_readiness,
+ state);
+}
+
+/*
+ * Log accept fail on an endpoint.
+ */
+static void rpc_host_endpoint_failed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct rpc_host_state *state = tevent_req_data(
+ req, struct rpc_host_state);
+ struct rpc_host_endpoint *endpoint = NULL;
+ char *binding_string = NULL;
+ int ret;
+
+ ret = rpc_host_endpoint_accept_recv(subreq, &endpoint);
+ TALLOC_FREE(subreq);
+
+ binding_string = dcerpc_binding_string(state, endpoint->binding);
+ DBG_DEBUG("rpc_host_endpoint_accept_recv for %s returned %s\n",
+ binding_string,
+ strerror(ret));
+ TALLOC_FREE(binding_string);
+}
+
+static NTSTATUS rpc_host_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static int rpc_host_pidfile_create(
+ struct messaging_context *msg_ctx,
+ const char *progname,
+ int ready_signal_fd)
+{
+ const char *piddir = lp_pid_directory();
+ size_t len = strlen(piddir) + strlen(progname) + 6;
+ char pidFile[len];
+ pid_t existing_pid;
+ int fd, ret;
+
+ snprintf(pidFile,
+ sizeof(pidFile),
+ "%s/%s.pid",
+ piddir, progname);
+
+ ret = pidfile_path_create(pidFile, &fd, &existing_pid);
+ if (ret == 0) {
+ /* leak fd */
+ return 0;
+ }
+
+ if (ret != EAGAIN) {
+ DBG_DEBUG("pidfile_path_create() failed: %s\n",
+ strerror(ret));
+ return ret;
+ }
+
+ DBG_DEBUG("%s pid %d exists\n", progname, (int)existing_pid);
+
+ if (ready_signal_fd != -1) {
+ NTSTATUS status = messaging_send_iov(
+ msg_ctx,
+ pid_to_procid(existing_pid),
+ MSG_DAEMON_READY_FD,
+ NULL,
+ 0,
+ &ready_signal_fd,
+ 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not send ready_signal_fd: %s\n",
+ nt_errstr(status));
+ }
+ }
+
+ return EAGAIN;
+}
+
+static void samba_dcerpcd_stdin_handler(
+ struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct tevent_req *req = talloc_get_type_abort(
+ private_data, struct tevent_req);
+ char c;
+
+ if (read(0, &c, 1) != 1) {
+ /* we have reached EOF on stdin, which means the
+ parent has exited. Shutdown the server */
+ tevent_req_done(req);
+ }
+}
+
+/*
+ * samba-dcerpcd microservice startup !
+ */
+int main(int argc, const char *argv[])
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *progname = getprogname();
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev_ctx = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ struct tevent_req *req = NULL;
+ char *servers = NULL;
+ const char *arg = NULL;
+ size_t num_servers;
+ poptContext pc;
+ int ret, err;
+ NTSTATUS status;
+ bool log_stdout;
+ bool ok;
+
+ int libexec_rpcds = 0;
+ int np_helper = 0;
+ int ready_signal_fd = -1;
+
+ struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg = NULL;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "libexec-rpcds",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &libexec_rpcds,
+ .descrip = "Use all rpcds in libexec",
+ },
+ {
+ .longName = "ready-signal-fd",
+ .argInfo = POPT_ARG_INT,
+ .arg = &ready_signal_fd,
+ .descrip = "fd to close when initialized",
+ },
+ {
+ .longName = "np-helper",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &np_helper,
+ .descrip = "Internal named pipe server",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_DAEMON
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ {
+ const char *fd_params[] = { "ready-signal-fd", };
+
+ closefrom_except_fd_params(
+ 3, ARRAY_SIZE(fd_params), fd_params, argc, argv);
+ }
+
+ talloc_enable_null_tracking();
+ frame = talloc_stackframe();
+ umask(0);
+ sec_init();
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+ true /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(ENOMEM);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(
+ pc, "[OPTIONS] [SERVICE_1 SERVICE_2 .. SERVICE_N]");
+
+ ret = poptGetNextOpt(pc);
+
+ if (ret != -1) {
+ if (ret >= 0) {
+ fprintf(stderr,
+ "\nGot unexpected option %d\n",
+ ret);
+ } else if (ret == POPT_ERROR_BADOPT) {
+ fprintf(stderr,
+ "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0),
+ poptStrerror(ret));
+ } else {
+ fprintf(stderr,
+ "\npoptGetNextOpt returned %s\n",
+ poptStrerror(ret));
+ }
+
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((arg = poptGetArg(pc)) != NULL) {
+ ret = strv_add(frame, &servers, arg);
+ if (ret != 0) {
+ DBG_ERR("strv_add() failed\n");
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ }
+
+ log_stdout = (debug_get_log_type() == DEBUG_STDOUT);
+ if (log_stdout) {
+ setup_logging(progname, DEBUG_STDOUT);
+ } else {
+ setup_logging(progname, DEBUG_FILE);
+ }
+
+ /*
+ * If "rpc start on demand helpers = true" in smb.conf we must
+ * not start as standalone, only on demand from
+ * local_np_connect() functions. Log an error message telling
+ * the admin how to fix and then exit.
+ */
+ if (lp_rpc_start_on_demand_helpers() && np_helper == 0) {
+ DBG_ERR("Cannot start in standalone mode if smb.conf "
+ "[global] setting "
+ "\"rpc start on demand helpers = true\" - "
+ "exiting\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ if (libexec_rpcds != 0) {
+ ret = rpc_host_list_servers(
+ dyn_SAMBA_LIBEXECDIR, frame, &servers);
+ if (ret != 0) {
+ DBG_ERR("Could not list libexec: %s\n",
+ strerror(ret));
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ }
+
+ num_servers = strv_count(servers);
+ if (num_servers == 0) {
+ poptPrintUsage(pc, stderr, 0);
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptFreeContext(pc);
+
+ cmdline_daemon_cfg = samba_cmdline_get_daemon_cfg();
+
+ if (log_stdout && cmdline_daemon_cfg->fork) {
+ DBG_ERR("Can't log to stdout unless in foreground\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ msg_ctx = global_messaging_context();
+ if (msg_ctx == NULL) {
+ DBG_ERR("messaging_init() failed\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ ev_ctx = messaging_tevent_context(msg_ctx);
+
+ if (cmdline_daemon_cfg->fork) {
+ become_daemon(
+ true,
+ cmdline_daemon_cfg->no_process_group,
+ log_stdout);
+
+ status = reinit_after_fork(msg_ctx, ev_ctx, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("reinit_after_fork() failed",
+ map_errno_from_nt_status(status));
+ }
+ } else {
+ DBG_DEBUG("Calling daemon_status\n");
+ daemon_status(progname, "Starting process ... ");
+ }
+
+ BlockSignals(true, SIGPIPE);
+
+ dump_core_setup(progname, lp_logfile(frame, lp_sub));
+
+ reopen_logs();
+
+ DBG_STARTUP_NOTICE("%s version %s started.\n%s\n",
+ progname,
+ samba_version_string(),
+ samba_copyright_string());
+
+ (void)winbind_off();
+ ok = init_guest_session_info(frame);
+ (void)winbind_on();
+ if (!ok) {
+ DBG_ERR("init_guest_session_info failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ ret = rpc_host_pidfile_create(msg_ctx, progname, ready_signal_fd);
+ if (ret != 0) {
+ DBG_DEBUG("rpc_host_pidfile_create failed: %s\n",
+ strerror(ret));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ req = rpc_host_send(
+ ev_ctx,
+ ev_ctx,
+ msg_ctx,
+ servers,
+ ready_signal_fd,
+ cmdline_daemon_cfg->fork ? NULL : progname,
+ np_helper != 0);
+ if (req == NULL) {
+ DBG_ERR("rpc_host_send failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ if (!cmdline_daemon_cfg->fork) {
+ struct stat st;
+ if (fstat(0, &st) != 0) {
+ DBG_DEBUG("fstat(0) failed: %s\n",
+ strerror(errno));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
+ tevent_add_fd(
+ ev_ctx,
+ ev_ctx,
+ 0,
+ TEVENT_FD_READ,
+ samba_dcerpcd_stdin_handler,
+ req);
+ }
+ }
+
+ ok = tevent_req_poll_unix(req, ev_ctx, &err);
+ if (!ok) {
+ DBG_ERR("tevent_req_poll_unix failed: %s\n",
+ strerror(err));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ status = rpc_host_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("rpc_host_recv returned %s\n", nt_errstr(status));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ TALLOC_FREE(frame);
+
+ return 0;
+}
diff --git a/source3/rpc_server/rpc_ncacn_np.c b/source3/rpc_server/rpc_ncacn_np.c
new file mode 100644
index 0000000..03618df
--- /dev/null
+++ b/source3/rpc_server/rpc_ncacn_np.c
@@ -0,0 +1,217 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Largely re-written : 2005
+ * Copyright (C) Jeremy Allison 1998 - 2005
+ * Copyright (C) Simo Sorce 2010
+ * Copyright (C) Andrew Bartlett 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_dce.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "librpc/gen_ndr/auth.h"
+#include "../auth/auth_sam_reply.h"
+#include "../auth/auth_util.h"
+#include "auth.h"
+#include "rpc_server/rpc_pipes.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "rpc_server/rpc_config.h"
+#include "librpc/ndr/ndr_table.h"
+#include "rpc_server/rpc_server.h"
+#include "librpc/rpc/dcerpc_util.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+struct np_proxy_state {
+ uint16_t file_type;
+ uint16_t device_state;
+ uint64_t allocation_size;
+ struct tstream_context *npipe;
+ struct tevent_queue *read_queue;
+ struct tevent_queue *write_queue;
+};
+
+struct npa_state *npa_state_init(TALLOC_CTX *mem_ctx)
+{
+ struct npa_state *npa;
+
+ npa = talloc_zero(mem_ctx, struct npa_state);
+ if (npa == NULL) {
+ return NULL;
+ }
+
+ npa->read_queue = tevent_queue_create(npa, "npa_cli_read");
+ if (npa->read_queue == NULL) {
+ DEBUG(0, ("tevent_queue_create failed\n"));
+ goto fail;
+ }
+
+ npa->write_queue = tevent_queue_create(npa, "npa_cli_write");
+ if (npa->write_queue == NULL) {
+ DEBUG(0, ("tevent_queue_create failed\n"));
+ goto fail;
+ }
+
+ return npa;
+fail:
+ talloc_free(npa);
+ return NULL;
+}
+
+/**
+ * @brief Create a new DCERPC Binding Handle which uses a local dispatch function.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] ndr_table Normally the ndr_table_<name>.
+ *
+ * @param[in] remote_address The info about the connected client.
+ *
+ * @param[in] serversupplied_info The server supplied authentication function.
+ *
+ * @param[in] msg_ctx The messaging context that can be used by the server
+ *
+ * @param[out] binding_handle A pointer to store the connected
+ * dcerpc_binding_handle
+ *
+ * @return NT_STATUS_OK on success, a corresponding NT status if an
+ * error occurred.
+ *
+ * @code
+ * struct dcerpc_binding_handle *winreg_binding;
+ * NTSTATUS status;
+ *
+ * status = rpcint_binding_handle(tmp_ctx,
+ * &ndr_table_winreg,
+ * p->remote_address,
+ * p->session_info,
+ * p->msg_ctx
+ * &winreg_binding);
+ * @endcode
+ */
+NTSTATUS rpcint_binding_handle(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *ndr_table,
+ const struct tsocket_address *remote_address,
+ const struct tsocket_address *local_address,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **binding_handle)
+{
+ struct rpc_pipe_client *rpccli = NULL;
+ NTSTATUS status;
+
+ status = rpc_pipe_open_local_np(
+ mem_ctx,
+ ndr_table,
+ NULL,
+ remote_address,
+ NULL,
+ local_address,
+ session_info,
+ &rpccli);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_pipe_open_local_np failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ *binding_handle = rpccli->binding_handle;
+ return NT_STATUS_OK;
+fail:
+ TALLOC_FREE(rpccli);
+ return status;
+}
+
+/**
+ * @brief Create a new RPC client context which uses a local dispatch function
+ * or a remote transport, depending on rpc_server configuration for the
+ * specific service.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] abstract_syntax Normally the syntax_id of the autogenerated
+ * ndr_table_<name>.
+ *
+ * @param[in] serversupplied_info The server supplied authentication function.
+ *
+ * @param[in] remote_address The client address information.
+ *
+ * @param[in] msg_ctx The messaging context to use.
+ *
+ * @param[out] presult A pointer to store the connected rpc client pipe.
+ *
+ * @return NT_STATUS_OK on success, a corresponding NT status if an
+ * error occurred.
+ *
+ * @code
+ * struct rpc_pipe_client *winreg_pipe;
+ * NTSTATUS status;
+ *
+ * status = rpc_pipe_open_interface(tmp_ctx,
+ * &ndr_table_winreg.syntax_id,
+ * p->session_info,
+ * remote_address,
+ * &winreg_pipe);
+ * @endcode
+ */
+
+NTSTATUS rpc_pipe_open_interface(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ const struct auth_session_info *session_info,
+ const struct tsocket_address *remote_address,
+ const struct tsocket_address *local_address,
+ struct messaging_context *msg_ctx,
+ struct rpc_pipe_client **cli_pipe)
+{
+ struct rpc_pipe_client *cli = NULL;
+ NTSTATUS status;
+
+ if (cli_pipe != NULL) {
+ if (rpccli_is_connected(*cli_pipe)) {
+ return NT_STATUS_OK;
+ } else {
+ TALLOC_FREE(*cli_pipe);
+ }
+ }
+
+ status = rpc_pipe_open_local_np(
+ mem_ctx,
+ table,
+ NULL,
+ remote_address,
+ NULL,
+ local_address,
+ session_info,
+ &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Could not connect to %s pipe: %s\n",
+ table->name,
+ nt_errstr(status));
+ return status;
+ }
+
+ if (NT_STATUS_IS_OK(status) && cli_pipe != NULL) {
+ *cli_pipe = cli;
+ }
+ return status;
+}
diff --git a/source3/rpc_server/rpc_ncacn_np.h b/source3/rpc_server/rpc_ncacn_np.h
new file mode 100644
index 0000000..acbc5f2
--- /dev/null
+++ b/source3/rpc_server/rpc_ncacn_np.h
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/Netbios implementation.
+ RPC Server Headers
+ Copyright (C) Simo Sorce 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RPC_NCACN_NP_H_
+#define _RPC_NCACN_NP_H_
+
+struct dcerpc_binding_handle;
+struct ndr_interface_table;
+struct tsocket_address;
+struct dcesrv_context;
+struct dcesrv_endpoint;
+
+struct npa_state {
+ struct tstream_context *stream;
+
+ struct tevent_queue *read_queue;
+ struct tevent_queue *write_queue;
+
+ uint64_t allocation_size;
+ uint16_t device_state;
+ uint16_t file_type;
+
+ void *private_data;
+};
+
+struct npa_state *npa_state_init(TALLOC_CTX *mem_ctx);
+
+NTSTATUS rpcint_binding_handle(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *ndr_table,
+ const struct tsocket_address *remote_address,
+ const struct tsocket_address *local_address,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **binding_handle);
+NTSTATUS rpc_pipe_open_interface(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ const struct auth_session_info *session_info,
+ const struct tsocket_address *remote_address,
+ const struct tsocket_address *local_address,
+ struct messaging_context *msg_ctx,
+ struct rpc_pipe_client **cli_pipe);
+#endif /* _RPC_NCACN_NP_H_ */
diff --git a/source3/rpc_server/rpc_pipes.h b/source3/rpc_server/rpc_pipes.h
new file mode 100644
index 0000000..17922b0
--- /dev/null
+++ b/source3/rpc_server/rpc_pipes.h
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/Netbios implementation.
+ RPC Server Headers
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+ Copyright (C) Jeremy Allison 2000-2004
+ Copyright (C) Simo Sorce 2010-2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RPC_PIPES_H_
+#define _RPC_PIPES_H_
+
+#include "source3/librpc/rpc/dcerpc.h"
+
+struct tsocket_address;
+struct pipes_struct;
+struct dcesrv_context;
+
+/*
+ * DCE/RPC-specific samba-internal-specific handling of data on
+ * NamedPipes.
+ */
+struct pipes_struct {
+ enum dcerpc_transport_t transport;
+
+ struct messaging_context *msg_ctx;
+
+ /*
+ * Set the DCERPC_FAULT to return.
+ */
+ int fault_state;
+
+ /* This context is used for PDU data and is freed between each pdu.
+ Don't use for pipe state storage. */
+ TALLOC_CTX *mem_ctx;
+
+ /* handle database to use on this pipe. */
+ struct dcesrv_call_state *dce_call;
+};
+
+bool check_open_pipes(void);
+size_t num_pipe_handles(void);
+
+void *create_policy_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ uint8_t handle_type,
+ void *data_ptr);
+
+void *_find_policy_by_hnd(struct pipes_struct *p,
+ const struct policy_handle *hnd,
+ uint8_t handle_type,
+ NTSTATUS *pstatus);
+#define find_policy_by_hnd(_p, _hnd, _hnd_type, _type, _pstatus) \
+ (_type *)_find_policy_by_hnd((_p), (_hnd), (_hnd_type), (_pstatus));
+
+bool close_policy_hnd(struct pipes_struct *p, struct policy_handle *hnd);
+bool pipe_access_check(struct pipes_struct *p);
+
+#endif /* _RPC_PIPES_H_ */
diff --git a/source3/rpc_server/rpc_server.c b/source3/rpc_server/rpc_server.c
new file mode 100644
index 0000000..a60f429
--- /dev/null
+++ b/source3/rpc_server/rpc_server.c
@@ -0,0 +1,309 @@
+/*
+ Unix SMB/Netbios implementation.
+ Generic infrstructure for RPC Daemons
+ Copyright (C) Simo Sorce 2010
+ Copyright (C) Andrew Bartlett 2011
+ Copyright (C) Andreas Schneider 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "rpc_server/rpc_pipes.h"
+#include "rpc_server/rpc_server.h"
+#include "rpc_server/rpc_config.h"
+#include "rpc_dce.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "librpc/gen_ndr/auth.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/named_pipe_auth/npa_tstream.h"
+#include "../auth/auth_sam_reply.h"
+#include "auth.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "rpc_server/srv_pipe_hnd.h"
+#include "lib/util/idtree_random.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* Start listening on the appropriate unix socket and setup all is needed to
+ * dispatch requests to the pipes rpc implementation */
+
+struct dcerpc_ncacn_listen_state {
+ int fd;
+
+ struct tevent_context *ev_ctx;
+ struct messaging_context *msg_ctx;
+ struct dcesrv_context *dce_ctx;
+ struct dcesrv_endpoint *endpoint;
+ dcerpc_ncacn_termination_fn termination_fn;
+ void *termination_data;
+};
+
+static void ncacn_terminate_connection(struct dcerpc_ncacn_conn *conn,
+ const char *reason);
+
+NTSTATUS dcesrv_auth_gensec_prepare(
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *call,
+ struct gensec_security **out,
+ void *private_data)
+{
+ struct gensec_security *gensec = NULL;
+ NTSTATUS status;
+
+ if (out == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = auth_generic_prepare(mem_ctx,
+ call->conn->remote_address,
+ call->conn->local_address,
+ "DCE/RPC",
+ &gensec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to prepare gensec: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ *out = gensec;
+
+ return NT_STATUS_OK;
+}
+
+void dcesrv_log_successful_authz(
+ struct dcesrv_call_state *call,
+ void *private_data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct auth4_context *auth4_context = NULL;
+ struct dcesrv_auth *auth = call->auth_state;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(
+ call->conn->endpoint->ep_description);
+ const char *auth_type = derpc_transport_string_by_transport(transport);
+ const char *transport_protection = AUTHZ_TRANSPORT_PROTECTION_NONE;
+ NTSTATUS status;
+
+ if (frame == NULL) {
+ DBG_ERR("No memory\n");
+ return;
+ }
+
+ if (transport == NCACN_NP) {
+ transport_protection = AUTHZ_TRANSPORT_PROTECTION_SMB;
+ }
+
+ become_root();
+ status = make_auth4_context(frame, &auth4_context);
+ unbecome_root();
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Unable to make auth context for authz log.\n");
+ TALLOC_FREE(frame);
+ return;
+ }
+
+ /*
+ * Log the authorization to this RPC interface. This
+ * covered ncacn_np pass-through auth, and anonymous
+ * DCE/RPC (eg epmapper, netlogon etc)
+ */
+ log_successful_authz_event(auth4_context->msg_ctx,
+ auth4_context->lp_ctx,
+ call->conn->remote_address,
+ call->conn->local_address,
+ "DCE/RPC",
+ auth_type,
+ transport_protection,
+ auth->session_info,
+ NULL /* client_audit_info */,
+ NULL /* server_audit_info */);
+
+ auth->auth_audited = true;
+
+ TALLOC_FREE(frame);
+}
+
+static int dcesrv_assoc_group_destructor(struct dcesrv_assoc_group *assoc_group)
+{
+ int ret;
+ ret = idr_remove(assoc_group->dce_ctx->assoc_groups_idr,
+ assoc_group->id);
+ if (ret != 0) {
+ DBG_ERR("Failed to remove assoc_group 0x%08x\n",
+ assoc_group->id);
+ }
+ return 0;
+}
+
+static NTSTATUS dcesrv_assoc_group_new(struct dcesrv_call_state *call)
+{
+ struct dcesrv_connection *conn = call->conn;
+ struct dcesrv_context *dce_ctx = conn->dce_ctx;
+ const struct dcesrv_endpoint *endpoint = conn->endpoint;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(endpoint->ep_description);
+ struct dcesrv_assoc_group *assoc_group = NULL;
+ int id;
+
+ assoc_group = talloc_zero(conn, struct dcesrv_assoc_group);
+ if (assoc_group == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ id = idr_get_new_random(dce_ctx->assoc_groups_idr,
+ assoc_group,
+ 1,
+ UINT16_MAX);
+ if (id == -1) {
+ TALLOC_FREE(assoc_group);
+ DBG_ERR("Out of association groups!\n");
+ return NT_STATUS_RPC_OUT_OF_RESOURCES;
+ }
+
+ assoc_group->transport = transport;
+ assoc_group->id = id;
+ assoc_group->dce_ctx = dce_ctx;
+
+ call->conn->assoc_group = assoc_group;
+
+ talloc_set_destructor(assoc_group, dcesrv_assoc_group_destructor);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_assoc_group_reference(struct dcesrv_call_state *call,
+ uint32_t assoc_group_id)
+{
+ struct dcesrv_connection *conn = call->conn;
+ const struct dcesrv_endpoint *endpoint = conn->endpoint;
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(endpoint->ep_description);
+ struct dcesrv_assoc_group *assoc_group = NULL;
+ void *id_ptr = NULL;
+
+ /* find an association group given a assoc_group_id */
+ id_ptr = idr_find(conn->dce_ctx->assoc_groups_idr, assoc_group_id);
+ if (id_ptr == NULL) {
+ /*
+ * FIXME If the association group is not found it has
+ * been created in other process (preforking daemons).
+ * Until this is properly fixed we just create a new
+ * association group in this process
+ */
+ DBG_NOTICE("Failed to find assoc_group 0x%08x in this "
+ "server process, creating a new one\n",
+ assoc_group_id);
+ return dcesrv_assoc_group_new(call);
+ }
+ assoc_group = talloc_get_type_abort(id_ptr, struct dcesrv_assoc_group);
+
+ if (assoc_group->transport != transport) {
+ const char *at =
+ derpc_transport_string_by_transport(
+ assoc_group->transport);
+ const char *ct =
+ derpc_transport_string_by_transport(
+ transport);
+
+ DBG_NOTICE("assoc_group 0x%08x (transport %s) "
+ "is not available on transport %s\n",
+ assoc_group_id, at, ct);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ conn->assoc_group = talloc_reference(conn, assoc_group);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcesrv_assoc_group_find(
+ struct dcesrv_call_state *call,
+ void *private_data)
+{
+ uint32_t assoc_group_id = call->pkt.u.bind.assoc_group_id;
+
+ if (assoc_group_id != 0) {
+ return dcesrv_assoc_group_reference(call, assoc_group_id);
+ }
+
+ /* If not requested by client create a new association group */
+ return dcesrv_assoc_group_new(call);
+}
+
+void dcesrv_transport_terminate_connection(struct dcesrv_connection *dce_conn,
+ const char *reason)
+{
+ struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
+ dce_conn->transport.private_data,
+ struct dcerpc_ncacn_conn);
+
+ ncacn_terminate_connection(ncacn_conn, reason);
+}
+
+static void ncacn_terminate_connection(struct dcerpc_ncacn_conn *conn,
+ const char *reason)
+{
+ if (reason == NULL) {
+ reason = "Unknown reason";
+ }
+
+ DBG_NOTICE("Terminating connection - '%s'\n", reason);
+
+ talloc_free(conn);
+}
+
+NTSTATUS dcesrv_endpoint_by_ncacn_np_name(struct dcesrv_context *dce_ctx,
+ const char *pipe_name,
+ struct dcesrv_endpoint **out)
+{
+ struct dcesrv_endpoint *e = NULL;
+
+ for (e = dce_ctx->endpoint_list; e; e = e->next) {
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(e->ep_description);
+ const char *endpoint = NULL;
+
+ if (transport != NCACN_NP) {
+ continue;
+ }
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description,
+ "endpoint");
+ if (endpoint == NULL) {
+ continue;
+ }
+
+ if (strncmp(endpoint, "\\pipe\\", 6) == 0) {
+ endpoint += 6;
+ }
+
+ if (strequal(endpoint, pipe_name)) {
+ *out = e;
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+struct pipes_struct *dcesrv_get_pipes_struct(struct dcesrv_connection *conn)
+{
+ struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
+ conn->transport.private_data,
+ struct dcerpc_ncacn_conn);
+
+ return &ncacn_conn->p;
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/rpc_server.h b/source3/rpc_server/rpc_server.h
new file mode 100644
index 0000000..73cd78a
--- /dev/null
+++ b/source3/rpc_server/rpc_server.h
@@ -0,0 +1,72 @@
+/*
+ * RPC Server helper headers
+ * Almost completely rewritten by (C) Jeremy Allison 2005 - 2010
+ * Copyright (C) Simo Sorce <idra@samba.org> - 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_SERVER_H_
+#define _RPC_SERVER_H_
+
+#include "librpc/rpc/rpc_common.h" /* For enum dcerpc_transport_t */
+
+#include "librpc/rpc/dcesrv_core.h"
+#include "rpc_pipes.h"
+
+struct auth_session_info;
+struct cli_credentials;
+
+typedef void (*dcerpc_ncacn_termination_fn)(struct dcesrv_connection *,
+ void *);
+
+struct dcerpc_ncacn_conn {
+ struct dcerpc_ncacn_conn *prev, *next;
+ int sock;
+
+ struct pipes_struct p;
+ dcerpc_ncacn_termination_fn termination_fn;
+ void *termination_data;
+
+ struct dcesrv_endpoint *endpoint;
+
+ char *remote_client_name;
+ char *local_server_name;
+};
+
+void set_incoming_fault(struct pipes_struct *p);
+void process_complete_pdu(struct pipes_struct *p, struct ncacn_packet *pkt);
+
+NTSTATUS dcesrv_auth_gensec_prepare(
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_call_state *call,
+ struct gensec_security **out,
+ void *private_data);
+void dcesrv_log_successful_authz(
+ struct dcesrv_call_state *call,
+ void *private_data);
+NTSTATUS dcesrv_assoc_group_find(
+ struct dcesrv_call_state *call,
+ void *private_data);
+
+NTSTATUS dcesrv_endpoint_by_ncacn_np_name(struct dcesrv_context *dce_ctx,
+ const char *endpoint,
+ struct dcesrv_endpoint **out);
+
+struct pipes_struct *dcesrv_get_pipes_struct(struct dcesrv_connection *conn);
+
+void dcesrv_transport_terminate_connection(struct dcesrv_connection *dce_conn,
+ const char *reason);
+
+#endif /* _PRC_SERVER_H_ */
diff --git a/source3/rpc_server/rpc_sock_helper.c b/source3/rpc_server/rpc_sock_helper.c
new file mode 100644
index 0000000..364b889
--- /dev/null
+++ b/source3/rpc_server/rpc_sock_helper.c
@@ -0,0 +1,399 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * RPC Socket Helper
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "ntdomain.h"
+
+#include "../lib/tsocket/tsocket.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "rpc_server/rpc_sock_helper.h"
+#include "librpc/ndr/ndr_table.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static NTSTATUS dcesrv_create_ncacn_np_socket(
+ struct dcerpc_binding *b, int *out_fd)
+{
+ char *np_dir = NULL;
+ int fd = -1;
+ NTSTATUS status;
+ const char *endpoint;
+ char *endpoint_normalized = NULL;
+ char *p = NULL;
+
+ endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ if (endpoint == NULL) {
+ DBG_ERR("Endpoint mandatory for named pipes\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* The endpoint string from IDL can be mixed uppercase and case is
+ * normalized by smbd on connection */
+ endpoint_normalized = strlower_talloc(talloc_tos(), endpoint);
+ if (endpoint_normalized == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The endpoint string from IDL can be prefixed by \pipe\ */
+ p = endpoint_normalized;
+ if (strncmp(p, "\\pipe\\", 6) == 0) {
+ p += 6;
+ }
+
+ /*
+ * As lp_ncalrpc_dir() should have 0755, but
+ * lp_ncalrpc_dir()/np should have 0700, we need to
+ * create lp_ncalrpc_dir() first.
+ */
+ if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create pipe directory %s - %s\n",
+ lp_ncalrpc_dir(), strerror(errno));
+ goto out;
+ }
+
+ np_dir = talloc_asprintf(talloc_tos(), "%s/np", lp_ncalrpc_dir());
+ if (!np_dir) {
+ status = NT_STATUS_NO_MEMORY;
+ DBG_ERR("Out of memory\n");
+ goto out;
+ }
+
+ if (!directory_create_or_exist_strict(np_dir, geteuid(), 0700)) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create pipe directory %s - %s\n",
+ np_dir, strerror(errno));
+ goto out;
+ }
+
+ fd = create_pipe_sock(np_dir, p, 0700);
+ if (fd == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create ncacn_np socket! '%s/%s': %s\n",
+ np_dir, p, strerror(errno));
+ goto out;
+ }
+
+ DBG_DEBUG("Opened pipe socket fd %d for %s\n", fd, p);
+
+ *out_fd = fd;
+
+ status = NT_STATUS_OK;
+
+out:
+ TALLOC_FREE(endpoint_normalized);
+ TALLOC_FREE(np_dir);
+ return status;
+}
+
+/********************************************************************
+ * Start listening on the tcp/ip socket
+ ********************************************************************/
+
+static NTSTATUS dcesrv_create_ncacn_ip_tcp_socket(
+ const struct sockaddr_storage *ifss, uint16_t *port, int *out_fd)
+{
+ int fd = -1;
+
+ if (*port == 0) {
+ static uint16_t low = 0;
+ uint16_t i;
+
+ if (low == 0) {
+ low = lp_rpc_low_port();
+ }
+
+ for (i = low; i <= lp_rpc_high_port(); i++) {
+ fd = open_socket_in(SOCK_STREAM, ifss, i, false);
+ if (fd >= 0) {
+ *port = i;
+ low = i+1;
+ break;
+ }
+ }
+ } else {
+ fd = open_socket_in(SOCK_STREAM, ifss, *port, true);
+ }
+
+ if (fd < 0) {
+ DBG_ERR("Failed to create socket on port %u!\n", *port);
+ return map_nt_error_from_unix(-fd);
+ }
+
+ /* ready to listen */
+ set_socket_options(fd, "SO_KEEPALIVE");
+ set_socket_options(fd, lp_socket_options());
+
+ DBG_DEBUG("Opened ncacn_ip_tcp socket fd %d for port %u\n", fd, *port);
+
+ *out_fd = fd;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_create_ncacn_ip_tcp_sockets(
+ struct dcerpc_binding *b,
+ TALLOC_CTX *mem_ctx,
+ size_t *pnum_fds,
+ int **pfds)
+{
+ uint16_t port = 0;
+ char port_str[11];
+ const char *endpoint = NULL;
+ size_t i = 0, num_fds;
+ int *fds = NULL;
+ struct samba_sockaddr *addrs = NULL;
+ NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+ bool ok;
+
+ endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ if (endpoint != NULL) {
+ port = atoi(endpoint);
+ }
+
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ num_fds = iface_count();
+ } else {
+ num_fds = 1;
+#ifdef HAVE_IPV6
+ num_fds += 1;
+#endif
+ }
+
+ addrs = talloc_array(mem_ctx, struct samba_sockaddr, num_fds);
+ if (addrs == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ fds = talloc_array(mem_ctx, int, num_fds);
+ if (fds == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ /*
+ * Fill "addrs"
+ */
+
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ for (i=0; i<num_fds; i++) {
+ const struct sockaddr_storage *ifss =
+ iface_n_sockaddr_storage(i);
+
+ ok = sockaddr_storage_to_samba_sockaddr(
+ &addrs[i], ifss);
+ if (!ok) {
+ i = 0; /* nothing to close */
+ goto fail;
+ }
+ }
+ } else {
+ struct sockaddr_storage ss = { .ss_family = 0 };
+
+#ifdef HAVE_IPV6
+ ok = interpret_string_addr(
+ &ss, "::", AI_NUMERICHOST|AI_PASSIVE);
+ if (!ok) {
+ goto fail;
+ }
+ ok = sockaddr_storage_to_samba_sockaddr(&addrs[0], &ss);
+ if (!ok) {
+ goto fail;
+ }
+#endif
+ ok = interpret_string_addr(
+ &ss, "0.0.0.0", AI_NUMERICHOST|AI_PASSIVE);
+ if (!ok) {
+ goto fail;
+ }
+
+ /* num_fds set above depending on HAVE_IPV6 */
+ ok = sockaddr_storage_to_samba_sockaddr(
+ &addrs[num_fds-1], &ss);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ for (i=0; i<num_fds; i++) {
+ status = dcesrv_create_ncacn_ip_tcp_socket(
+ &addrs[i].u.ss, &port, &fds[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ samba_sockaddr_set_port(&addrs[i], port);
+ }
+
+ /* Set the port in the endpoint */
+ snprintf(port_str, sizeof(port_str), "%"PRIu16, port);
+
+ status = dcerpc_binding_set_string_option(b, "endpoint", port_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set binding endpoint '%s': %s\n",
+ port_str, nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(addrs);
+
+ *pfds = fds;
+ *pnum_fds = num_fds;
+
+ return NT_STATUS_OK;
+
+fail:
+ while (i > 0) {
+ close(fds[i-1]);
+ i -= 1;
+ }
+ TALLOC_FREE(fds);
+ TALLOC_FREE(addrs);
+ return status;
+}
+
+/********************************************************************
+ * Start listening on the ncalrpc socket
+ ********************************************************************/
+
+static NTSTATUS dcesrv_create_ncalrpc_socket(
+ struct dcerpc_binding *b, int *out_fd)
+{
+ int fd = -1;
+ const char *endpoint = NULL;
+ NTSTATUS status;
+
+ endpoint = dcerpc_binding_get_string_option(b, "endpoint");
+ if (endpoint == NULL) {
+ /*
+ * No identifier specified: use DEFAULT or SMBD.
+ *
+ * When role is AD DC we run two rpc server instances, the one
+ * started by 'samba' and the one embedded in 'smbd'.
+ * Avoid listening in DEFAULT socket for NCALRPC as both
+ * servers will race to accept connections. In this case smbd
+ * will listen in SMBD socket and rpcint binding handle
+ * implementation will pick the right socket to use.
+ *
+ * TODO: DO NOT hardcode this value anywhere else. Rather,
+ * specify no endpoint and let the epmapper worry about it.
+ */
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ endpoint = "SMBD";
+ } else {
+ endpoint = "DEFAULT";
+ }
+ status = dcerpc_binding_set_string_option(
+ b, "endpoint", endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set ncalrpc 'endpoint' binding "
+ "string option to '%s': %s\n",
+ endpoint, nt_errstr(status));
+ return status;
+ }
+ }
+
+ if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create ncalrpc directory '%s': %s\n",
+ lp_ncalrpc_dir(), strerror(errno));
+ goto out;
+ }
+
+ fd = create_pipe_sock(lp_ncalrpc_dir(), endpoint, 0755);
+ if (fd == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create ncalrpc socket '%s/%s': %s\n",
+ lp_ncalrpc_dir(), endpoint, strerror(errno));
+ goto out;
+ }
+
+ DBG_DEBUG("Opened ncalrpc socket fd '%d' for '%s/%s'\n",
+ fd, lp_ncalrpc_dir(), endpoint);
+
+ *out_fd = fd;
+
+ return NT_STATUS_OK;
+
+out:
+ return status;
+}
+
+NTSTATUS dcesrv_create_binding_sockets(
+ struct dcerpc_binding *b,
+ TALLOC_CTX *mem_ctx,
+ size_t *pnum_fds,
+ int **pfds)
+{
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(b);
+ size_t i, num_fds = 1;
+ int *fds = NULL;
+ NTSTATUS status;
+
+ if ((transport == NCALRPC) || (transport == NCACN_NP)) {
+ fds = talloc(mem_ctx, int);
+ if (fds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ switch(transport) {
+ case NCALRPC:
+ status = dcesrv_create_ncalrpc_socket(b, fds);
+ break;
+ case NCACN_NP:
+ status = dcesrv_create_ncacn_np_socket(b, fds);
+ break;
+ case NCACN_IP_TCP:
+ status = dcesrv_create_ncacn_ip_tcp_sockets(
+ b, talloc_tos(), &num_fds, &fds);
+ break;
+ default:
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(fds);
+ return status;
+ }
+
+ for (i=0; i<num_fds; i++) {
+ bool ok = smb_set_close_on_exec(fds[i]);
+ if (!ok) {
+ status = map_nt_error_from_unix(errno);
+ break;
+ }
+ }
+ if (i < num_fds) {
+ for (i=0; i<num_fds; i++) {
+ close(fds[i]);
+ }
+ TALLOC_FREE(fds);
+ return status;
+ }
+
+ *pfds = fds;
+ *pnum_fds = num_fds;
+ return NT_STATUS_OK;
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/rpc_sock_helper.h b/source3/rpc_server/rpc_sock_helper.h
new file mode 100644
index 0000000..9c8128a
--- /dev/null
+++ b/source3/rpc_server/rpc_sock_helper.h
@@ -0,0 +1,36 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * RPC Socket Helper
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _RPC_SOCK_HELPER_H_
+#define _RPC_SOCK_HELPER_H_
+
+#include "rpc_server.h"
+
+NTSTATUS dcesrv_create_binding_sockets(
+ struct dcerpc_binding *b,
+ TALLOC_CTX *mem_ctx,
+ size_t *pnum_fds,
+ int **fds);
+
+#endif /* _RPC_SOCK_HELPER_H_ */
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/rpc_worker.c b/source3/rpc_server/rpc_worker.c
new file mode 100644
index 0000000..bf9671d
--- /dev/null
+++ b/source3/rpc_server/rpc_worker.c
@@ -0,0 +1,1287 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "source3/include/includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "rpc_worker.h"
+#include "rpc_config.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/rpc/dcerpc_util.h"
+#include "source3/librpc/gen_ndr/ndr_rpc_host.h"
+#include "lib/util/debug.h"
+#include "lib/util/fault.h"
+#include "rpc_server.h"
+#include "rpc_pipes.h"
+#include "source3/smbd/proto.h"
+#include "source3/lib/smbd_shim.h"
+#include "source3/lib/global_contexts.h"
+#include "source3/lib/util_procid.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/named_pipe_auth/npa_tstream.h"
+#include "libcli/smb/smb_constants.h"
+#include "lib/param/param.h"
+#include "lib/util/idtree_random.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/async_req/async_sock.h"
+#include "lib/util/dlinklist.h"
+#include "source3/include/auth.h"
+#include "nsswitch/winbind_client.h"
+#include "source3/include/messages.h"
+#include "libcli/security/security_token.h"
+#include "libcli/security/dom_sid.h"
+#include "source3/include/proto.h"
+
+/*
+ * This is the generic code that becomes the
+ * template that all rpcd_* instances that
+ * serve DCERPC can use to provide services to samba-dcerpcd.
+ *
+ * The external entry point is:
+ * rpc_worker_main() which takes an argc/argv list
+ * and two functions:
+ *
+ * get_interfaces() - List all interfaces that this server provides
+ * get_servers() - Provide the RPC server implementations
+ *
+ * Each rpcd_* service needs only to provide
+ * the implementations of get_interfaces() and get_servers()
+ * and call rpc_worker_main() from their main() function
+ * to provide services that can be connected to from samba-dcerpcd.
+ */
+
+struct rpc_worker {
+ struct dcerpc_ncacn_conn *conns;
+ struct server_id rpc_host_pid;
+ struct messaging_context *msg_ctx;
+ struct dcesrv_context *dce_ctx;
+
+ struct dcesrv_context_callbacks cb;
+
+ struct rpc_worker_status status;
+
+ bool done;
+};
+
+static void rpc_worker_print_interface(
+ FILE *f, const struct ndr_interface_table *t)
+{
+ const struct ndr_interface_string_array *endpoints = t->endpoints;
+ uint32_t i;
+ struct ndr_syntax_id_buf id_buf;
+
+ fprintf(f,
+ "%s %s\n",
+ ndr_syntax_id_buf_string(&t->syntax_id, &id_buf),
+ t->name);
+
+ for (i=0; i<endpoints->count; i++) {
+ fprintf(f, " %s\n", endpoints->names[i]);
+ }
+}
+
+static NTSTATUS rpc_worker_report_status(struct rpc_worker *worker)
+{
+ uint8_t buf[16];
+ DATA_BLOB blob = { .data = buf, .length = sizeof(buf), };
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ worker->status.num_association_groups = worker->dce_ctx->assoc_groups_num;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(rpc_worker_status, &worker->status);
+ }
+
+ ndr_err = ndr_push_struct_into_fixed_blob(
+ &blob,
+ &worker->status,
+ (ndr_push_flags_fn_t)ndr_push_rpc_worker_status);
+ SMB_ASSERT(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+
+ status = messaging_send(
+ worker->msg_ctx,
+ worker->rpc_host_pid,
+ MSG_RPC_WORKER_STATUS,
+ &blob);
+ return status;
+}
+
+static void rpc_worker_connection_terminated(
+ struct dcesrv_connection *conn, void *private_data)
+{
+ struct rpc_worker *worker = talloc_get_type_abort(
+ private_data, struct rpc_worker);
+ struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
+ conn->transport.private_data, struct dcerpc_ncacn_conn);
+ struct dcerpc_ncacn_conn *w = NULL;
+ NTSTATUS status;
+ bool found = false;
+
+ /*
+ * We need to drop the association group reference
+ * explicitly here in order to avoid the order given
+ * by the destructors. rpc_worker_report_status() below,
+ * expects worker->dce_ctx->assoc_groups_num to be updated
+ * already.
+ */
+ if (conn->assoc_group != NULL) {
+ talloc_unlink(conn, conn->assoc_group);
+ conn->assoc_group = NULL;
+ }
+
+ SMB_ASSERT(worker->status.num_connections > 0);
+
+ for (w = worker->conns; w != NULL; w = w->next) {
+ if (w == ncacn_conn) {
+ found = true;
+ break;
+ }
+ }
+ SMB_ASSERT(found);
+
+ DLIST_REMOVE(worker->conns, ncacn_conn);
+
+ worker->status.num_connections -= 1;
+
+ status = rpc_worker_report_status(worker);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_worker_report_status returned %s\n",
+ nt_errstr(status));
+ }
+}
+
+static int dcesrv_connection_destructor(struct dcesrv_connection *conn)
+{
+ struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
+ conn->transport.private_data,
+ struct dcerpc_ncacn_conn);
+
+ if (ncacn_conn->termination_fn != NULL) {
+ ncacn_conn->termination_fn(conn, ncacn_conn->termination_data);
+ }
+
+ return 0;
+}
+
+/*
+ * A new client has been passed to us from samba-dcerpcd.
+ */
+static void rpc_worker_new_client(
+ struct rpc_worker *worker,
+ struct rpc_host_client *client,
+ int sock)
+{
+ struct dcesrv_context *dce_ctx = worker->dce_ctx;
+ struct named_pipe_auth_req_info8 *info8 = client->npa_info8;
+ struct tsocket_address *remote_client_addr = NULL;
+ struct tsocket_address *local_server_addr = NULL;
+ struct dcerpc_binding *b = NULL;
+ enum dcerpc_transport_t transport;
+ struct dcesrv_endpoint *ep = NULL;
+ struct tstream_context *tstream = NULL;
+ struct dcerpc_ncacn_conn *ncacn_conn = NULL;
+ struct dcesrv_connection *dcesrv_conn = NULL;
+ DATA_BLOB buffer = { .data = NULL };
+ struct ncacn_packet *pkt = NULL;
+ struct security_token *token = NULL;
+ uint32_t npa_flags, state_flags;
+ bool found_npa_flags;
+ NTSTATUS status;
+ int ret;
+
+ DBG_DEBUG("Got new conn sock %d for binding %s\n",
+ sock,
+ client->binding);
+
+ status = dcerpc_parse_binding(client, client->binding, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_parse_binding(%s) failed: %s\n",
+ client->binding,
+ nt_errstr(status));
+ goto fail;
+ }
+ transport = dcerpc_binding_get_transport(b);
+
+ status = dcesrv_find_endpoint(dce_ctx, b, &ep);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND) &&
+ ((transport == NCACN_IP_TCP) || (transport == NCALRPC)) &&
+ (dcerpc_binding_get_string_option(b, "endpoint") != NULL)) {
+ /*
+ * We have two kinds of servers: Those who explicitly
+ * bind to a port (e.g. 135 for epmapper) and those
+ * who just specify a transport. The client specified
+ * a port (or socket name), but we did not find this
+ * in the list of servers having specified a
+ * port. Retry just matching for the transport,
+ * catching the servers that did not explicitly
+ * specify a port.
+ *
+ * This is not fully correct, what we should do is
+ * that once the port the server listens on has been
+ * finalized we should mark this in the server list,
+ * but for now it works. We don't have the same RPC
+ * interface listening twice on different ports.
+ */
+ struct dcerpc_binding *b_without_port = dcerpc_binding_dup(
+ client, b);
+ if (b_without_port == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = dcerpc_binding_set_string_option(
+ b_without_port, "endpoint", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not delete endpoint: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(b_without_port);
+ goto fail;
+ }
+
+ status = dcesrv_find_endpoint(dce_ctx, b_without_port, &ep);
+
+ TALLOC_FREE(b_without_port);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not find endpoint for %s: %s\n",
+ client->binding,
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ncacn_conn = talloc(dce_ctx, struct dcerpc_ncacn_conn);
+ if (ncacn_conn == NULL) {
+ DBG_DEBUG("talloc failed\n");
+ goto fail;
+ }
+ *ncacn_conn = (struct dcerpc_ncacn_conn) {
+ .endpoint = ep,
+ .sock = sock,
+ .termination_fn = rpc_worker_connection_terminated,
+ .termination_data = worker,
+ };
+
+ if (transport == NCALRPC) {
+ ret = tsocket_address_unix_from_path(ncacn_conn,
+ info8->remote_client_addr,
+ &remote_client_addr);
+ if (ret == -1) {
+ DBG_DEBUG("tsocket_address_unix_from_path"
+ "(%s) failed: %s\n",
+ info8->remote_client_addr,
+ strerror(errno));
+ goto fail;
+ }
+
+ ncacn_conn->remote_client_name =
+ talloc_strdup(ncacn_conn, info8->remote_client_name);
+ if (ncacn_conn->remote_client_name == NULL) {
+ DBG_DEBUG("talloc_strdup(%s) failed\n",
+ info8->remote_client_name);
+ goto fail;
+ }
+
+ ret = tsocket_address_unix_from_path(ncacn_conn,
+ info8->local_server_addr,
+ &local_server_addr);
+ if (ret == -1) {
+ DBG_DEBUG("tsocket_address_unix_from_path"
+ "(%s) failed: %s\n",
+ info8->local_server_addr,
+ strerror(errno));
+ goto fail;
+ }
+
+ ncacn_conn->local_server_name =
+ talloc_strdup(ncacn_conn, info8->local_server_name);
+ if (ncacn_conn->local_server_name == NULL) {
+ DBG_DEBUG("talloc_strdup(%s) failed\n",
+ info8->local_server_name);
+ goto fail;
+ }
+ } else {
+ ret = tsocket_address_inet_from_strings(
+ ncacn_conn,
+ "ip",
+ info8->remote_client_addr,
+ info8->remote_client_port,
+ &remote_client_addr);
+ if (ret == -1) {
+ DBG_DEBUG("tsocket_address_inet_from_strings"
+ "(%s, %" PRIu16 ") failed: %s\n",
+ info8->remote_client_addr,
+ info8->remote_client_port,
+ strerror(errno));
+ goto fail;
+ }
+ ncacn_conn->remote_client_name =
+ talloc_strdup(ncacn_conn, info8->remote_client_name);
+ if (ncacn_conn->remote_client_name == NULL) {
+ DBG_DEBUG("talloc_strdup(%s) failed\n",
+ info8->remote_client_name);
+ goto fail;
+ }
+
+ ret = tsocket_address_inet_from_strings(
+ ncacn_conn,
+ "ip",
+ info8->local_server_addr,
+ info8->local_server_port,
+ &local_server_addr);
+ if (ret == -1) {
+ DBG_DEBUG("tsocket_address_inet_from_strings"
+ "(%s, %" PRIu16 ") failed: %s\n",
+ info8->local_server_addr,
+ info8->local_server_port,
+ strerror(errno));
+ goto fail;
+ }
+ ncacn_conn->local_server_name =
+ talloc_strdup(ncacn_conn, info8->local_server_name);
+ if (ncacn_conn->local_server_name == NULL) {
+ DBG_DEBUG("talloc_strdup(%s) failed\n",
+ info8->local_server_name);
+ goto fail;
+ }
+ }
+
+ if (transport == NCACN_NP) {
+ ret = tstream_npa_existing_socket(
+ ncacn_conn,
+ sock,
+ FILE_TYPE_MESSAGE_MODE_PIPE,
+ &tstream);
+ if (ret == -1) {
+ DBG_DEBUG("tstream_npa_existing_socket failed: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * "transport" so far is implicitly assigned by the
+ * socket that the client connected to, passed in from
+ * samba-dcerpcd via the binding. For NCACN_NP (root
+ * only by unix permissions) we got a
+ * named_pipe_auth_req_info8 where the transport can
+ * be overridden.
+ */
+ transport = info8->transport;
+ } else {
+ ret = tstream_bsd_existing_socket(
+ ncacn_conn, sock, &tstream);
+ if (ret == -1) {
+ DBG_DEBUG("tstream_bsd_existing_socket failed: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+ /* as server we want to fail early */
+ tstream_bsd_fail_readv_first_error(tstream, true);
+ }
+ sock = -1;
+
+ token = info8->session_info->session_info->security_token;
+
+ if (security_token_is_system(token) && (transport != NCALRPC)) {
+ DBG_DEBUG("System token only allowed on NCALRPC\n");
+ goto fail;
+ }
+
+ state_flags = DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
+
+ found_npa_flags = security_token_find_npa_flags(token, &npa_flags);
+ if (found_npa_flags) {
+ if (npa_flags & SAMBA_NPA_FLAGS_WINBIND_OFF) {
+ state_flags |=
+ DCESRV_CALL_STATE_FLAG_WINBIND_OFF;
+ }
+
+ /*
+ * Delete the flags so that we don't bail in
+ * local_np_connect_send() on subsequent
+ * connects. Once we connect to another RPC service, a
+ * new flags sid will be added if required.
+ */
+ security_token_del_npa_flags(token);
+ }
+
+ ncacn_conn->p.msg_ctx = global_messaging_context();
+ ncacn_conn->p.transport = transport;
+
+ status = dcesrv_endpoint_connect(dce_ctx,
+ ncacn_conn,
+ ep,
+ info8->session_info->session_info,
+ global_event_context(),
+ state_flags,
+ &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Failed to connect to endpoint: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ talloc_set_destructor(dcesrv_conn, dcesrv_connection_destructor);
+
+ dcesrv_conn->transport.private_data = ncacn_conn;
+ dcesrv_conn->transport.report_output_data =
+ dcesrv_sock_report_output_data;
+ dcesrv_conn->transport.terminate_connection =
+ dcesrv_transport_terminate_connection;
+
+ dcesrv_conn->send_queue = tevent_queue_create(
+ dcesrv_conn, "dcesrv send queue");
+ if (dcesrv_conn->send_queue == NULL) {
+ DBG_DEBUG("tevent_queue_create failed\n");
+ goto fail;
+ }
+
+ dcesrv_conn->stream = talloc_move(dcesrv_conn, &tstream);
+ dcesrv_conn->local_address =
+ talloc_move(dcesrv_conn, &local_server_addr);
+ dcesrv_conn->remote_address =
+ talloc_move(dcesrv_conn, &remote_client_addr);
+
+ if (client->bind_packet.length == 0) {
+ DBG_DEBUG("Expected bind packet\n");
+ goto fail;
+ }
+
+ buffer = (DATA_BLOB) {
+ .data = talloc_move(dcesrv_conn, &client->bind_packet.data),
+ .length = client->bind_packet.length,
+ };
+
+ pkt = talloc(dcesrv_conn, struct ncacn_packet);
+ if (pkt == NULL) {
+ DBG_DEBUG("talloc failed\n");
+ goto fail;
+ }
+
+ status = dcerpc_pull_ncacn_packet(pkt, &buffer, pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_pull_ncacn_packet failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ TALLOC_FREE(client);
+
+ DLIST_ADD(worker->conns, ncacn_conn);
+ worker->status.num_connections += 1;
+
+ dcesrv_loop_next_packet(dcesrv_conn, pkt, buffer);
+
+ return;
+fail:
+ TALLOC_FREE(ncacn_conn);
+ TALLOC_FREE(dcesrv_conn);
+ TALLOC_FREE(client);
+ if (sock != -1) {
+ close(sock);
+ }
+
+ /*
+ * Parent thinks it successfully sent us a client. Tell it
+ * that we declined.
+ */
+ status = rpc_worker_report_status(worker);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("rpc_worker_report_status returned %s\n",
+ nt_errstr(status));
+ }
+}
+
+/*
+ * New client message processing.
+ */
+static bool rpc_worker_new_client_filter(
+ struct messaging_rec *rec, void *private_data)
+{
+ struct rpc_worker *worker = talloc_get_type_abort(
+ private_data, struct rpc_worker);
+ struct dcesrv_context *dce_ctx = worker->dce_ctx;
+ struct rpc_host_client *client = NULL;
+ enum ndr_err_code ndr_err;
+ int sock;
+
+ if (rec->msg_type != MSG_RPC_HOST_NEW_CLIENT) {
+ return false;
+ }
+
+ if (rec->num_fds != 1) {
+ DBG_DEBUG("Got %"PRIu8" fds\n", rec->num_fds);
+ return false;
+ }
+
+ client = talloc(dce_ctx, struct rpc_host_client);
+ if (client == NULL) {
+ DBG_DEBUG("talloc failed\n");
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &rec->buf,
+ client,
+ client,
+ (ndr_pull_flags_fn_t)ndr_pull_rpc_host_client);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_DEBUG("ndr_pull_rpc_host_client failed: %s\n",
+ ndr_errstr(ndr_err));
+ TALLOC_FREE(client);
+ return false;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(rpc_host_client, client);
+ }
+
+ sock = rec->fds[0];
+ rec->fds[0] = -1;
+
+ rpc_worker_new_client(worker, client, sock);
+
+ return false;
+}
+
+/*
+ * Return your status message processing.
+ */
+static bool rpc_worker_status_filter(
+ struct messaging_rec *rec, void *private_data)
+{
+ struct rpc_worker *worker = talloc_get_type_abort(
+ private_data, struct rpc_worker);
+ struct dcerpc_ncacn_conn *conn = NULL;
+ FILE *f = NULL;
+ int fd;
+
+ if (rec->msg_type != MSG_RPC_DUMP_STATUS) {
+ return false;
+ }
+
+ if (rec->num_fds != 1) {
+ DBG_DEBUG("Got %"PRIu8" fds\n", rec->num_fds);
+ return false;
+ }
+
+ fd = dup(rec->fds[0]);
+ if (fd == -1) {
+ DBG_DEBUG("dup(%"PRIi64") failed: %s\n",
+ rec->fds[0],
+ strerror(errno));
+ return false;
+ }
+
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ DBG_DEBUG("fdopen failed: %s\n", strerror(errno));
+ close(fd);
+ return false;
+ }
+
+ for (conn = worker->conns; conn != NULL; conn = conn->next) {
+ char *endpoint = NULL;
+
+ endpoint = dcerpc_binding_string(
+ conn, conn->endpoint->ep_description);
+
+ fprintf(f,
+ "endpoint=%s client=%s server=%s\n",
+ endpoint ? endpoint : "UNKNOWN",
+ conn->remote_client_name,
+ conn->local_server_name);
+ TALLOC_FREE(endpoint);
+ }
+
+ fclose(f);
+
+ return false;
+}
+
+/*
+ take a reference to an existing association group
+ */
+static struct dcesrv_assoc_group *rpc_worker_assoc_group_reference(
+ struct dcesrv_connection *conn,
+ uint32_t id)
+{
+ const struct dcesrv_endpoint *endpoint = conn->endpoint;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(
+ endpoint->ep_description);
+ struct dcesrv_assoc_group *assoc_group = NULL;
+ void *id_ptr = NULL;
+
+ /* find an association group given a assoc_group_id */
+ id_ptr = idr_find(conn->dce_ctx->assoc_groups_idr, id & UINT16_MAX);
+ if (id_ptr == NULL) {
+ DBG_NOTICE("Failed to find assoc_group 0x%08x\n", id);
+ return NULL;
+ }
+ assoc_group = talloc_get_type_abort(id_ptr, struct dcesrv_assoc_group);
+
+ if (assoc_group->transport != transport) {
+ const char *at = derpc_transport_string_by_transport(
+ assoc_group->transport);
+ const char *ct = derpc_transport_string_by_transport(
+ transport);
+
+ DBG_NOTICE("assoc_group 0x%08x (transport %s) "
+ "is not available on transport %s\n",
+ id, at, ct);
+ return NULL;
+ }
+
+ /*
+ * Yes, this is a talloc_reference: The assoc group must be
+ * removed when all connections go. This should be replaced by
+ * adding a linked list of dcesrv_connection structs to the
+ * assoc group.
+ */
+ return talloc_reference(conn, assoc_group);
+}
+
+static int rpc_worker_assoc_group_destructor(
+ struct dcesrv_assoc_group *assoc_group)
+{
+ int ret;
+
+ ret = idr_remove(
+ assoc_group->dce_ctx->assoc_groups_idr,
+ assoc_group->id & UINT16_MAX);
+ if (ret != 0) {
+ DBG_WARNING("Failed to remove assoc_group 0x%08x\n",
+ assoc_group->id);
+ }
+
+ SMB_ASSERT(assoc_group->dce_ctx->assoc_groups_num > 0);
+ assoc_group->dce_ctx->assoc_groups_num -= 1;
+ return 0;
+}
+
+/*
+ allocate a new association group
+ */
+static struct dcesrv_assoc_group *rpc_worker_assoc_group_new(
+ struct dcesrv_connection *conn, uint16_t worker_index)
+{
+ struct dcesrv_context *dce_ctx = conn->dce_ctx;
+ const struct dcesrv_endpoint *endpoint = conn->endpoint;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(
+ endpoint->ep_description);
+ struct dcesrv_assoc_group *assoc_group = NULL;
+ int id;
+
+ assoc_group = talloc_zero(conn, struct dcesrv_assoc_group);
+ if (assoc_group == NULL) {
+ return NULL;
+ }
+
+ /*
+ * We use 16-bit to encode the worker index,
+ * have 16-bits left within the worker to form a
+ * 32-bit association group id.
+ */
+ id = idr_get_new_random(
+ dce_ctx->assoc_groups_idr, assoc_group, 1, UINT16_MAX);
+ if (id == -1) {
+ talloc_free(assoc_group);
+ DBG_WARNING("Out of association groups!\n");
+ return NULL;
+ }
+ assoc_group->id = (((uint32_t)worker_index) << 16) | id;
+ assoc_group->transport = transport;
+ assoc_group->dce_ctx = dce_ctx;
+
+ talloc_set_destructor(assoc_group, rpc_worker_assoc_group_destructor);
+
+ SMB_ASSERT(dce_ctx->assoc_groups_num < UINT16_MAX);
+ dce_ctx->assoc_groups_num += 1;
+
+ return assoc_group;
+}
+
+static NTSTATUS rpc_worker_assoc_group_find(
+ struct dcesrv_call_state *call,
+ void *private_data)
+{
+ struct rpc_worker *w = talloc_get_type_abort(
+ private_data, struct rpc_worker);
+ uint32_t assoc_group_id = call->pkt.u.bind.assoc_group_id;
+
+ if (assoc_group_id != 0) {
+ uint16_t worker_index = (assoc_group_id & 0xffff0000) >> 16;
+ if (worker_index != w->status.worker_index) {
+ DBG_DEBUG("Wrong worker id %"PRIu16", "
+ "expected %"PRIu32"\n",
+ worker_index,
+ w->status.worker_index);
+ return NT_STATUS_NOT_FOUND;
+ }
+ call->conn->assoc_group = rpc_worker_assoc_group_reference(
+ call->conn, assoc_group_id);
+ } else {
+ call->conn->assoc_group = rpc_worker_assoc_group_new(
+ call->conn, w->status.worker_index);
+ }
+
+ if (call->conn->assoc_group == NULL) {
+ /* TODO Return correct status */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static struct rpc_worker *rpc_worker_new(
+ TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx)
+{
+ struct rpc_worker *worker = NULL;
+
+ worker = talloc_zero(mem_ctx, struct rpc_worker);
+ if (worker == NULL) {
+ return NULL;
+ }
+
+ worker->rpc_host_pid = (struct server_id) { .pid = 0 };
+ worker->msg_ctx = msg_ctx;
+
+ worker->cb = (struct dcesrv_context_callbacks) {
+ .log.successful_authz = dcesrv_log_successful_authz,
+ .auth.gensec_prepare = dcesrv_auth_gensec_prepare,
+ .auth.become_root = become_root,
+ .auth.unbecome_root = unbecome_root,
+ .assoc_group.find = rpc_worker_assoc_group_find,
+ .assoc_group.private_data = worker,
+ };
+
+ worker->dce_ctx = global_dcesrv_context();
+ if (worker->dce_ctx == NULL) {
+ goto fail;
+ }
+ dcesrv_context_set_callbacks(worker->dce_ctx, &worker->cb);
+
+ return worker;
+fail:
+ TALLOC_FREE(worker);
+ return NULL;
+}
+
+static struct dcesrv_context *rpc_worker_dce_ctx(struct rpc_worker *w)
+{
+ return w->dce_ctx;
+}
+
+struct rpc_worker_state {
+ struct tevent_context *ev;
+ struct rpc_worker *w;
+ struct tevent_req *new_client_req;
+ struct tevent_req *status_req;
+ struct tevent_req *finish_req;
+};
+
+static void rpc_worker_done(struct tevent_req *subreq);
+static void rpc_worker_shutdown(
+ struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+
+static struct tevent_req *rpc_worker_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct rpc_worker *w,
+ pid_t rpc_host_pid,
+ int server_index,
+ int worker_index)
+{
+ struct tevent_req *req = NULL;
+ struct rpc_worker_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct rpc_worker_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->w = w;
+
+ if ((server_index < 0) || ((unsigned)server_index > UINT32_MAX)) {
+ DBG_ERR("Invalid server index %d\n", server_index);
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+ if ((worker_index < 0) || ((unsigned)worker_index > UINT16_MAX)) {
+ DBG_ERR("Invalid worker index %d\n", worker_index);
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+ w->rpc_host_pid = pid_to_procid(rpc_host_pid);
+
+ w->status = (struct rpc_worker_status) {
+ .server_index = server_index,
+ .worker_index = worker_index,
+ };
+
+ /* Wait for new client messages. */
+ state->new_client_req = messaging_filtered_read_send(
+ w,
+ messaging_tevent_context(w->msg_ctx),
+ w->msg_ctx,
+ rpc_worker_new_client_filter,
+ w);
+ if (tevent_req_nomem(state->new_client_req, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* Wait for report your status messages. */
+ state->status_req = messaging_filtered_read_send(
+ w,
+ messaging_tevent_context(w->msg_ctx),
+ w->msg_ctx,
+ rpc_worker_status_filter,
+ w);
+ if (tevent_req_nomem(state->status_req, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* Wait for shutdown messages. */
+ status = messaging_register(
+ w->msg_ctx, req, MSG_SHUTDOWN, rpc_worker_shutdown);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("messaging_register failed: %s\n",
+ nt_errstr(status));
+ tevent_req_error(req, map_errno_from_nt_status(status));
+ return tevent_req_post(req, ev);
+ }
+
+ state->finish_req = wait_for_read_send(state, ev, 0, false);
+ if (tevent_req_nomem(state->finish_req, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->finish_req, rpc_worker_done, req);
+
+ rpc_worker_report_status(w);
+
+ return req;
+}
+
+static void rpc_worker_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int err = 0;
+ bool ok;
+
+ ok = wait_for_read_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, err);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void rpc_worker_shutdown(
+ struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct tevent_req *req = talloc_get_type_abort(
+ private_data, struct tevent_req);
+ tevent_req_done(req);
+}
+
+static int rpc_worker_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+static void sig_term_handler(
+ struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ exit(0);
+}
+
+static void sig_hup_handler(
+ struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ change_to_root_user();
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+}
+
+static NTSTATUS register_ep_server(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ NTSTATUS status;
+
+ DBG_DEBUG("Registering server %s\n", ep_server->name);
+
+ status = dcerpc_register_ep_server(ep_server);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ DBG_ERR("Failed to register '%s' endpoint server: %s\n",
+ ep_server->name,
+ nt_errstr(status));
+ return status;
+ }
+
+ status = dcesrv_init_ep_server(dce_ctx, ep_server->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("dcesrv_init_ep_server(%s) failed: %s\n",
+ ep_server->name,
+ nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * @brief Main function for RPC server implementations
+ *
+ * This function provides all that is necessary to run a RPC server
+ * inside the samba-dcerpcd framework. Just pass argv and argc on to
+ * this function.
+ *
+ * The get_interfaces() callback provides the information that is
+ * passed to samba-dcerpcd via --list-interfaces, it should not do any
+ * real RPC server initialization work. Quickly after this function is
+ * called by rpc_worker_main, the process exits again. It should
+ * return the number of interfaces provided.
+ *
+ * get_servers() is called when the process is about to do the real
+ * work. So more heavy-weight initialization should happen here. It
+ * should return NT_STATUS_OK and the number of server implementations provided.
+ *
+ * @param[in] argc argc from main()
+ * @param[in] argv argv from main()
+ * @param[in] get_interfaces List all interfaces that this server provides
+ * @param[in] get_servers Provide the RPC server implementations
+ * @param[in] private_data Passed to the callback functions
+ * @return 0 It should never return except on successful process exit
+ */
+
+int rpc_worker_main(
+ int argc,
+ const char *argv[],
+ const char *daemon_config_name,
+ int num_workers,
+ int idle_seconds,
+ size_t (*get_interfaces)(
+ const struct ndr_interface_table ***ifaces,
+ void *private_data),
+ NTSTATUS (*get_servers)(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***ep_servers,
+ size_t *num_ep_servers,
+ void *private_data),
+ void *private_data)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *progname = getprogname();
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev_ctx = NULL;
+ struct tevent_req *req = NULL;
+ struct messaging_context *msg_ctx = NULL;
+ struct dcesrv_context *dce_ctx = NULL;
+ struct tevent_signal *se = NULL;
+ poptContext pc;
+ int opt;
+ NTSTATUS status;
+ int ret;
+ int worker_group = -1;
+ int worker_index = -1;
+ bool log_stdout;
+ int list_interfaces = 0;
+ struct rpc_worker *worker = NULL;
+ const struct dcesrv_endpoint_server **ep_servers;
+ size_t i, num_servers;
+ bool ok;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "list-interfaces",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &list_interfaces,
+ .descrip = "List the interfaces provided",
+ },
+ {
+ .longName = "worker-group",
+ .argInfo = POPT_ARG_INT,
+ .arg = &worker_group,
+ .descrip = "Group index in status message",
+ },
+ {
+ .longName = "worker-index",
+ .argInfo = POPT_ARG_INT,
+ .arg = &worker_index,
+ .descrip = "Worker index in status message",
+ },
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ static const struct smbd_shim smbd_shim_fns = {
+ .become_authenticated_pipe_user =
+ smbd_become_authenticated_pipe_user,
+ .unbecome_authenticated_pipe_user =
+ smbd_unbecome_authenticated_pipe_user,
+ .become_root = smbd_become_root,
+ .unbecome_root = smbd_unbecome_root,
+ };
+
+ closefrom(3);
+ talloc_enable_null_tracking();
+ frame = talloc_stackframe();
+ umask(0);
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+ true /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(ENOMEM);
+ }
+
+ pc = samba_popt_get_context(progname, argc, argv, long_options, 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ d_fprintf(stderr,
+ "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0),
+ poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ TALLOC_FREE(frame);
+ exit(1);
+ };
+ poptFreeContext(pc);
+
+ if (list_interfaces != 0) {
+ const struct ndr_interface_table **ifaces = NULL;
+ size_t num_ifaces;
+
+ num_workers = lp_parm_int(
+ -1, daemon_config_name, "num_workers", num_workers);
+ idle_seconds = lp_parm_int(
+ -1, daemon_config_name, "idle_seconds", idle_seconds);
+
+ DBG_DEBUG("daemon=%s, num_workers=%d, idle_seconds=%d\n",
+ daemon_config_name,
+ num_workers,
+ idle_seconds);
+
+ fprintf(stdout, "%d\n%d\n", num_workers, idle_seconds);
+
+ num_ifaces = get_interfaces(&ifaces, private_data);
+
+ for (i=0; i<num_ifaces; i++) {
+ rpc_worker_print_interface(stdout, ifaces[i]);
+ }
+
+ TALLOC_FREE(frame);
+ exit(0);
+ }
+
+ log_stdout = (debug_get_log_type() == DEBUG_STDOUT);
+ if (log_stdout != 0) {
+ setup_logging(argv[0], DEBUG_STDOUT);
+ } else {
+ setup_logging(argv[0], DEBUG_FILE);
+ }
+
+ set_smbd_shim(&smbd_shim_fns);
+
+ dump_core_setup(progname, lp_logfile(talloc_tos(), lp_sub));
+
+ /* POSIX demands that signals are inherited. If the invoking
+ * process has these signals masked, we will have problems, as
+ * we won't receive them. */
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGTERM);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(True,SIGFPE);
+#endif
+ /* We no longer use USR2... */
+#if defined(SIGUSR2)
+ BlockSignals(True, SIGUSR2);
+#endif
+ /* Ignore children - no zombies. */
+ CatchChild();
+
+ reopen_logs();
+
+ DBG_STARTUP_NOTICE("%s version %s started.\n%s\n",
+ progname,
+ samba_version_string(),
+ samba_copyright_string());
+
+ msg_ctx = global_messaging_context();
+ if (msg_ctx == NULL) {
+ DBG_ERR("global_messaging_context() failed\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ ev_ctx = messaging_tevent_context(msg_ctx);
+
+ worker = rpc_worker_new(ev_ctx, msg_ctx);
+ if (worker == NULL) {
+ DBG_ERR("rpc_worker_new failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ dce_ctx = rpc_worker_dce_ctx(worker);
+
+ se = tevent_add_signal(
+ ev_ctx, ev_ctx, SIGTERM, 0, sig_term_handler, NULL);
+ if (se == NULL) {
+ DBG_ERR("tevent_add_signal failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ BlockSignals(false, SIGTERM);
+
+ se = tevent_add_signal(
+ ev_ctx, ev_ctx, SIGHUP, 0, sig_hup_handler, NULL);
+ if (se == NULL) {
+ DBG_ERR("tevent_add_signal failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ BlockSignals(false, SIGHUP);
+
+ (void)winbind_off();
+ ok = init_guest_session_info(NULL);
+ (void)winbind_on();
+ if (!ok) {
+ DBG_WARNING("init_guest_session_info failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ status = init_system_session_info(NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("init_system_session_info failed: %s\n",
+ nt_errstr(status));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
+
+ status = get_servers(dce_ctx,
+ &ep_servers,
+ &num_servers,
+ private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("get_servers failed: %s\n", nt_errstr(status));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ DBG_DEBUG("get_servers() returned %zu servers\n", num_servers);
+
+ for (i=0; i<num_servers; i++) {
+ status = register_ep_server(dce_ctx, ep_servers[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("register_ep_server failed: %s\n",
+ nt_errstr(status));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ }
+
+ req = rpc_worker_send(
+ ev_ctx, ev_ctx, worker, getppid(), worker_group, worker_index);
+ if (req == NULL) {
+ DBG_ERR("rpc_worker_send failed\n");
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ DBG_DEBUG("%s worker running\n", progname);
+
+ while (tevent_req_is_in_progress(req)) {
+ TALLOC_CTX *loop_frame = NULL;
+
+ loop_frame = talloc_stackframe();
+
+ ret = tevent_loop_once(ev_ctx);
+
+ TALLOC_FREE(loop_frame);
+
+ if (ret != 0) {
+ DBG_WARNING("tevent_req_once() failed: %s\n",
+ strerror(errno));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ }
+
+ status = dcesrv_shutdown_registered_ep_servers(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Shutdown failed with: %s\n",
+ nt_errstr(status));
+ }
+
+ ret = rpc_worker_recv(req);
+ if (ret != 0) {
+ DBG_DEBUG("rpc_worker_recv returned %s\n", strerror(ret));
+ global_messaging_context_free();
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/rpc_server/rpc_worker.h b/source3/rpc_server/rpc_worker.h
new file mode 100644
index 0000000..54cc53f
--- /dev/null
+++ b/source3/rpc_server/rpc_worker.h
@@ -0,0 +1,40 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RPC_WORKER_H__
+#define __RPC_WORKER_H__
+
+#include "replace.h"
+#include "librpc/rpc/dcesrv_core.h"
+
+int rpc_worker_main(
+ int argc,
+ const char *argv[],
+ const char *daemon_config_name,
+ int num_workers,
+ int idle_seconds,
+ size_t (*get_interfaces)(
+ const struct ndr_interface_table ***ifaces,
+ void *private_data),
+ NTSTATUS (*get_servers)(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***ep_servers,
+ size_t *num_ep_servers,
+ void *private_data),
+ void *private_data);
+
+#endif /* __RPC_WORKER_H__ */
diff --git a/source3/rpc_server/rpcd_classic.c b/source3/rpc_server/rpcd_classic.c
new file mode 100644
index 0000000..2b7e939
--- /dev/null
+++ b/source3/rpc_server/rpcd_classic.c
@@ -0,0 +1,149 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc_scompat.h"
+#include "librpc/gen_ndr/ndr_dfs.h"
+#include "librpc/gen_ndr/ndr_dfs_scompat.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "librpc/gen_ndr/ndr_wkssvc_scompat.h"
+#include "librpc/gen_ndr/ndr_svcctl.h"
+#include "librpc/gen_ndr/ndr_svcctl_scompat.h"
+#include "librpc/gen_ndr/ndr_ntsvcs.h"
+#include "librpc/gen_ndr/ndr_ntsvcs_scompat.h"
+#include "librpc/gen_ndr/ndr_eventlog.h"
+#include "librpc/gen_ndr/ndr_eventlog_scompat.h"
+#include "librpc/gen_ndr/ndr_initshutdown.h"
+#include "librpc/gen_ndr/ndr_initshutdown_scompat.h"
+#include "source3/include/secrets.h"
+#include "locking/share_mode_lock.h"
+#include "source3/smbd/proto.h"
+
+static size_t classic_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_srvsvc,
+ &ndr_table_netdfs,
+ &ndr_table_initshutdown,
+ &ndr_table_svcctl,
+ &ndr_table_ntsvcs,
+ &ndr_table_eventlog,
+ /*
+ * This last item is truncated from the list by the
+ * num_ifaces -= 1 below. Take care when adding new
+ * services.
+ */
+ &ndr_table_wkssvc,
+ };
+ size_t num_ifaces = ARRAY_SIZE(ifaces);
+
+ switch(lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * On the AD DC wkssvc is provided by the 'samba'
+ * binary from source4/
+ */
+ num_ifaces -= 1;
+ break;
+ default:
+ break;
+ }
+
+ *pifaces = ifaces;
+ return num_ifaces;
+
+}
+
+static NTSTATUS classic_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[7] = { NULL };
+ size_t num_servers = ARRAY_SIZE(ep_servers);
+ NTSTATUS status;
+ bool ok;
+
+ ep_servers[0] = srvsvc_get_ep_server();
+ ep_servers[1] = netdfs_get_ep_server();
+ ep_servers[2] = initshutdown_get_ep_server();
+ ep_servers[3] = svcctl_get_ep_server();
+ ep_servers[4] = ntsvcs_get_ep_server();
+ ep_servers[5] = eventlog_get_ep_server();
+ ep_servers[6] = wkssvc_get_ep_server();
+
+ switch(lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * On the AD DC wkssvc is provided by the 'samba'
+ * binary from source4/
+ */
+ num_servers -= 1;
+ break;
+ default:
+ break;
+ }
+
+ ok = secrets_init();
+ if (!ok) {
+ DBG_ERR("secrets_init() failed\n");
+ exit(1);
+ }
+
+ ok = locking_init();
+ if (!ok) {
+ DBG_ERR("locking_init() failed\n");
+ exit(1);
+ }
+
+ status = share_info_db_init();
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("share_info_db_init failed: %s\n", nt_errstr(status));
+ exit(1);
+ }
+
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+
+ mangle_reset_cache();
+
+ status = dcesrv_register_default_auth_types_machine_principal(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = num_servers;
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_classic",
+ 5,
+ 60,
+ classic_interfaces,
+ classic_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_epmapper.c b/source3/rpc_server/rpcd_epmapper.c
new file mode 100644
index 0000000..9b2cc4f
--- /dev/null
+++ b/source3/rpc_server/rpcd_epmapper.c
@@ -0,0 +1,109 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "librpc/gen_ndr/ndr_epmapper_scompat.h"
+#include "param/loadparm.h"
+#include "libds/common/roles.h"
+
+static size_t epmapper_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_epmapper,
+ };
+ size_t num_ifaces = ARRAY_SIZE(ifaces);
+
+ switch(lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * On the AD DC epmapper is provided by the 'samba'
+ * binary from source4/
+ */
+ num_ifaces = 0;
+ break;
+ default:
+ break;
+ }
+
+ *pifaces = ifaces;
+ return num_ifaces;
+}
+
+static NTSTATUS epmapper_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[] = { NULL };
+ size_t num_servers = ARRAY_SIZE(ep_servers);
+ NTSTATUS status;
+
+ /*
+ * Windows Server 2022 registers the following auth_types
+ * all with an empty principal name:
+ *
+ * principle name for proto 9 (spnego) is ''
+ * principle name for proto 10 (ntlmssp) is ''
+ * principle name for proto 14 is ''
+ * principle name for proto 16 (gssapi_krb5) is ''
+ * principle name for proto 22 is ''
+ * principle name for proto 30 is ''
+ * principle name for proto 31 is ''
+ *
+ * We only register what we also support.
+ */
+ status = dcesrv_register_default_auth_types(dce_ctx, "");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ep_servers[0] = epmapper_get_ep_server();
+
+ switch(lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * On the AD DC epmapper is provided by the 'samba'
+ * binary from source4/
+ */
+ num_servers = 0;
+ break;
+ default:
+ break;
+ }
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = num_servers;
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_epmapper",
+ 1,
+ 10,
+ epmapper_interfaces,
+ epmapper_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_fsrvp.c b/source3/rpc_server/rpcd_fsrvp.c
new file mode 100644
index 0000000..f7db544
--- /dev/null
+++ b/source3/rpc_server/rpcd_fsrvp.c
@@ -0,0 +1,82 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_fsrvp.h"
+#include "librpc/gen_ndr/ndr_fsrvp_scompat.h"
+
+static size_t fsrvp_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_FileServerVssAgent,
+ };
+
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ /*
+ * For now, don't do shadow copies on the AD DC. This
+ * might change in the future, but there's a
+ * recommendation to split DCs from file servers.
+ *
+ * But then we need to put the snap logic into the ad
+ * dc testenv's smb.conf.
+ */
+ *pifaces = NULL;
+ return 0;
+ }
+
+ *pifaces = ifaces;
+ return ARRAY_SIZE(ifaces);
+}
+
+static NTSTATUS fsrvp_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[1] = { NULL };
+
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ *_ep_servers = NULL;
+ *_num_ep_servers = 0;
+ return NT_STATUS_OK;
+ }
+
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+
+ ep_servers[0] = FileServerVssAgent_get_ep_server();
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = ARRAY_SIZE(ep_servers);
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_fsrvp",
+ 5,
+ 60,
+ fsrvp_interfaces,
+ fsrvp_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_lsad.c b/source3/rpc_server/rpcd_lsad.c
new file mode 100644
index 0000000..d00f704
--- /dev/null
+++ b/source3/rpc_server/rpcd_lsad.c
@@ -0,0 +1,141 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_scompat.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/ndr_samr_scompat.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_scompat.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "librpc/gen_ndr/ndr_dssetup_scompat.h"
+#include "source3/include/auth.h"
+#include "source3/include/secrets.h"
+
+static size_t lsad_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_lsarpc,
+ &ndr_table_samr,
+ &ndr_table_dssetup,
+ /*
+ * This last item is truncated from the list by the
+ * num_ifaces -= 1 below for the fileserver. Take
+ * care when adding new services.
+ */
+ &ndr_table_netlogon,
+ };
+ size_t num_ifaces = ARRAY_SIZE(ifaces);
+
+ switch(lp_server_role()) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ /* no netlogon for non-dc */
+ num_ifaces -= 1;
+ break;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * All these services are provided by the 'samba'
+ * binary from source4, not this code which is the
+ * source3 / NT4-like "classic" DC implementation
+ */
+ num_ifaces = 0;
+ break;
+ default:
+ break;
+ }
+
+ *pifaces = ifaces;
+ return num_ifaces;
+}
+
+static NTSTATUS lsad_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[4] = { NULL, };
+ size_t num_servers = ARRAY_SIZE(ep_servers);
+ NTSTATUS status;
+ bool ok;
+
+ ep_servers[0] = lsarpc_get_ep_server();
+ ep_servers[1] = samr_get_ep_server();
+ ep_servers[2] = dssetup_get_ep_server();
+ ep_servers[3] = netlogon_get_ep_server();
+
+ ok = secrets_init();
+ if (!ok) {
+ DBG_ERR("secrets_init() failed\n");
+ exit(1);
+ }
+
+ status = dcesrv_register_default_auth_types_machine_principal(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch(lp_server_role()) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ /* no netlogon for non-dc */
+ num_servers -= 1;
+ break;
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * All these services are provided by the 'samba'
+ * binary from source4, not this code which is the
+ * source3 / NT4-like "classic" DC implementation
+ */
+ num_servers = 0;
+ break;
+ default:
+ /*
+ * As DC we also register schannel with an
+ * empty principal
+ */
+ status = dcesrv_auth_type_principal_register(dce_ctx,
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ "");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ }
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = num_servers;
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_lsad",
+ 5,
+ 60,
+ lsad_interfaces,
+ lsad_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_mdssvc.c b/source3/rpc_server/rpcd_mdssvc.c
new file mode 100644
index 0000000..c872aa6
--- /dev/null
+++ b/source3/rpc_server/rpcd_mdssvc.c
@@ -0,0 +1,71 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "source3/locking/proto.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_mdssvc.h"
+#include "librpc/gen_ndr/ndr_mdssvc_scompat.h"
+
+static size_t mdssvc_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_mdssvc,
+ };
+
+ *pifaces = ifaces;
+ return ARRAY_SIZE(ifaces);
+}
+
+static NTSTATUS mdssvc_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[1] = { NULL };
+ bool ok;
+
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+
+ ok = posix_locking_init(false);
+ if (!ok) {
+ DBG_ERR("posix_locking_init() failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ep_servers[0] = mdssvc_get_ep_server();
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = ARRAY_SIZE(ep_servers);
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_mdssvc",
+ 5,
+ 60,
+ mdssvc_interfaces,
+ mdssvc_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_rpcecho.c b/source3/rpc_server/rpcd_rpcecho.c
new file mode 100644
index 0000000..5466663
--- /dev/null
+++ b/source3/rpc_server/rpcd_rpcecho.c
@@ -0,0 +1,89 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "librpc/gen_ndr/ndr_echo_scompat.h"
+#include "param/loadparm.h"
+#include "libds/common/roles.h"
+
+static size_t rpcecho_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_rpcecho,
+ };
+ size_t num_ifaces = ARRAY_SIZE(ifaces);
+
+ switch(lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * On the AD DC rpcecho is provided by the 'samba'
+ * binary from source4/
+ */
+ num_ifaces = 0;
+ break;
+ default:
+ break;
+ }
+
+ *pifaces = ifaces;
+ return num_ifaces;
+}
+
+static NTSTATUS rpcecho_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[1] = { NULL };
+ size_t num_servers = ARRAY_SIZE(ep_servers);
+
+ ep_servers[0] = rpcecho_get_ep_server();
+
+ switch(lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /*
+ * On the AD DC rpcecho is provided by the 'samba'
+ * binary from source4/
+ */
+ num_servers = 0;
+ break;
+ default:
+ break;
+ }
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = num_servers;
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_rpcecho",
+ 1,
+ 1,
+ rpcecho_interfaces,
+ rpcecho_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_spoolss.c b/source3/rpc_server/rpcd_spoolss.c
new file mode 100644
index 0000000..43c7d53
--- /dev/null
+++ b/source3/rpc_server/rpcd_spoolss.c
@@ -0,0 +1,91 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_worker.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_scompat.h"
+#include "source3/locking/share_mode_lock.h"
+#include "source3/printing/queue_process.h"
+#include "source3/include/messages.h"
+#include "source3/include/secrets.h"
+#include "source3/smbd/proto.h"
+
+static size_t spoolss_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_spoolss,
+ };
+ *pifaces = ifaces;
+ return ARRAY_SIZE(ifaces);
+}
+
+static NTSTATUS spoolss_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[1] = { NULL };
+ struct messaging_context *msg_ctx = global_messaging_context();
+ struct tevent_context *ev_ctx = messaging_tevent_context(msg_ctx);
+ bool ok;
+
+ ep_servers[0] = spoolss_get_ep_server();
+
+ ok = secrets_init();
+ if (!ok) {
+ DBG_ERR("secrets_init() failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = locking_init();
+ if (!ok) {
+ DBG_ERR("locking_init() failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+
+ ok = printing_subsystem_init(ev_ctx, msg_ctx, dce_ctx);
+ if (!ok) {
+ DBG_ERR("printing_subsystem_init() failed\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ mangle_reset_cache();
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = ARRAY_SIZE(ep_servers);
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_spoolss",
+ 5,
+ 60,
+ spoolss_interfaces,
+ spoolss_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_winreg.c b/source3/rpc_server/rpcd_winreg.c
new file mode 100644
index 0000000..44715d8
--- /dev/null
+++ b/source3/rpc_server/rpcd_winreg.c
@@ -0,0 +1,71 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_worker.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "librpc/gen_ndr/ndr_winreg_scompat.h"
+#include "source3/registry/reg_init_full.h"
+
+static size_t winreg_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_winreg,
+ };
+ *pifaces = ifaces;
+ return ARRAY_SIZE(ifaces);
+}
+
+static NTSTATUS winreg_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[1] = { NULL };
+ WERROR werr;
+
+ ep_servers[0] = winreg_get_ep_server();
+
+ werr = registry_init_full();
+ if (!W_ERROR_IS_OK(werr)) {
+ DBG_ERR("registry_init_full() failed: %s\n",
+ win_errstr(werr));
+ return werror_to_ntstatus(werr);
+ }
+
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = ARRAY_SIZE(ep_servers);
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_winreg",
+ 5,
+ 60,
+ winreg_interfaces,
+ winreg_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/rpcd_witness.c b/source3/rpc_server/rpcd_witness.c
new file mode 100644
index 0000000..9dcf180
--- /dev/null
+++ b/source3/rpc_server/rpcd_witness.c
@@ -0,0 +1,120 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2023 Stefan Metzmacher
+ *
+ * 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_worker.h"
+#include "librpc/gen_ndr/ndr_witness.h"
+#include "librpc/gen_ndr/ndr_witness_scompat.h"
+
+static size_t witness_interfaces(
+ const struct ndr_interface_table ***pifaces,
+ void *private_data)
+{
+ static const struct ndr_interface_table *ifaces[] = {
+ &ndr_table_witness,
+ };
+
+ if (!lp_clustering()) {
+ /*
+ * Without clustering there's no need for witness.
+ */
+ *pifaces = NULL;
+ return 0;
+ }
+
+ *pifaces = ifaces;
+ return ARRAY_SIZE(ifaces);
+}
+
+static NTSTATUS witness_servers(
+ struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server ***_ep_servers,
+ size_t *_num_ep_servers,
+ void *private_data)
+{
+ static const struct dcesrv_endpoint_server *ep_servers[1] = { NULL };
+ char *principal = NULL;
+ NTSTATUS status;
+
+ if (!lp_clustering()) {
+ /*
+ * Without clustering there's no need for witness.
+ */
+ *_ep_servers = NULL;
+ *_num_ep_servers = 0;
+ return NT_STATUS_OK;
+ }
+
+ principal = talloc_asprintf(talloc_tos(),
+ "cifs/%s",
+ lp_netbios_name());
+ if (principal == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcesrv_auth_type_principal_register(dce_ctx,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ principal);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = dcesrv_auth_type_principal_register(dce_ctx,
+ DCERPC_AUTH_TYPE_SPNEGO,
+ principal);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (lp_security() == SEC_ADS) {
+ status = dcesrv_auth_type_principal_register(dce_ctx,
+ DCERPC_AUTH_TYPE_KRB5,
+ principal);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ TALLOC_FREE(principal);
+
+ /*
+ * We prefer NDR64 for witness,
+ * as it's a very simple protocol and
+ * we support it from the beginning,
+ * which means it's well tested.
+ */
+ dce_ctx->preferred_transfer = &ndr_transfer_syntax_ndr64;
+
+ ep_servers[0] = witness_get_ep_server();
+
+ *_ep_servers = ep_servers;
+ *_num_ep_servers = ARRAY_SIZE(ep_servers);
+ return NT_STATUS_OK;
+}
+
+int main(int argc, const char *argv[])
+{
+ return rpc_worker_main(
+ argc,
+ argv,
+ "rpcd_witness",
+ 5,
+ 60,
+ witness_interfaces,
+ witness_servers,
+ NULL);
+}
diff --git a/source3/rpc_server/samr/srv_samr_chgpasswd.c b/source3/rpc_server/samr/srv_samr_chgpasswd.c
new file mode 100644
index 0000000..5fcc08e
--- /dev/null
+++ b/source3/rpc_server/samr/srv_samr_chgpasswd.c
@@ -0,0 +1,1421 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001-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/>.
+*/
+
+/* These comments regard the code to change the user's unix password: */
+
+/* fork a child process to exec passwd and write to its
+ * tty to change a users password. This is running as the
+ * user who is attempting to change the password.
+ */
+
+/*
+ * This code was copied/borrowed and stolen from various sources.
+ * The primary source was the poppasswd.c from the authors of POPMail. This software
+ * was included as a client to change passwords using the 'passwd' program
+ * on the remote machine.
+ *
+ * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
+ * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
+ * and rights to modify, distribute or incorporate this change to the CAP suite or
+ * using it for any other reason are granted, so long as this disclaimer is left intact.
+ */
+
+/*
+ This code was hacked considerably for inclusion in Samba, primarily
+ by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
+ of the "password chat" option, which allows the easy runtime
+ specification of the expected sequence of events to change a
+ password.
+ */
+
+#include "includes.h"
+#include "system/terminal.h"
+#include "system/passwd.h"
+#include "system/filesys.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "rpc_server/samr/srv_samr_util.h"
+#include "passdb.h"
+#include "auth.h"
+#include "lib/util/sys_rw.h"
+#include "librpc/rpc/dcerpc_samr.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#ifndef ALLOW_CHANGE_PASSWORD
+#if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
+#define ALLOW_CHANGE_PASSWORD 1
+#endif
+#endif
+
+#if ALLOW_CHANGE_PASSWORD
+
+static int findpty(char **slave)
+{
+ int master = -1;
+ char *line = NULL;
+ DIR *dirp = NULL;
+ const char *dpname;
+
+ *slave = NULL;
+
+#if defined(HAVE_GRANTPT)
+#if defined(HAVE_POSIX_OPENPT)
+ master = posix_openpt(O_RDWR|O_NOCTTY);
+#else
+ /* Try to open /dev/ptmx. If that fails, fall through to old method. */
+ master = open("/dev/ptmx", O_RDWR, 0);
+#endif
+ if (master >= 0) {
+ grantpt(master);
+ unlockpt(master);
+ line = (char *)ptsname(master);
+ if (line) {
+ *slave = SMB_STRDUP(line);
+ }
+
+ if (*slave == NULL) {
+ DEBUG(0,
+ ("findpty: Unable to create master/slave pty pair.\n"));
+ /* Stop fd leak on error. */
+ close(master);
+ return -1;
+ } else {
+ DEBUG(10,
+ ("findpty: Allocated slave pty %s\n", *slave));
+ return (master);
+ }
+ }
+#endif /* HAVE_GRANTPT */
+
+ line = SMB_STRDUP("/dev/ptyXX");
+ if (!line) {
+ return (-1);
+ }
+
+ dirp = opendir("/dev");
+ if (!dirp) {
+ SAFE_FREE(line);
+ return (-1);
+ }
+
+ while ((dpname = readdirname(dirp)) != NULL) {
+ if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
+ DEBUG(3,
+ ("pty: try to open %s, line was %s\n", dpname,
+ line));
+ line[8] = dpname[3];
+ line[9] = dpname[4];
+ if ((master = open(line, O_RDWR, 0)) >= 0) {
+ DEBUG(3, ("pty: opened %s\n", line));
+ line[5] = 't';
+ *slave = line;
+ closedir(dirp);
+ return (master);
+ }
+ }
+ }
+ closedir(dirp);
+ SAFE_FREE(line);
+ return (-1);
+}
+
+static int dochild(int master, const char *slavedev, const struct passwd *pass,
+ const char *passwordprogram, bool as_root)
+{
+ int slave;
+ struct termios stermios;
+ gid_t gid;
+ uid_t uid;
+ char * const eptrs[1] = { NULL };
+
+ if (pass == NULL)
+ {
+ DEBUG(0,
+ ("dochild: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ gid = pass->pw_gid;
+ uid = pass->pw_uid;
+
+ gain_root_privilege();
+
+ /* Start new session - gets rid of controlling terminal. */
+ if (setsid() < 0)
+ {
+ DEBUG(3,
+ ("Weirdness, couldn't let go of controlling terminal\n"));
+ return (False);
+ }
+
+ /* Open slave pty and acquire as new controlling terminal. */
+ if ((slave = open(slavedev, O_RDWR, 0)) < 0)
+ {
+ DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
+ return (False);
+ }
+#if defined(TIOCSCTTY) && !defined(SUNOS5)
+ /*
+ * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
+ * see the discussion under
+ * https://bugzilla.samba.org/show_bug.cgi?id=5366.
+ */
+ if (ioctl(slave, TIOCSCTTY, 0) < 0)
+ {
+ DEBUG(3, ("Error in ioctl call for slave pty\n"));
+ /* return(False); */
+ }
+#elif defined(I_PUSH) && defined(I_FIND)
+ if (ioctl(slave, I_FIND, "ptem") == 0) {
+ ioctl(slave, I_PUSH, "ptem");
+ }
+ if (ioctl(slave, I_FIND, "ldterm") == 0) {
+ ioctl(slave, I_PUSH, "ldterm");
+ }
+#endif
+
+ /* Close master. */
+ close(master);
+
+ /* Make slave stdin/out/err of child. */
+
+ if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdin\n"));
+ return (False);
+ }
+ if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stdout\n"));
+ return (False);
+ }
+ if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
+ {
+ DEBUG(3, ("Could not re-direct stderr\n"));
+ return (False);
+ }
+ if (slave > 2)
+ close(slave);
+
+ /* Set proper terminal attributes - no echo, canonical input processing,
+ no map NL to CR/NL on output. */
+
+ if (tcgetattr(0, &stermios) < 0)
+ {
+ DEBUG(3,
+ ("could not read default terminal attributes on pty\n"));
+ return (False);
+ }
+ stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ stermios.c_lflag |= ICANON;
+#ifdef ONLCR
+ stermios.c_oflag &= ~(ONLCR);
+#endif
+ if (tcsetattr(0, TCSANOW, &stermios) < 0)
+ {
+ DEBUG(3, ("could not set attributes of pty\n"));
+ return (False);
+ }
+
+ /* make us completely into the right uid */
+ if (!as_root)
+ {
+ become_user_permanently(uid, gid);
+ }
+
+ DEBUG(10,
+ ("Invoking '%s' as password change program.\n",
+ passwordprogram));
+
+ /* execl() password-change application */
+ if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
+ {
+ DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
+ return (False);
+ }
+ return (True);
+}
+
+static int expect(int master, char *issue, char *expected)
+{
+ char buffer[1024];
+ int attempts, timeout, nread;
+ size_t len;
+ bool match = False;
+
+ for (attempts = 0; attempts < 2; attempts++) {
+ NTSTATUS status;
+ if (!strequal(issue, ".")) {
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: sending [%s]\n", issue));
+
+ if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
+ DEBUG(2,("expect: (short) write returned %d\n",
+ (int)len ));
+ return False;
+ }
+ }
+
+ if (strequal(expected, "."))
+ return True;
+
+ /* Initial timeout. */
+ timeout = lp_passwd_chat_timeout() * 1000;
+ nread = 0;
+ buffer[nread] = 0;
+
+ while (True) {
+ status = read_fd_with_timeout(
+ master, buffer + nread, 1,
+ sizeof(buffer) - nread - 1,
+ timeout, &len);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("expect: read error %s\n",
+ nt_errstr(status)));
+ break;
+ }
+ nread += len;
+ buffer[nread] = 0;
+
+ {
+ /* Eat leading/trailing whitespace before match. */
+ char *str = SMB_STRDUP(buffer);
+ if (!str) {
+ DEBUG(2,("expect: ENOMEM\n"));
+ return False;
+ }
+ trim_char(str, ' ', ' ');
+
+ if ((match = unix_wild_match(expected, str)) == True) {
+ /* Now data has started to return, lower timeout. */
+ timeout = lp_passwd_chat_timeout() * 100;
+ }
+ SAFE_FREE(str);
+ }
+ }
+
+ if (lp_passwd_chat_debug())
+ DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
+ expected, buffer, match ? "yes" : "no" ));
+
+ if (match)
+ break;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("expect: %s\n", nt_errstr(status)));
+ return False;
+ }
+ }
+
+ DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
+ return match;
+}
+
+static void pwd_sub(char *buf)
+{
+ all_string_sub(buf, "\\n", "\n", 0);
+ all_string_sub(buf, "\\r", "\r", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static int talktochild(int master, const char *seq)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ int count = 0;
+ char *issue;
+ char *expected;
+
+ issue = talloc_strdup(frame, ".");
+ if (!issue) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ while (next_token_talloc(frame, &seq, &expected, NULL)) {
+ pwd_sub(expected);
+ count++;
+
+ if (!expect(master, issue, expected)) {
+ DEBUG(3, ("Response %d incorrect\n", count));
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!next_token_talloc(frame, &seq, &issue, NULL)) {
+ issue = talloc_strdup(frame, ".");
+ if (!issue) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ }
+ pwd_sub(issue);
+ }
+
+ if (!strequal(issue, ".")) {
+ /* we have one final issue to send */
+ expected = talloc_strdup(frame, ".");
+ if (!expected) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ if (!expect(master, issue, expected)) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+ }
+ TALLOC_FREE(frame);
+ return (count > 0);
+}
+
+static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
+ char *chatsequence, bool as_root)
+{
+ char *slavedev = NULL;
+ int master;
+ pid_t pid, wpid;
+ int wstat;
+ bool chstat = False;
+ void (*saved_handler)(int);
+
+ if (pass == NULL) {
+ DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
+ return False;
+ }
+
+ /* allocate a pseudo-terminal device */
+ if ((master = findpty(&slavedev)) < 0) {
+ DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
+ return (False);
+ }
+
+ /*
+ * We need to temporarily stop CatchChild from eating
+ * SIGCLD signals as it also eats the exit status code. JRA.
+ */
+
+ saved_handler = CatchChildLeaveStatus();
+
+ if ((pid = fork()) < 0) {
+ DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
+ SAFE_FREE(slavedev);
+ close(master);
+ (void)CatchSignal(SIGCLD, saved_handler);
+ return (False);
+ }
+
+ /* we now have a pty */
+ if (pid > 0) { /* This is the parent process */
+ /* Don't need this anymore in parent. */
+ SAFE_FREE(slavedev);
+
+ if ((chstat = talktochild(master, chatsequence)) == False) {
+ DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
+ kill(pid, SIGKILL); /* be sure to end this process */
+ }
+
+ while ((wpid = waitpid(pid, &wstat, 0)) < 0) {
+ if (errno == EINTR) {
+ errno = 0;
+ continue;
+ }
+ break;
+ }
+
+ if (wpid < 0) {
+ DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
+ close(master);
+ (void)CatchSignal(SIGCLD, saved_handler);
+ return (False);
+ }
+
+ /*
+ * Go back to ignoring children.
+ */
+ (void)CatchSignal(SIGCLD, saved_handler);
+
+ close(master);
+
+ if (pid != wpid) {
+ DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
+ return (False);
+ }
+ if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
+ DEBUG(3, ("chat_with_program: The process exited with status %d \
+while we were waiting\n", WEXITSTATUS(wstat)));
+ return (False);
+ }
+#if defined(WIFSIGNALLED) && defined(WTERMSIG)
+ else if (WIFSIGNALLED(wstat)) {
+ DEBUG(3, ("chat_with_program: The process was killed by signal %d \
+while we were waiting\n", WTERMSIG(wstat)));
+ return (False);
+ }
+#endif
+ } else {
+ /* CHILD */
+
+ /*
+ * Lose any elevated privileges.
+ */
+ drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
+ drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
+
+ /* make sure it doesn't freeze */
+ alarm(20);
+
+ if (as_root)
+ become_root();
+
+ DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
+ (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
+ chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
+
+ if (as_root)
+ unbecome_root();
+
+ /*
+ * The child should never return from dochild() ....
+ */
+
+ DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
+ exit(1);
+ }
+
+ if (chstat)
+ DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
+ (chstat ? "" : "un"), pass->pw_name));
+ return (chstat);
+}
+
+bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
+ const char *oldpass, const char *newpass, bool as_root)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *passwordprogram = NULL;
+ char *chatsequence = NULL;
+ size_t i;
+ size_t len;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ if (!oldpass) {
+ oldpass = "";
+ }
+
+ DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
+#endif
+
+ /* Take the passed information and test it for minimum criteria */
+
+ /* Password is same as old password */
+ if (strcmp(oldpass, newpass) == 0) {
+ /* don't allow same password */
+ DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
+ return (False); /* inform the user */
+ }
+
+ /*
+ * Check the old and new passwords don't contain any control
+ * characters.
+ */
+
+ len = strlen(oldpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)oldpass[i])) {
+ DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+ len = strlen(newpass);
+ for (i = 0; i < len; i++) {
+ if (iscntrl((int)newpass[i])) {
+ DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
+ return False;
+ }
+ }
+
+#ifdef WITH_PAM
+ if (lp_pam_password_change()) {
+ bool ret;
+#ifdef HAVE_SETLOCALE
+ const char *prevlocale = setlocale(LC_ALL, "C");
+#endif
+
+ if (as_root)
+ become_root();
+
+ if (pass) {
+ ret = smb_pam_passchange(pass->pw_name, rhost,
+ oldpass, newpass);
+ } else {
+ ret = smb_pam_passchange(name, rhost, oldpass,
+ newpass);
+ }
+
+ if (as_root)
+ unbecome_root();
+
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_ALL, prevlocale);
+#endif
+
+ return ret;
+ }
+#endif
+
+ /* A non-PAM password change just doesn't make sense without a valid local user */
+
+ if (pass == NULL) {
+ DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
+ return false;
+ }
+
+ passwordprogram = lp_passwd_program(ctx, lp_sub);
+ if (!passwordprogram || !*passwordprogram) {
+ DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
+ return false;
+ }
+ chatsequence = lp_passwd_chat(ctx, lp_sub);
+ if (!chatsequence || !*chatsequence) {
+ DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
+ return false;
+ }
+
+ if (as_root) {
+ /* The password program *must* contain the user name to work. Fail if not. */
+ if (strstr_m(passwordprogram, "%u") == NULL) {
+ DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
+the string %%u, and the given string %s does not.\n", passwordprogram ));
+ return false;
+ }
+ }
+
+ passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
+ if (!passwordprogram) {
+ return false;
+ }
+
+ /* note that we do NOT substitute the %o and %n in the password program
+ as this would open up a security hole where the user could use
+ a new password containing shell escape characters */
+
+ chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
+ if (!chatsequence) {
+ return false;
+ }
+ chatsequence = talloc_all_string_sub(ctx,
+ chatsequence,
+ "%o",
+ oldpass);
+ if (!chatsequence) {
+ return false;
+ }
+ chatsequence = talloc_all_string_sub(ctx,
+ chatsequence,
+ "%n",
+ newpass);
+ if (chatsequence == NULL) {
+ return false;
+ }
+ return chat_with_program(passwordprogram,
+ pass,
+ chatsequence,
+ as_root);
+}
+
+#else /* ALLOW_CHANGE_PASSWORD */
+
+bool chgpasswd(const char *name, const struct passwd *pass,
+ const char *oldpass, const char *newpass, bool as_root)
+{
+ DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
+ return (False);
+}
+#endif /* ALLOW_CHANGE_PASSWORD */
+
+/***********************************************************
+ Decrypt and verify a user password change.
+
+ The 516 byte long buffers are encrypted with the old NT and
+ old LM passwords, and if the NT passwords are present, both
+ buffers contain a unicode string.
+
+ After decrypting the buffers, check the password is correct by
+ matching the old hashed passwords with the passwords in the passdb.
+
+************************************************************/
+
+static NTSTATUS check_oem_password(const char *user,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ struct samu *sampass,
+ char **pp_new_passwd)
+{
+ uchar null_pw[16];
+ uchar null_ntpw[16];
+ uint8_t *password_encrypted;
+ const uint8_t *encryption_key;
+ const uint8_t *lanman_pw, *nt_pw;
+ uint32_t acct_ctrl;
+ size_t new_pw_len;
+ uchar new_nt_hash[16];
+ uchar new_lm_hash[16];
+ uchar verifier[16];
+ char no_pw[2];
+
+ bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
+ bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
+ enum ntlm_auth_level ntlm_auth_level = lp_ntlm_auth();
+
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t enc_key;
+ int rc;
+
+ /* this call should be disabled without NTLM auth */
+ if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
+ DBG_WARNING("NTLM password changes not"
+ "permitted by configuration.\n");
+ return NT_STATUS_NTLM_BLOCKED;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+#if 0
+ /* I am convinced this check here is wrong, it is valid to
+ * change a password of a user that has a disabled account - gd */
+
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+#endif
+ if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
+ /* construct a null password (in case one is needed */
+ no_pw[0] = 0;
+ no_pw[1] = 0;
+ nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
+ lanman_pw = null_pw;
+ nt_pw = null_pw;
+
+ } else {
+ /* save pointers to passwords so we don't have to keep looking them up */
+ if (lp_lanman_auth()) {
+ lanman_pw = pdb_get_lanman_passwd(sampass);
+ } else {
+ lanman_pw = NULL;
+ }
+ nt_pw = pdb_get_nt_passwd(sampass);
+ }
+
+ if (nt_pw && nt_pass_set) {
+ /* IDEAL Case: passwords are in unicode, and we can
+ * read use the password encrypted with the NT hash
+ */
+ password_encrypted = password_encrypted_with_nt_hash;
+ encryption_key = nt_pw;
+ } else if (lanman_pw && lm_pass_set) {
+ /* password may still be in unicode, but use LM hash version */
+ password_encrypted = password_encrypted_with_lm_hash;
+ encryption_key = lanman_pw;
+ } else if (nt_pass_set) {
+ DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
+ user));
+ return NT_STATUS_WRONG_PASSWORD;
+ } else if (lm_pass_set) {
+ if (lp_lanman_auth()) {
+ DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
+ user));
+ } else {
+ DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
+ user));
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+ } else {
+ DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
+ user));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * Decrypt the password with the key
+ */
+ enc_key = (gnutls_datum_t) {
+ .data = discard_const_p(unsigned char, encryption_key),
+ .size = 16,
+ };
+
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &enc_key,
+ NULL);
+ if (rc < 0) {
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ password_encrypted,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ }
+
+ if (!decode_pw_buffer(talloc_tos(),
+ password_encrypted,
+ pp_new_passwd,
+ &new_pw_len,
+ nt_pass_set ? CH_UTF16 : CH_DOS)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * To ensure we got the correct new password, hash it and
+ * use it as a key to test the passed old password.
+ */
+
+ if (nt_pass_set) {
+ /* NT passwords, verify the NT hash. */
+
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(new_nt_hash, '\0', 16);
+ E_md4hash(*pp_new_passwd, new_nt_hash);
+
+ if (nt_pw) {
+ /*
+ * check the NT verifier
+ */
+ rc = E_old_pw_hash(new_nt_hash, nt_pw, verifier);
+ if (rc != 0) {
+ NTSTATUS status = NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
+ return gnutls_error_to_ntstatus(rc, status);
+ }
+ if (!mem_equal_const_time(verifier, old_nt_hash_encrypted, 16)) {
+ DEBUG(0, ("check_oem_password: old nt "
+ "password doesn't match.\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* We could check the LM password here, but there is
+ * little point, we already know the password is
+ * correct, and the LM password might not even be
+ * present. */
+
+ /* Further, LM hash generation algorithms
+ * differ with charset, so we could
+ * incorrectly fail a perfectly valid password
+ * change */
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", *pp_new_passwd));
+#endif
+ return NT_STATUS_OK;
+ }
+
+ if (lanman_pw) {
+ /*
+ * check the lm verifier
+ */
+ rc = E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
+ if (rc != 0) {
+ NTSTATUS status = NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
+ return gnutls_error_to_ntstatus(rc, status);
+ }
+ if (!mem_equal_const_time(verifier, old_lm_hash_encrypted, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", *pp_new_passwd));
+#endif
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (lanman_pw && lm_pass_set) {
+
+ E_deshash(*pp_new_passwd, new_lm_hash);
+
+ /*
+ * check the lm verifier
+ */
+ rc = E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
+ if (rc != 0) {
+ NTSTATUS status = NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
+ return gnutls_error_to_ntstatus(rc, status);
+ }
+ if (!mem_equal_const_time(verifier, old_lm_hash_encrypted, 16)) {
+ DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,
+ ("check_oem_password: password %s ok\n", *pp_new_passwd));
+#endif
+ return NT_STATUS_OK;
+ }
+
+ /* should not be reached */
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
+ uint32_t pw_history_len,
+ const uint8_t *pw_history)
+{
+ int i;
+
+ dump_data(100, nt_pw, NT_HASH_LEN);
+ dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
+
+ for (i=0; i<pw_history_len; i++) {
+ uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
+ const uint8_t *current_salt;
+ const uint8_t *old_nt_pw_salted_md5_hash;
+
+ current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
+ old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
+
+ if (all_zero(old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
+ /* Ignore zero valued entries. */
+ continue;
+ }
+
+ if (all_zero(current_salt, PW_HISTORY_SALT_LEN)) {
+ /*
+ * New format: zero salt and then plain nt hash.
+ * Directly compare the hashes.
+ */
+ if (mem_equal_const_time(nt_pw, old_nt_pw_salted_md5_hash,
+ SALTED_MD5_HASH_LEN))
+ {
+ return true;
+ }
+ } else {
+ gnutls_hash_hd_t hash_hnd = NULL;
+ int rc;
+
+ /*
+ * Old format: md5sum of salted nt hash.
+ * Create salted version of new pw to compare.
+ */
+ rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (rc < 0) {
+ return false;
+ }
+
+ rc = gnutls_hash(hash_hnd, current_salt, 16);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return false;
+ }
+ rc = gnutls_hash(hash_hnd, nt_pw, 16);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ return false;
+ }
+ gnutls_hash_deinit(hash_hnd, new_nt_pw_salted_md5_hash);
+
+ if (mem_equal_const_time(new_nt_pw_salted_md5_hash,
+ old_nt_pw_salted_md5_hash,
+ SALTED_MD5_HASH_LEN)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/***********************************************************
+ This routine takes the given password and checks it against
+ the password history. Returns True if this password has been
+ found in the history list.
+************************************************************/
+
+static bool check_passwd_history(struct samu *sampass, const char *plaintext)
+{
+ uchar new_nt_p16[NT_HASH_LEN];
+ const uint8_t *nt_pw;
+ const uint8_t *pwhistory;
+ uint32_t pwHisLen, curr_pwHisLen;
+
+ pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
+ if (pwHisLen == 0) {
+ return False;
+ }
+
+ pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
+ if (!pwhistory || curr_pwHisLen == 0) {
+ return False;
+ }
+
+ /* Only examine the minimum of the current history len and
+ the stored history len. Avoids race conditions. */
+ pwHisLen = MIN(pwHisLen,curr_pwHisLen);
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+
+ E_md4hash(plaintext, new_nt_p16);
+
+ if (mem_equal_const_time(nt_pw, new_nt_p16, NT_HASH_LEN)) {
+ DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
+ pdb_get_username(sampass) ));
+ return True;
+ }
+
+ if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
+ DEBUG(1,("check_passwd_history: proposed new password for "
+ "user %s found in history list !\n",
+ pdb_get_username(sampass) ));
+ return true;
+ }
+ return false;
+}
+
+/***********************************************************
+************************************************************/
+
+NTSTATUS check_password_complexity(const char *username,
+ const char *fullname,
+ const char *password,
+ enum samPwdChangeReason *samr_reject_reason)
+{
+ TALLOC_CTX *tosctx = talloc_tos();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int check_ret;
+ char *cmd;
+
+ /* Use external script to check password complexity */
+ if ((lp_check_password_script(tosctx, lp_sub) == NULL)
+ || (*(lp_check_password_script(tosctx, lp_sub)) == '\0')){
+ return NT_STATUS_OK;
+ }
+
+ cmd = talloc_string_sub(tosctx, lp_check_password_script(tosctx, lp_sub), "%u",
+ username);
+ if (!cmd) {
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ check_ret = setenv("SAMBA_CPS_ACCOUNT_NAME", username, 1);
+ if (check_ret != 0) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME");
+ if (fullname != NULL) {
+ check_ret = setenv("SAMBA_CPS_FULL_NAME", fullname, 1);
+ } else {
+ unsetenv("SAMBA_CPS_FULL_NAME");
+ }
+ if (check_ret != 0) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ check_ret = smbrunsecret(cmd, password);
+ unsetenv("SAMBA_CPS_ACCOUNT_NAME");
+ unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME");
+ unsetenv("SAMBA_CPS_FULL_NAME");
+ DEBUG(5,("check_password_complexity: check password script (%s) "
+ "returned [%d]\n", cmd, check_ret));
+ TALLOC_FREE(cmd);
+
+ if (check_ret != 0) {
+ DEBUG(1,("check_password_complexity: "
+ "check password script said new password is not good "
+ "enough!\n"));
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***********************************************************
+ Code to change the oem password. Changes both the lanman
+ and NT hashes. Old_passwd is almost always NULL.
+ NOTE this function is designed to be called as root. Check the old password
+ is correct before calling. JRA.
+************************************************************/
+
+NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
+ char *old_passwd, char *new_passwd,
+ bool as_root,
+ enum samPwdChangeReason *samr_reject_reason)
+{
+ uint32_t min_len;
+ uint32_t refuse;
+ TALLOC_CTX *tosctx = talloc_tos();
+ struct passwd *pass = NULL;
+ const char *username = pdb_get_username(hnd);
+ const char *fullname = pdb_get_fullname(hnd);
+ time_t can_change_time = pdb_get_pass_can_change_time(hnd);
+ NTSTATUS status;
+
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+ }
+
+ /* check to see if the secdesc has previously been set to disallow */
+ if (!pdb_get_pass_can_change(hnd)) {
+ DEBUG(1, ("user %s does not have permissions to change password\n", username));
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+ }
+ return NT_STATUS_ACCOUNT_RESTRICTION;
+ }
+
+ /* check to see if it is a Machine account and if the policy
+ * denies machines to change the password. *
+ * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
+ if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
+ if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
+ DEBUG(1, ("Machine %s cannot change password now, "
+ "denied by Refuse Machine Password Change policy\n",
+ username));
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+ }
+ return NT_STATUS_ACCOUNT_RESTRICTION;
+ }
+ }
+
+ /* removed calculation here, because passdb now calculates
+ based on policy. jmcd */
+ if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
+ DEBUG(1, ("user %s cannot change password now, must "
+ "wait until %s\n", username,
+ http_timestring(tosctx, can_change_time)));
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+ }
+ return NT_STATUS_ACCOUNT_RESTRICTION;
+ }
+
+ if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
+ DBG_WARNING("user %s cannot change password - "
+ "password too short\n"
+ " account policy min password len = %"PRIu32"\n",
+ username,
+ min_len);
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+/* return NT_STATUS_PWD_TOO_SHORT; */
+ }
+
+ if (check_passwd_history(hnd,new_passwd)) {
+ if (samr_reject_reason) {
+ *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ pass = Get_Pwnam_alloc(tosctx, username);
+ if (!pass) {
+ DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = check_password_complexity(username,
+ fullname,
+ new_passwd,
+ samr_reject_reason);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(pass);
+ return status;
+ }
+
+ /*
+ * If unix password sync was requested, attempt to change
+ * the /etc/passwd database first. Return failure if this cannot
+ * be done.
+ *
+ * This occurs before the oem change, because we don't want to
+ * update it if chgpasswd failed.
+ *
+ * Conditional on lp_unix_password_sync() because we don't want
+ * to touch the unix db unless we have admin permission.
+ */
+
+ if(lp_unix_password_sync() &&
+ !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
+ as_root)) {
+ TALLOC_FREE(pass);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ TALLOC_FREE(pass);
+
+ if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Now write it into the file. */
+ return pdb_update_sam_account (hnd);
+}
+
+/***********************************************************
+ Code to check and change the OEM hashed password.
+************************************************************/
+
+NTSTATUS pass_oem_change(char *user, const char *rhost,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ enum samPwdChangeReason *reject_reason)
+{
+ char *new_passwd = NULL;
+ struct samu *sampass = NULL;
+ NTSTATUS nt_status;
+ bool ret = false;
+ bool updated_badpw = false;
+ NTSTATUS update_login_attempts_status;
+ char *mutex_name_by_user = NULL;
+ struct named_mutex *mtx = NULL;
+
+ if (!(sampass = samu_new(NULL))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if (ret == false) {
+ DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ /* Quit if the account was locked out. */
+ if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+ DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", user));
+ nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT;
+ goto done;
+ }
+
+ nt_status = check_oem_password(user,
+ password_encrypted_with_lm_hash,
+ old_lm_hash_encrypted,
+ password_encrypted_with_nt_hash,
+ old_nt_hash_encrypted,
+ sampass,
+ &new_passwd);
+
+ /*
+ * We must re-load the sam account information under a mutex
+ * lock to ensure we don't miss any concurrent account lockout
+ * changes.
+ */
+
+ /* Clear out old sampass info. */
+ TALLOC_FREE(sampass);
+
+ sampass = samu_new(NULL);
+ if (sampass == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mutex_name_by_user = talloc_asprintf(NULL,
+ "check_sam_security_mutex_%s",
+ user);
+ if (mutex_name_by_user == NULL) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Grab the named mutex under root with 30 second timeout. */
+ become_root();
+ mtx = grab_named_mutex(NULL, mutex_name_by_user, 30);
+ if (mtx != NULL) {
+ /* Re-load the account information if we got the mutex. */
+ ret = pdb_getsampwnam(sampass, user);
+ }
+ unbecome_root();
+
+ /* Everything from here on until mtx is freed is done under the mutex.*/
+
+ if (mtx == NULL) {
+ DBG_ERR("Acquisition of mutex %s failed "
+ "for user %s\n",
+ mutex_name_by_user,
+ user);
+ nt_status = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ if (!ret) {
+ /*
+ * Re-load of account failed. This could only happen if the
+ * user was deleted in the meantime.
+ */
+ DBG_NOTICE("reload of user '%s' in passdb failed.\n",
+ user);
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ /*
+ * Check if the account is now locked out - now under the mutex.
+ * This can happen if the server is under
+ * a password guess attack and the ACB_AUTOLOCK is set by
+ * another process.
+ */
+ if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+ DBG_NOTICE("Account for user %s was locked out.\n", user);
+ nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT;
+ goto done;
+ }
+
+ /*
+ * Notify passdb backend of login success/failure. If not
+ * NT_STATUS_OK the backend doesn't like the login
+ */
+ update_login_attempts_status = pdb_update_login_attempts(sampass,
+ NT_STATUS_IS_OK(nt_status));
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ bool increment_bad_pw_count = false;
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) &&
+ (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ NT_STATUS_IS_OK(update_login_attempts_status))
+ {
+ increment_bad_pw_count = true;
+ }
+
+ if (increment_bad_pw_count) {
+ pdb_increment_bad_password_count(sampass);
+ updated_badpw = true;
+ } else {
+ pdb_update_bad_password_count(sampass,
+ &updated_badpw);
+ }
+ } else {
+
+ if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ (pdb_get_bad_password_count(sampass) > 0)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ updated_badpw = true;
+ }
+ }
+
+ if (updated_badpw) {
+ NTSTATUS update_status;
+ become_root();
+ update_status = pdb_update_sam_account(sampass);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(update_status)) {
+ DEBUG(1, ("Failed to modify entry: %s\n",
+ nt_errstr(update_status)));
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ goto done;
+ }
+
+ /* We've already checked the old password here.... */
+ become_root();
+ nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
+ True, reject_reason);
+ unbecome_root();
+
+ BURN_STR(new_passwd);
+
+done:
+ TALLOC_FREE(sampass);
+ TALLOC_FREE(mutex_name_by_user);
+ TALLOC_FREE(mtx);
+
+ return nt_status;
+}
+
+NTSTATUS samr_set_password_aes(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *cdk,
+ struct samr_EncryptedPasswordAES *pwbuf,
+ char **new_password_str)
+{
+ DATA_BLOB pw_data = data_blob_null;
+ DATA_BLOB new_password = data_blob_null;
+ const DATA_BLOB ciphertext =
+ data_blob_const(pwbuf->cipher, pwbuf->cipher_len);
+ DATA_BLOB iv = data_blob_const(pwbuf->salt, sizeof(pwbuf->salt));
+ NTSTATUS status;
+ bool ok;
+
+ *new_password_str = NULL;
+
+ status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(
+ mem_ctx,
+ &ciphertext,
+ cdk,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ &iv,
+ pwbuf->auth_data,
+ &pw_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ ok = decode_pwd_string_from_buffer514(mem_ctx,
+ pw_data.data,
+ CH_UTF16,
+ &new_password);
+ TALLOC_FREE(pw_data.data);
+ if (!ok) {
+ DBG_NOTICE("samr: failed to decode password buffer\n");
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ *new_password_str = talloc_strndup(mem_ctx,
+ (char *)new_password.data,
+ new_password.length);
+ TALLOC_FREE(new_password.data);
+ if (*new_password_str == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_keep_secret(*new_password_str);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_server/samr/srv_samr_nt.c b/source3/rpc_server/samr/srv_samr_nt.c
new file mode 100644
index 0000000..d26a8d5
--- /dev/null
+++ b/source3/rpc_server/samr/srv_samr_nt.c
@@ -0,0 +1,7910 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jeremy Allison 2001-2008,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002,
+ * Copyright (C) Gerald (Jerry) Carter 2003-2004,
+ * Copyright (C) Simo Sorce 2003.
+ * Copyright (C) Volker Lendecke 2005.
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This is the implementation of the SAMR code.
+ */
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "../librpc/gen_ndr/ndr_samr.h"
+#include "../librpc/gen_ndr/ndr_samr_scompat.h"
+#include "rpc_server/samr/srv_samr_util.h"
+#include "secrets.h"
+#include "rpc_client/init_lsa.h"
+#include "../libcli/security/security.h"
+#include "passdb.h"
+#include "auth.h"
+#include "rpc_server/srv_access_check.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/util/base64.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_helper.h"
+#include "librpc/rpc/dcerpc_samr.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include "lib/global_contexts.h"
+#include "nsswitch/winbind_client.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define SAMR_USR_RIGHTS_WRITE_PW \
+ ( READ_CONTROL_ACCESS | \
+ SAMR_USER_ACCESS_CHANGE_PASSWORD | \
+ SAMR_USER_ACCESS_SET_LOC_COM)
+#define SAMR_USR_RIGHTS_CANT_WRITE_PW \
+ ( READ_CONTROL_ACCESS | SAMR_USER_ACCESS_SET_LOC_COM )
+
+#define DISP_INFO_CACHE_TIMEOUT 10
+
+#define MAX_SAM_ENTRIES_W2K 0x400 /* 1024 */
+#define MAX_SAM_ENTRIES_W95 50
+
+enum samr_handle {
+ SAMR_HANDLE_CONNECT,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_HANDLE_USER,
+ SAMR_HANDLE_GROUP,
+ SAMR_HANDLE_ALIAS
+};
+
+struct samr_info {
+ uint32_t access_granted;
+ struct dom_sid sid;
+ struct disp_info *disp_info;
+};
+
+typedef struct disp_info {
+ struct dom_sid sid; /* identify which domain this is. */
+ struct pdb_search *users; /* querydispinfo 1 and 4 */
+ struct pdb_search *machines; /* querydispinfo 2 */
+ struct pdb_search *groups; /* querydispinfo 3 and 5, enumgroups */
+ struct pdb_search *aliases; /* enumaliases */
+
+ uint32_t enum_acb_mask;
+ struct pdb_search *enum_users; /* enumusers with a mask */
+
+ struct tevent_timer *cache_timeout_event; /* cache idle timeout
+ * handler. */
+} DISP_INFO;
+
+static const struct generic_mapping sam_generic_mapping = {
+ GENERIC_RIGHTS_SAM_READ,
+ GENERIC_RIGHTS_SAM_WRITE,
+ GENERIC_RIGHTS_SAM_EXECUTE,
+ GENERIC_RIGHTS_SAM_ALL_ACCESS};
+static const struct generic_mapping dom_generic_mapping = {
+ GENERIC_RIGHTS_DOMAIN_READ,
+ GENERIC_RIGHTS_DOMAIN_WRITE,
+ GENERIC_RIGHTS_DOMAIN_EXECUTE,
+ GENERIC_RIGHTS_DOMAIN_ALL_ACCESS};
+static const struct generic_mapping usr_generic_mapping = {
+ GENERIC_RIGHTS_USER_READ,
+ GENERIC_RIGHTS_USER_WRITE,
+ GENERIC_RIGHTS_USER_EXECUTE,
+ GENERIC_RIGHTS_USER_ALL_ACCESS};
+static const struct generic_mapping usr_nopwchange_generic_mapping = {
+ GENERIC_RIGHTS_USER_READ,
+ GENERIC_RIGHTS_USER_WRITE,
+ GENERIC_RIGHTS_USER_EXECUTE & ~SAMR_USER_ACCESS_CHANGE_PASSWORD,
+ GENERIC_RIGHTS_USER_ALL_ACCESS};
+static const struct generic_mapping grp_generic_mapping = {
+ GENERIC_RIGHTS_GROUP_READ,
+ GENERIC_RIGHTS_GROUP_WRITE,
+ GENERIC_RIGHTS_GROUP_EXECUTE,
+ GENERIC_RIGHTS_GROUP_ALL_ACCESS};
+static const struct generic_mapping ali_generic_mapping = {
+ GENERIC_RIGHTS_ALIAS_READ,
+ GENERIC_RIGHTS_ALIAS_WRITE,
+ GENERIC_RIGHTS_ALIAS_EXECUTE,
+ GENERIC_RIGHTS_ALIAS_ALL_ACCESS};
+
+/*******************************************************************
+*******************************************************************/
+static NTSTATUS create_samr_policy_handle(TALLOC_CTX *mem_ctx,
+ struct pipes_struct *p,
+ enum samr_handle type,
+ uint32_t acc_granted,
+ struct dom_sid *sid,
+ struct disp_info *disp_info,
+ struct policy_handle *handle)
+{
+ struct samr_info *info = NULL;
+ bool ok;
+
+ ZERO_STRUCTP(handle);
+
+ info = talloc_zero(mem_ctx, struct samr_info);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->access_granted = acc_granted;
+
+ if (sid != NULL) {
+ sid_copy(&info->sid, sid);
+ }
+
+ if (disp_info != NULL) {
+ info->disp_info = disp_info;
+ }
+
+ ok = create_policy_hnd(p, handle, type, info);
+ if (!ok) {
+ talloc_free(info);
+ ZERO_STRUCTP(handle);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samr_handle_access_check(uint32_t access_granted,
+ uint32_t access_required,
+ uint32_t *paccess_granted)
+{
+ if ((access_required & access_granted) != access_required) {
+ if (root_mode()) {
+ DBG_INFO("ACCESS should be DENIED (granted: "
+ "%#010x; required: %#010x) but overwritten "
+ "by euid == 0\n", access_granted,
+ access_required);
+ goto okay;
+ }
+ DBG_NOTICE("ACCESS DENIED (granted: %#010x; required: "
+ "%#010x)\n", access_granted, access_required);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+okay:
+ if (paccess_granted != NULL) {
+ *paccess_granted = access_granted;
+ }
+ return NT_STATUS_OK;
+}
+
+static void *samr_policy_handle_find(struct pipes_struct *p,
+ const struct policy_handle *handle,
+ uint8_t handle_type,
+ uint32_t access_required,
+ uint32_t *access_granted,
+ NTSTATUS *pstatus)
+{
+ struct samr_info *info = NULL;
+ NTSTATUS status;
+
+ info = find_policy_by_hnd(p,
+ handle,
+ handle_type,
+ struct samr_info,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ *pstatus = NT_STATUS_INVALID_HANDLE;
+ return NULL;
+ }
+
+ status = samr_handle_access_check(info->access_granted,
+ access_required,
+ access_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ *pstatus = status;
+ return NULL;
+ }
+
+ *pstatus = NT_STATUS_OK;
+ return info;
+}
+
+static NTSTATUS make_samr_object_sd( TALLOC_CTX *ctx, struct security_descriptor **psd, size_t *sd_size,
+ const struct generic_mapping *map,
+ struct dom_sid *sid, uint32_t sid_access )
+{
+ struct dom_sid domadmin_sid;
+ struct security_ace ace[5]; /* at most 5 entries */
+ size_t i = 0;
+
+ struct security_acl *psa = NULL;
+
+ /* basic access for Everyone */
+
+ init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ map->generic_execute | map->generic_read, 0);
+
+ /* add Full Access 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */
+
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Account_Operators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0);
+
+ /* Add Full Access for Domain Admins if we are a DC */
+
+ if ( IS_DC ) {
+ sid_compose(&domadmin_sid, get_global_sam_sid(),
+ DOMAIN_RID_ADMINS);
+ init_sec_ace(&ace[i++], &domadmin_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, map->generic_all, 0);
+ }
+
+ /* if we have a sid, give it some special access */
+
+ if ( sid ) {
+ init_sec_ace(&ace[i++], sid, SEC_ACE_TYPE_ACCESS_ALLOWED, sid_access, 0);
+ }
+
+ /* create the security descriptor */
+
+ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if ((*psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL,
+ psa, sd_size)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Fetch or create a dispinfo struct.
+********************************************************************/
+
+static DISP_INFO *get_samr_dispinfo_by_sid(const struct dom_sid *psid)
+{
+ /*
+ * We do a static cache for DISP_INFO's here. Explanation can be found
+ * in Jeremy's checkin message to r11793:
+ *
+ * Fix the SAMR cache so it works across completely insane
+ * client behaviour (ie.:
+ * open pipe/open SAMR handle/enumerate 0 - 1024
+ * close SAMR handle, close pipe.
+ * open pipe/open SAMR handle/enumerate 1024 - 2048...
+ * close SAMR handle, close pipe.
+ * And on ad-nausium. Amazing.... probably object-oriented
+ * client side programming in action yet again.
+ * This change should *massively* improve performance when
+ * enumerating users from an LDAP database.
+ * Jeremy.
+ *
+ * "Our" and the builtin domain are the only ones where we ever
+ * enumerate stuff, so just cache 2 entries.
+ */
+
+ static struct disp_info *builtin_dispinfo;
+ static struct disp_info *domain_dispinfo;
+
+ /* There are two cases to consider here:
+ 1) The SID is a domain SID and we look for an equality match, or
+ 2) This is an account SID and so we return the DISP_INFO* for our
+ domain */
+
+ if (psid == NULL) {
+ return NULL;
+ }
+
+ if (sid_check_is_builtin(psid) || sid_check_is_in_builtin(psid)) {
+ /*
+ * Necessary only once, but it does not really hurt.
+ */
+ if (builtin_dispinfo == NULL) {
+ builtin_dispinfo = talloc_zero(NULL, struct disp_info);
+ if (builtin_dispinfo == NULL) {
+ return NULL;
+ }
+ }
+ sid_copy(&builtin_dispinfo->sid, &global_sid_Builtin);
+
+ return builtin_dispinfo;
+ }
+
+ if (sid_check_is_our_sam(psid) || sid_check_is_in_our_sam(psid)) {
+ /*
+ * Necessary only once, but it does not really hurt.
+ */
+ if (domain_dispinfo == NULL) {
+ domain_dispinfo = talloc_zero(NULL, struct disp_info);
+ if (domain_dispinfo == NULL) {
+ return NULL;
+ }
+ }
+ sid_copy(&domain_dispinfo->sid, get_global_sam_sid());
+
+ return domain_dispinfo;
+ }
+
+ return NULL;
+}
+
+/*******************************************************************
+ Function to free the per SID data.
+ ********************************************************************/
+
+static void free_samr_cache(DISP_INFO *disp_info)
+{
+ struct dom_sid_buf buf;
+
+ DEBUG(10, ("free_samr_cache: deleting cache for SID %s\n",
+ dom_sid_str_buf(&disp_info->sid, &buf)));
+
+ /* We need to become root here because the paged search might have to
+ * tell the LDAP server we're not interested in the rest anymore. */
+
+ become_root();
+
+ TALLOC_FREE(disp_info->users);
+ TALLOC_FREE(disp_info->machines);
+ TALLOC_FREE(disp_info->groups);
+ TALLOC_FREE(disp_info->aliases);
+ TALLOC_FREE(disp_info->enum_users);
+
+ unbecome_root();
+}
+
+/*******************************************************************
+ Idle event handler. Throw away the disp info cache.
+ ********************************************************************/
+
+static void disp_info_cache_idle_timeout_handler(struct tevent_context *ev_ctx,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ DISP_INFO *disp_info = (DISP_INFO *)private_data;
+
+ TALLOC_FREE(disp_info->cache_timeout_event);
+
+ DEBUG(10, ("disp_info_cache_idle_timeout_handler: caching timed "
+ "out\n"));
+ free_samr_cache(disp_info);
+}
+
+/*******************************************************************
+ Setup cache removal idle event handler.
+ ********************************************************************/
+
+static void set_disp_info_cache_timeout(DISP_INFO *disp_info, time_t secs_fromnow)
+{
+ struct dom_sid_buf buf;
+
+ /* Remove any pending timeout and update. */
+
+ TALLOC_FREE(disp_info->cache_timeout_event);
+
+ DEBUG(10,("set_disp_info_cache_timeout: caching enumeration for "
+ "SID %s for %u seconds\n",
+ dom_sid_str_buf(&disp_info->sid, &buf),
+ (unsigned int)secs_fromnow ));
+
+ disp_info->cache_timeout_event = tevent_add_timer(
+ global_event_context(), NULL,
+ timeval_current_ofs(secs_fromnow, 0),
+ disp_info_cache_idle_timeout_handler, (void *)disp_info);
+}
+
+/*******************************************************************
+ Force flush any cache. We do this on any samr_set_xxx call.
+ We must also remove the timeout handler.
+ ********************************************************************/
+
+static void force_flush_samr_cache(const struct dom_sid *sid)
+{
+ struct disp_info *disp_info = get_samr_dispinfo_by_sid(sid);
+
+ if ((disp_info == NULL) || (disp_info->cache_timeout_event == NULL)) {
+ return;
+ }
+
+ DEBUG(10,("force_flush_samr_cache: clearing idle event\n"));
+ TALLOC_FREE(disp_info->cache_timeout_event);
+ free_samr_cache(disp_info);
+}
+
+/*******************************************************************
+ Ensure password info is never given out. Paranioa... JRA.
+ ********************************************************************/
+
+static void samr_clear_sam_passwd(struct samu *sam_pass)
+{
+
+ if (!sam_pass)
+ return;
+
+ /* These now zero out the old password */
+
+ pdb_set_lanman_passwd(sam_pass, NULL, PDB_DEFAULT);
+ pdb_set_nt_passwd(sam_pass, NULL, PDB_DEFAULT);
+}
+
+static uint32_t count_sam_users(struct disp_info *info, uint32_t acct_flags)
+{
+ struct samr_displayentry *entry;
+
+ if (sid_check_is_builtin(&info->sid)) {
+ /* No users in builtin. */
+ return 0;
+ }
+
+ if (info->users == NULL) {
+ info->users = pdb_search_users(info, acct_flags);
+ if (info->users == NULL) {
+ return 0;
+ }
+ }
+ /* Fetch the last possible entry, thus trigger an enumeration */
+ pdb_search_entries(info->users, 0xffffffff, 1, &entry);
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(info, DISP_INFO_CACHE_TIMEOUT);
+
+ return info->users->num_entries;
+}
+
+static uint32_t count_sam_groups(struct disp_info *info)
+{
+ struct samr_displayentry *entry;
+
+ if (sid_check_is_builtin(&info->sid)) {
+ /* No groups in builtin. */
+ return 0;
+ }
+
+ if (info->groups == NULL) {
+ info->groups = pdb_search_groups(info);
+ if (info->groups == NULL) {
+ return 0;
+ }
+ }
+ /* Fetch the last possible entry, thus trigger an enumeration */
+ pdb_search_entries(info->groups, 0xffffffff, 1, &entry);
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(info, DISP_INFO_CACHE_TIMEOUT);
+
+ return info->groups->num_entries;
+}
+
+static uint32_t count_sam_aliases(struct disp_info *info)
+{
+ struct samr_displayentry *entry;
+
+ if (info->aliases == NULL) {
+ info->aliases = pdb_search_aliases(info, &info->sid);
+ if (info->aliases == NULL) {
+ return 0;
+ }
+ }
+ /* Fetch the last possible entry, thus trigger an enumeration */
+ pdb_search_entries(info->aliases, 0xffffffff, 1, &entry);
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(info, DISP_INFO_CACHE_TIMEOUT);
+
+ return info->aliases->num_entries;
+}
+
+/*******************************************************************
+ _samr_Close
+ ********************************************************************/
+
+NTSTATUS _samr_Close(struct pipes_struct *p, struct samr_Close *r)
+{
+ if (!close_policy_hnd(p, r->in.handle)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_OpenDomain
+ ********************************************************************/
+
+NTSTATUS _samr_OpenDomain(struct pipes_struct *p,
+ struct samr_OpenDomain *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd = NULL;
+ uint32_t acc_granted;
+ uint32_t des_access = r->in.access_mask;
+ NTSTATUS status;
+ size_t sd_size;
+ uint32_t extra_access = SAMR_DOMAIN_ACCESS_CREATE_USER;
+ struct disp_info *disp_info = NULL;
+
+ /* find the connection policy handle. */
+ (void)samr_policy_handle_find(p,
+ r->in.connect_handle,
+ SAMR_HANDLE_CONNECT,
+ 0,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*check if access can be granted as requested by client. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ make_samr_object_sd( p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0 );
+ se_map_generic( &des_access, &dom_generic_mapping );
+
+ /*
+ * Users with SeAddUser get the ability to manipulate groups
+ * and aliases.
+ */
+ if (security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_ADD_USERS)) {
+ extra_access |= (SAMR_DOMAIN_ACCESS_CREATE_GROUP |
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS |
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT |
+ SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS |
+ SAMR_DOMAIN_ACCESS_CREATE_ALIAS);
+ }
+
+ /*
+ * Users with SeMachineAccount or SeAddUser get additional
+ * SAMR_DOMAIN_ACCESS_CREATE_USER access.
+ */
+
+ status = access_check_object( psd, session_info->security_token,
+ SEC_PRIV_MACHINE_ACCOUNT, SEC_PRIV_ADD_USERS,
+ extra_access, des_access,
+ &acc_granted, "_samr_OpenDomain" );
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ if (!sid_check_is_our_sam(r->in.sid) &&
+ !sid_check_is_builtin(r->in.sid)) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ disp_info = get_samr_dispinfo_by_sid(r->in.sid);
+
+ status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_DOMAIN,
+ acc_granted,
+ r->in.sid,
+ disp_info,
+ r->out.domain_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_OpenDomain: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_GetUserPwInfo
+ ********************************************************************/
+
+NTSTATUS _samr_GetUserPwInfo(struct pipes_struct *p,
+ struct samr_GetUserPwInfo *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct samr_info *uinfo;
+ enum lsa_SidType sid_type;
+ uint32_t min_password_length = 0;
+ uint32_t password_properties = 0;
+ bool ret = false;
+ NTSTATUS status;
+
+ DEBUG(5,("_samr_GetUserPwInfo: %d\n", __LINE__));
+
+ uinfo = samr_policy_handle_find(p, r->in.user_handle,
+ SAMR_HANDLE_USER,
+ SAMR_USER_ACCESS_GET_ATTRIBUTES,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_check_is_in_our_sam(&uinfo->sid)) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ become_root();
+ ret = lookup_sid(p->mem_ctx, &uinfo->sid, NULL, NULL, &sid_type);
+ unbecome_root();
+ if (ret == false) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ switch (sid_type) {
+ case SID_NAME_USER:
+ become_root();
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN,
+ &min_password_length);
+ pdb_get_account_policy(PDB_POLICY_USER_MUST_LOGON_TO_CHG_PASS,
+ &password_properties);
+ unbecome_root();
+
+ if (lp_check_password_script(talloc_tos(), lp_sub)
+ && *lp_check_password_script(talloc_tos(), lp_sub)) {
+ password_properties |= DOMAIN_PASSWORD_COMPLEX;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ r->out.info->min_password_length = min_password_length;
+ r->out.info->password_properties = password_properties;
+
+ DEBUG(5,("_samr_GetUserPwInfo: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_SetSecurity
+ ********************************************************************/
+
+NTSTATUS _samr_SetSecurity(struct pipes_struct *p,
+ struct samr_SetSecurity *r)
+{
+ struct samr_info *uinfo;
+ uint32_t i;
+ struct security_acl *dacl;
+ bool ret;
+ struct samu *sampass=NULL;
+ NTSTATUS status;
+
+ uinfo = samr_policy_handle_find(p,
+ r->in.handle,
+ SAMR_HANDLE_USER,
+ SAMR_USER_ACCESS_SET_ATTRIBUTES,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!(sampass = samu_new( p->mem_ctx))) {
+ DEBUG(0,("No memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* get the user record */
+ become_root();
+ ret = pdb_getsampwsid(sampass, &uinfo->sid);
+ unbecome_root();
+
+ if (!ret) {
+ struct dom_sid_buf buf;
+ DEBUG(4, ("User %s not found\n",
+ dom_sid_str_buf(&uinfo->sid, &buf)));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ dacl = r->in.sdbuf->sd->dacl;
+ for (i=0; i < dacl->num_aces; i++) {
+ if (dom_sid_equal(&uinfo->sid, &dacl->aces[i].trustee)) {
+ ret = pdb_set_pass_can_change(sampass,
+ (dacl->aces[i].access_mask &
+ SAMR_USER_ACCESS_CHANGE_PASSWORD) ?
+ True: False);
+ break;
+ }
+ }
+
+ if (!ret) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ become_root();
+ status = pdb_update_sam_account(sampass);
+ unbecome_root();
+
+ TALLOC_FREE(sampass);
+
+ return status;
+}
+
+/*******************************************************************
+ build correct perms based on policies and password times for _samr_query_sec_obj
+*******************************************************************/
+static bool check_change_pw_access(TALLOC_CTX *mem_ctx, struct dom_sid *user_sid)
+{
+ struct samu *sampass=NULL;
+ bool ret;
+
+ if ( !(sampass = samu_new( mem_ctx )) ) {
+ DEBUG(0,("No memory!\n"));
+ return False;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(sampass, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ struct dom_sid_buf buf;
+ DEBUG(4,("User %s not found\n",
+ dom_sid_str_buf(user_sid, &buf)));
+ TALLOC_FREE(sampass);
+ return False;
+ }
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) ));
+
+ if (pdb_get_pass_can_change(sampass)) {
+ TALLOC_FREE(sampass);
+ return True;
+ }
+ TALLOC_FREE(sampass);
+ return False;
+}
+
+
+/*******************************************************************
+ _samr_QuerySecurity
+ ********************************************************************/
+
+NTSTATUS _samr_QuerySecurity(struct pipes_struct *p,
+ struct samr_QuerySecurity *r)
+{
+ struct samr_info *info;
+ NTSTATUS status;
+ struct security_descriptor * psd = NULL;
+ size_t sd_size = 0;
+ struct dom_sid_buf buf;
+
+ info = samr_policy_handle_find(p,
+ r->in.handle,
+ SAMR_HANDLE_CONNECT,
+ SEC_STD_READ_CONTROL,
+ NULL,
+ &status);
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("_samr_QuerySecurity: querying security on SAM\n"));
+ status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size,
+ &sam_generic_mapping, NULL, 0);
+ goto done;
+ }
+
+ info = samr_policy_handle_find(p,
+ r->in.handle,
+ SAMR_HANDLE_DOMAIN,
+ SEC_STD_READ_CONTROL,
+ NULL,
+ &status);
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("_samr_QuerySecurity: querying security on Domain "
+ "with SID: %s\n",
+ dom_sid_str_buf(&info->sid, &buf)));
+ /*
+ * TODO: Builtin probably needs a different SD with restricted
+ * write access
+ */
+ status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size,
+ &dom_generic_mapping, NULL, 0);
+ goto done;
+ }
+
+ info = samr_policy_handle_find(p,
+ r->in.handle,
+ SAMR_HANDLE_USER,
+ SEC_STD_READ_CONTROL,
+ NULL,
+ &status);
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("_samr_QuerySecurity: querying security on user "
+ "Object with SID: %s\n",
+ dom_sid_str_buf(&info->sid, &buf)));
+ if (check_change_pw_access(p->mem_ctx, &info->sid)) {
+ status = make_samr_object_sd(
+ p->mem_ctx, &psd, &sd_size,
+ &usr_generic_mapping,
+ &info->sid, SAMR_USR_RIGHTS_WRITE_PW);
+ } else {
+ status = make_samr_object_sd(
+ p->mem_ctx, &psd, &sd_size,
+ &usr_nopwchange_generic_mapping,
+ &info->sid, SAMR_USR_RIGHTS_CANT_WRITE_PW);
+ }
+ goto done;
+ }
+
+ info = samr_policy_handle_find(p,
+ r->in.handle,
+ SAMR_HANDLE_GROUP,
+ SEC_STD_READ_CONTROL,
+ NULL,
+ &status);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * TODO: different SDs have to be generated for aliases groups
+ * and users. Currently all three get a default user SD
+ */
+ DEBUG(10,("_samr_QuerySecurity: querying security on group "
+ "Object with SID: %s\n",
+ dom_sid_str_buf(&info->sid, &buf)));
+ status = make_samr_object_sd(
+ p->mem_ctx, &psd, &sd_size,
+ &usr_nopwchange_generic_mapping,
+ &info->sid, SAMR_USR_RIGHTS_CANT_WRITE_PW);
+ goto done;
+ }
+
+ info = samr_policy_handle_find(p,
+ r->in.handle,
+ SAMR_HANDLE_ALIAS,
+ SEC_STD_READ_CONTROL,
+ NULL,
+ &status);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * TODO: different SDs have to be generated for aliases groups
+ * and users. Currently all three get a default user SD
+ */
+ DEBUG(10,("_samr_QuerySecurity: querying security on alias "
+ "Object with SID: %s\n",
+ dom_sid_str_buf(&info->sid, &buf)));
+ status = make_samr_object_sd(
+ p->mem_ctx, &psd, &sd_size,
+ &usr_nopwchange_generic_mapping,
+ &info->sid, SAMR_USR_RIGHTS_CANT_WRITE_PW);
+ goto done;
+ }
+
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+done:
+ if ((*r->out.sdbuf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a user list.
+********************************************************************/
+
+static NTSTATUS make_user_sam_entry_list(TALLOC_CTX *ctx,
+ struct samr_SamEntry **sam_pp,
+ uint32_t num_entries,
+ uint32_t start_idx,
+ struct samr_displayentry *entries)
+{
+ uint32_t i;
+ struct samr_SamEntry *sam;
+
+ *sam_pp = NULL;
+
+ if (num_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ sam = talloc_zero_array(ctx, struct samr_SamEntry, num_entries);
+ if (sam == NULL) {
+ DEBUG(0, ("make_user_sam_entry_list: TALLOC_ZERO failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+#if 0
+ /*
+ * usrmgr expects a non-NULL terminated string with
+ * trust relationships
+ */
+ if (entries[i].acct_flags & ACB_DOMTRUST) {
+ init_unistr2(&uni_temp_name, entries[i].account_name,
+ UNI_FLAGS_NONE);
+ } else {
+ init_unistr2(&uni_temp_name, entries[i].account_name,
+ UNI_STR_TERMINATE);
+ }
+#endif
+ init_lsa_String(&sam[i].name, entries[i].account_name);
+ sam[i].idx = entries[i].rid;
+ }
+
+ *sam_pp = sam;
+
+ return NT_STATUS_OK;
+}
+
+#define MAX_SAM_ENTRIES MAX_SAM_ENTRIES_W2K
+
+/*******************************************************************
+ _samr_EnumDomainUsers
+ ********************************************************************/
+
+NTSTATUS _samr_EnumDomainUsers(struct pipes_struct *p,
+ struct samr_EnumDomainUsers *r)
+{
+ NTSTATUS status;
+ struct samr_info *dinfo;
+ uint32_t num_account;
+ uint32_t enum_context = *r->in.resume_handle;
+ enum remote_arch_types ra_type = get_remote_arch();
+ int max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+ uint32_t max_entries = max_sam_entries;
+ struct samr_displayentry *entries = NULL;
+ struct samr_SamArray *samr_array = NULL;
+ struct samr_SamEntry *samr_entries = NULL;
+
+ DEBUG(5,("_samr_EnumDomainUsers: %d\n", __LINE__));
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ samr_array = talloc_zero(p->mem_ctx, struct samr_SamArray);
+ if (!samr_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.sam = samr_array;
+
+ if (sid_check_is_builtin(&dinfo->sid)) {
+ /* No users in builtin. */
+ *r->out.resume_handle = *r->in.resume_handle;
+ DEBUG(5,("_samr_EnumDomainUsers: No users in BUILTIN\n"));
+ return status;
+ }
+
+ become_root();
+
+ /* AS ROOT !!!! */
+
+ if ((dinfo->disp_info->enum_users != NULL) &&
+ (dinfo->disp_info->enum_acb_mask != r->in.acct_flags)) {
+ TALLOC_FREE(dinfo->disp_info->enum_users);
+ }
+
+ if (dinfo->disp_info->enum_users == NULL) {
+ dinfo->disp_info->enum_users = pdb_search_users(
+ dinfo->disp_info, r->in.acct_flags);
+ dinfo->disp_info->enum_acb_mask = r->in.acct_flags;
+ }
+
+ if (dinfo->disp_info->enum_users == NULL) {
+ /* END AS ROOT !!!! */
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ num_account = pdb_search_entries(dinfo->disp_info->enum_users,
+ enum_context, max_entries,
+ &entries);
+
+ /* END AS ROOT !!!! */
+
+ unbecome_root();
+
+ if (num_account == 0) {
+ DEBUG(5, ("_samr_EnumDomainUsers: enumeration handle over "
+ "total entries\n"));
+ *r->out.resume_handle = *r->in.resume_handle;
+ return NT_STATUS_OK;
+ }
+
+ status = make_user_sam_entry_list(p->mem_ctx, &samr_entries,
+ num_account, enum_context,
+ entries);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (max_entries <= num_account) {
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ DEBUG(5, ("_samr_EnumDomainUsers: %d\n", __LINE__));
+
+ samr_array->count = num_account;
+ samr_array->entries = samr_entries;
+
+ *r->out.resume_handle = *r->in.resume_handle + num_account;
+ *r->out.num_entries = num_account;
+
+ DEBUG(5,("_samr_EnumDomainUsers: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+makes a SAM_ENTRY / UNISTR2* structure from a group list.
+********************************************************************/
+
+static void make_group_sam_entry_list(TALLOC_CTX *ctx,
+ struct samr_SamEntry **sam_pp,
+ uint32_t num_sam_entries,
+ struct samr_displayentry *entries)
+{
+ struct samr_SamEntry *sam;
+ uint32_t i;
+
+ *sam_pp = NULL;
+
+ if (num_sam_entries == 0) {
+ return;
+ }
+
+ sam = talloc_zero_array(ctx, struct samr_SamEntry, num_sam_entries);
+ if (sam == NULL) {
+ return;
+ }
+
+ for (i = 0; i < num_sam_entries; i++) {
+ /*
+ * JRA. I think this should include the null. TNG does not.
+ */
+ init_lsa_String(&sam[i].name, entries[i].account_name);
+ sam[i].idx = entries[i].rid;
+ }
+
+ *sam_pp = sam;
+}
+
+/*******************************************************************
+ _samr_EnumDomainGroups
+ ********************************************************************/
+
+NTSTATUS _samr_EnumDomainGroups(struct pipes_struct *p,
+ struct samr_EnumDomainGroups *r)
+{
+ NTSTATUS status;
+ struct samr_info *dinfo;
+ struct samr_displayentry *groups;
+ uint32_t num_groups;
+ struct samr_SamArray *samr_array = NULL;
+ struct samr_SamEntry *samr_entries = NULL;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_EnumDomainGroups: %d\n", __LINE__));
+
+ samr_array = talloc_zero(p->mem_ctx, struct samr_SamArray);
+ if (!samr_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.sam = samr_array;
+
+ if (sid_check_is_builtin(&dinfo->sid)) {
+ /* No groups in builtin. */
+ *r->out.resume_handle = *r->in.resume_handle;
+ DEBUG(5,("_samr_EnumDomainGroups: No groups in BUILTIN\n"));
+ return status;
+ }
+
+ /* the domain group array is being allocated in the function below */
+
+ become_root();
+
+ if (dinfo->disp_info->groups == NULL) {
+ dinfo->disp_info->groups = pdb_search_groups(dinfo->disp_info);
+
+ if (dinfo->disp_info->groups == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ num_groups = pdb_search_entries(dinfo->disp_info->groups,
+ *r->in.resume_handle,
+ MAX_SAM_ENTRIES, &groups);
+ unbecome_root();
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ make_group_sam_entry_list(p->mem_ctx, &samr_entries,
+ num_groups, groups);
+
+ if (MAX_SAM_ENTRIES <= num_groups) {
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ samr_array->count = num_groups;
+ samr_array->entries = samr_entries;
+
+ *r->out.num_entries = num_groups;
+ *r->out.resume_handle = num_groups + *r->in.resume_handle;
+
+ DEBUG(5,("_samr_EnumDomainGroups: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_EnumDomainAliases
+ ********************************************************************/
+
+NTSTATUS _samr_EnumDomainAliases(struct pipes_struct *p,
+ struct samr_EnumDomainAliases *r)
+{
+ NTSTATUS status;
+ struct samr_info *dinfo;
+ struct samr_displayentry *aliases;
+ uint32_t num_aliases = 0;
+ struct samr_SamArray *samr_array = NULL;
+ struct samr_SamEntry *samr_entries = NULL;
+ struct dom_sid_buf buf;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_EnumDomainAliases: sid %s\n",
+ dom_sid_str_buf(&dinfo->sid, &buf)));
+
+ samr_array = talloc_zero(p->mem_ctx, struct samr_SamArray);
+ if (!samr_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+
+ if (dinfo->disp_info->aliases == NULL) {
+ dinfo->disp_info->aliases = pdb_search_aliases(
+ dinfo->disp_info, &dinfo->sid);
+ if (dinfo->disp_info->aliases == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ num_aliases = pdb_search_entries(dinfo->disp_info->aliases,
+ *r->in.resume_handle,
+ MAX_SAM_ENTRIES, &aliases);
+ unbecome_root();
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ make_group_sam_entry_list(p->mem_ctx, &samr_entries,
+ num_aliases, aliases);
+
+ DEBUG(5,("_samr_EnumDomainAliases: %d\n", __LINE__));
+
+ if (MAX_SAM_ENTRIES <= num_aliases) {
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ samr_array->count = num_aliases;
+ samr_array->entries = samr_entries;
+
+ *r->out.sam = samr_array;
+ *r->out.num_entries = num_aliases;
+ *r->out.resume_handle = num_aliases + *r->in.resume_handle;
+
+ return status;
+}
+
+/*******************************************************************
+ inits a samr_DispInfoGeneral structure.
+********************************************************************/
+
+static NTSTATUS init_samr_dispinfo_1(TALLOC_CTX *ctx,
+ struct samr_DispInfoGeneral *r,
+ uint32_t num_entries,
+ uint32_t start_idx,
+ struct samr_displayentry *entries)
+{
+ uint32_t i;
+
+ DEBUG(10, ("init_samr_dispinfo_1: num_entries: %d\n", num_entries));
+
+ if (num_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->count = num_entries;
+
+ r->entries = talloc_zero_array(ctx, struct samr_DispEntryGeneral, num_entries);
+ if (!r->entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries ; i++) {
+
+ init_lsa_String(&r->entries[i].account_name,
+ entries[i].account_name);
+
+ init_lsa_String(&r->entries[i].description,
+ entries[i].description);
+
+ init_lsa_String(&r->entries[i].full_name,
+ entries[i].fullname);
+
+ r->entries[i].rid = entries[i].rid;
+ r->entries[i].acct_flags = entries[i].acct_flags;
+ r->entries[i].idx = start_idx+i+1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ inits a samr_DispInfoFull structure.
+********************************************************************/
+
+static NTSTATUS init_samr_dispinfo_2(TALLOC_CTX *ctx,
+ struct samr_DispInfoFull *r,
+ uint32_t num_entries,
+ uint32_t start_idx,
+ struct samr_displayentry *entries)
+{
+ uint32_t i;
+
+ DEBUG(10, ("init_samr_dispinfo_2: num_entries: %d\n", num_entries));
+
+ if (num_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->count = num_entries;
+
+ r->entries = talloc_zero_array(ctx, struct samr_DispEntryFull, num_entries);
+ if (!r->entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries ; i++) {
+
+ init_lsa_String(&r->entries[i].account_name,
+ entries[i].account_name);
+
+ init_lsa_String(&r->entries[i].description,
+ entries[i].description);
+
+ r->entries[i].rid = entries[i].rid;
+ r->entries[i].acct_flags = entries[i].acct_flags;
+ r->entries[i].idx = start_idx+i+1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ inits a samr_DispInfoFullGroups structure.
+********************************************************************/
+
+static NTSTATUS init_samr_dispinfo_3(TALLOC_CTX *ctx,
+ struct samr_DispInfoFullGroups *r,
+ uint32_t num_entries,
+ uint32_t start_idx,
+ struct samr_displayentry *entries)
+{
+ uint32_t i;
+
+ DEBUG(5, ("init_samr_dispinfo_3: num_entries: %d\n", num_entries));
+
+ if (num_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->count = num_entries;
+
+ r->entries = talloc_zero_array(ctx, struct samr_DispEntryFullGroup, num_entries);
+ if (!r->entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries ; i++) {
+
+ init_lsa_String(&r->entries[i].account_name,
+ entries[i].account_name);
+
+ init_lsa_String(&r->entries[i].description,
+ entries[i].description);
+
+ r->entries[i].rid = entries[i].rid;
+ r->entries[i].acct_flags = entries[i].acct_flags;
+ r->entries[i].idx = start_idx+i+1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ inits a samr_DispInfoAscii structure.
+********************************************************************/
+
+static NTSTATUS init_samr_dispinfo_4(TALLOC_CTX *ctx,
+ struct samr_DispInfoAscii *r,
+ uint32_t num_entries,
+ uint32_t start_idx,
+ struct samr_displayentry *entries)
+{
+ uint32_t i;
+
+ DEBUG(5, ("init_samr_dispinfo_4: num_entries: %d\n", num_entries));
+
+ if (num_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->count = num_entries;
+
+ r->entries = talloc_zero_array(ctx, struct samr_DispEntryAscii, num_entries);
+ if (!r->entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries ; i++) {
+
+ init_lsa_AsciiStringLarge(&r->entries[i].account_name,
+ entries[i].account_name);
+
+ r->entries[i].idx = start_idx+i+1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ inits a samr_DispInfoAscii structure.
+********************************************************************/
+
+static NTSTATUS init_samr_dispinfo_5(TALLOC_CTX *ctx,
+ struct samr_DispInfoAscii *r,
+ uint32_t num_entries,
+ uint32_t start_idx,
+ struct samr_displayentry *entries)
+{
+ uint32_t i;
+
+ DEBUG(5, ("init_samr_dispinfo_5: num_entries: %d\n", num_entries));
+
+ if (num_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->count = num_entries;
+
+ r->entries = talloc_zero_array(ctx, struct samr_DispEntryAscii, num_entries);
+ if (!r->entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_entries ; i++) {
+
+ init_lsa_AsciiStringLarge(&r->entries[i].account_name,
+ entries[i].account_name);
+
+ r->entries[i].idx = start_idx+i+1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_QueryDisplayInfo
+ ********************************************************************/
+
+NTSTATUS _samr_QueryDisplayInfo(struct pipes_struct *p,
+ struct samr_QueryDisplayInfo *r)
+{
+ NTSTATUS status;
+ struct samr_info *dinfo;
+ uint32_t struct_size=0x20; /* W2K always reply that, client doesn't care */
+
+ uint32_t max_entries = r->in.max_entries;
+
+ union samr_DispInfo *disp_info = r->out.info;
+
+ uint32_t temp_size=0;
+ NTSTATUS disp_ret = NT_STATUS_UNSUCCESSFUL;
+ uint32_t num_account = 0;
+ enum remote_arch_types ra_type = get_remote_arch();
+ uint32_t max_sam_entries = (ra_type == RA_WIN95) ?
+ MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K;
+ struct samr_displayentry *entries = NULL;
+
+ DEBUG(5,("_samr_QueryDisplayInfo: %d\n", __LINE__));
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (sid_check_is_builtin(&dinfo->sid)) {
+ DEBUG(5,("_samr_QueryDisplayInfo: no users in BUILTIN\n"));
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * calculate how many entries we will return.
+ * based on
+ * - the number of entries the client asked
+ * - our limit on that
+ * - the starting point (enumeration context)
+ * - the buffer size the client will accept
+ */
+
+ /*
+ * We are a lot more like W2K. Instead of reading the SAM
+ * each time to find the records we need to send back,
+ * we read it once and link that copy to the sam handle.
+ * For large user list (over the MAX_SAM_ENTRIES)
+ * it's a definitive win.
+ * second point to notice: between enumerations
+ * our sam is now the same as it's a snapshoot.
+ * third point: got rid of the static SAM_USER_21 struct
+ * no more intermediate.
+ * con: it uses much more memory, as a full copy is stored
+ * in memory.
+ *
+ * If you want to change it, think twice and think
+ * of the second point , that's really important.
+ *
+ * JFM, 12/20/2001
+ */
+
+ if ((r->in.level < 1) || (r->in.level > 5)) {
+ DEBUG(0,("_samr_QueryDisplayInfo: Unknown info level (%u)\n",
+ (unsigned int)r->in.level ));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* first limit the number of entries we will return */
+ if (r->in.max_entries > max_sam_entries) {
+ DEBUG(5, ("_samr_QueryDisplayInfo: client requested %d "
+ "entries, limiting to %d\n", r->in.max_entries,
+ max_sam_entries));
+ max_entries = max_sam_entries;
+ }
+
+ /* calculate the size and limit on the number of entries we will
+ * return */
+
+ temp_size=max_entries*struct_size;
+
+ if (temp_size > r->in.buf_size) {
+ max_entries = MIN((r->in.buf_size / struct_size),max_entries);
+ DEBUG(5, ("_samr_QueryDisplayInfo: buffer size limits to "
+ "only %d entries\n", max_entries));
+ }
+
+ become_root();
+
+ /* The following done as ROOT. Don't return without unbecome_root(). */
+
+ switch (r->in.level) {
+ case 1:
+ case 4:
+ if (dinfo->disp_info->users == NULL) {
+ dinfo->disp_info->users = pdb_search_users(
+ dinfo->disp_info, ACB_NORMAL);
+ if (dinfo->disp_info->users == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_QueryDisplayInfo: starting user enumeration at index %u\n",
+ (unsigned int)r->in.start_idx));
+ } else {
+ DEBUG(10,("_samr_QueryDisplayInfo: using cached user enumeration at index %u\n",
+ (unsigned int)r->in.start_idx));
+ }
+
+ num_account = pdb_search_entries(dinfo->disp_info->users,
+ r->in.start_idx, max_entries,
+ &entries);
+ break;
+ case 2:
+ if (dinfo->disp_info->machines == NULL) {
+ dinfo->disp_info->machines = pdb_search_users(
+ dinfo->disp_info, ACB_WSTRUST|ACB_SVRTRUST);
+ if (dinfo->disp_info->machines == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_QueryDisplayInfo: starting machine enumeration at index %u\n",
+ (unsigned int)r->in.start_idx));
+ } else {
+ DEBUG(10,("_samr_QueryDisplayInfo: using cached machine enumeration at index %u\n",
+ (unsigned int)r->in.start_idx));
+ }
+
+ num_account = pdb_search_entries(dinfo->disp_info->machines,
+ r->in.start_idx, max_entries,
+ &entries);
+ break;
+ case 3:
+ case 5:
+ if (dinfo->disp_info->groups == NULL) {
+ dinfo->disp_info->groups = pdb_search_groups(
+ dinfo->disp_info);
+ if (dinfo->disp_info->groups == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_QueryDisplayInfo: starting group enumeration at index %u\n",
+ (unsigned int)r->in.start_idx));
+ } else {
+ DEBUG(10,("_samr_QueryDisplayInfo: using cached group enumeration at index %u\n",
+ (unsigned int)r->in.start_idx));
+ }
+
+ num_account = pdb_search_entries(dinfo->disp_info->groups,
+ r->in.start_idx, max_entries,
+ &entries);
+ break;
+ default:
+ unbecome_root();
+ smb_panic("info class changed");
+ break;
+ }
+ unbecome_root();
+
+
+ /* Now create reply structure */
+ switch (r->in.level) {
+ case 1:
+ disp_ret = init_samr_dispinfo_1(p->mem_ctx, &disp_info->info1,
+ num_account, r->in.start_idx,
+ entries);
+ break;
+ case 2:
+ disp_ret = init_samr_dispinfo_2(p->mem_ctx, &disp_info->info2,
+ num_account, r->in.start_idx,
+ entries);
+ break;
+ case 3:
+ disp_ret = init_samr_dispinfo_3(p->mem_ctx, &disp_info->info3,
+ num_account, r->in.start_idx,
+ entries);
+ break;
+ case 4:
+ disp_ret = init_samr_dispinfo_4(p->mem_ctx, &disp_info->info4,
+ num_account, r->in.start_idx,
+ entries);
+ break;
+ case 5:
+ disp_ret = init_samr_dispinfo_5(p->mem_ctx, &disp_info->info5,
+ num_account, r->in.start_idx,
+ entries);
+ break;
+ default:
+ smb_panic("info class changed");
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+
+ if (max_entries <= num_account) {
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ DEBUG(5, ("_samr_QueryDisplayInfo: %d\n", __LINE__));
+
+ *r->out.total_size = num_account * struct_size;
+ *r->out.returned_size = num_account ? temp_size : 0;
+
+ return status;
+}
+
+/****************************************************************
+ _samr_QueryDisplayInfo2
+****************************************************************/
+
+NTSTATUS _samr_QueryDisplayInfo2(struct pipes_struct *p,
+ struct samr_QueryDisplayInfo2 *r)
+{
+ struct samr_QueryDisplayInfo q;
+
+ 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;
+
+ q.out.total_size = r->out.total_size;
+ q.out.returned_size = r->out.returned_size;
+ q.out.info = r->out.info;
+
+ return _samr_QueryDisplayInfo(p, &q);
+}
+
+/****************************************************************
+ _samr_QueryDisplayInfo3
+****************************************************************/
+
+NTSTATUS _samr_QueryDisplayInfo3(struct pipes_struct *p,
+ struct samr_QueryDisplayInfo3 *r)
+{
+ struct samr_QueryDisplayInfo q;
+
+ 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;
+
+ q.out.total_size = r->out.total_size;
+ q.out.returned_size = r->out.returned_size;
+ q.out.info = r->out.info;
+
+ return _samr_QueryDisplayInfo(p, &q);
+}
+
+/*******************************************************************
+ _samr_QueryAliasInfo
+ ********************************************************************/
+
+NTSTATUS _samr_QueryAliasInfo(struct pipes_struct *p,
+ struct samr_QueryAliasInfo *r)
+{
+ struct samr_info *ainfo;
+ struct acct_info *info;
+ NTSTATUS status;
+ union samr_AliasInfo *alias_info = NULL;
+ const char *alias_name = NULL;
+ const char *alias_description = NULL;
+
+ DEBUG(5,("_samr_QueryAliasInfo: %d\n", __LINE__));
+
+ ainfo = samr_policy_handle_find(p,
+ r->in.alias_handle,
+ SAMR_HANDLE_ALIAS,
+ SAMR_ALIAS_ACCESS_LOOKUP_INFO,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ alias_info = talloc_zero(p->mem_ctx, union samr_AliasInfo);
+ if (!alias_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info = talloc_zero(p->mem_ctx, struct acct_info);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ status = pdb_get_aliasinfo(&ainfo->sid, info);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(info);
+ return status;
+ }
+
+ alias_name = talloc_steal(r, info->acct_name);
+ alias_description = talloc_steal(r, info->acct_desc);
+ TALLOC_FREE(info);
+
+ switch (r->in.level) {
+ case ALIASINFOALL:
+ alias_info->all.name.string = alias_name;
+ alias_info->all.num_members = 1; /* ??? */
+ alias_info->all.description.string = alias_description;
+ break;
+ case ALIASINFONAME:
+ alias_info->name.string = alias_name;
+ break;
+ case ALIASINFODESCRIPTION:
+ alias_info->description.string = alias_description;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = alias_info;
+
+ DEBUG(5,("_samr_QueryAliasInfo: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_LookupNames
+ ********************************************************************/
+
+NTSTATUS _samr_LookupNames(struct pipes_struct *p,
+ struct samr_LookupNames *r)
+{
+ struct samr_info *dinfo;
+ NTSTATUS status;
+ uint32_t *rid;
+ enum lsa_SidType *type;
+ uint32_t i, num_rids = r->in.num_names;
+ struct samr_Ids rids, types;
+ uint32_t num_mapped = 0;
+ struct dom_sid_buf buf;
+
+ DEBUG(5,("_samr_LookupNames: %d\n", __LINE__));
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ 0 /* Don't know the acc_bits yet */,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_LookupNames: truncating entries to %d\n", num_rids));
+ }
+
+ rid = talloc_array(p->mem_ctx, uint32_t, num_rids);
+ NT_STATUS_HAVE_NO_MEMORY(rid);
+
+ type = talloc_array(p->mem_ctx, enum lsa_SidType, num_rids);
+ NT_STATUS_HAVE_NO_MEMORY(type);
+
+ DEBUG(5,("_samr_LookupNames: looking name on SID %s\n",
+ dom_sid_str_buf(&dinfo->sid, &buf)));
+
+ for (i = 0; i < num_rids; i++) {
+
+ status = NT_STATUS_NONE_MAPPED;
+ type[i] = SID_NAME_UNKNOWN;
+
+ rid[i] = 0xffffffff;
+
+ if (sid_check_is_builtin(&dinfo->sid)) {
+ if (lookup_builtin_name(r->in.names[i].string,
+ &rid[i]))
+ {
+ type[i] = SID_NAME_ALIAS;
+ }
+ } else {
+ lookup_global_sam_name(r->in.names[i].string, 0,
+ &rid[i], &type[i]);
+ }
+
+ if (type[i] != SID_NAME_UNKNOWN) {
+ num_mapped++;
+ }
+ }
+
+ if (num_mapped == num_rids) {
+ status = NT_STATUS_OK;
+ } else if (num_mapped == 0) {
+ status = NT_STATUS_NONE_MAPPED;
+ } else {
+ status = STATUS_SOME_UNMAPPED;
+ }
+
+ rids.count = num_rids;
+ rids.ids = rid;
+
+ types.count = num_rids;
+ types.ids = talloc_array(p->mem_ctx, uint32_t, num_rids);
+ NT_STATUS_HAVE_NO_MEMORY(type);
+ for (i = 0; i < num_rids; i++) {
+ types.ids[i] = (type[i] & 0xffffffff);
+ }
+
+ *r->out.rids = rids;
+ *r->out.types = types;
+
+ DEBUG(5,("_samr_LookupNames: %d\n", __LINE__));
+
+ return status;
+}
+
+/****************************************************************
+ _samr_ChangePasswordUser.
+
+ So old it is just not worth implementing
+ because it does not supply a plaintext and so we can't do password
+ complexity checking and cannot update other services that use a
+ plaintext password via passwd chat/pam password change/ldap password
+ sync.
+****************************************************************/
+
+NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
+ struct samr_ChangePasswordUser *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*******************************************************************
+ _samr_ChangePasswordUser2
+ ********************************************************************/
+
+NTSTATUS _samr_ChangePasswordUser2(struct pipes_struct *p,
+ struct samr_ChangePasswordUser2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ char *user_name = NULL;
+ char *rhost;
+ const char *wks = NULL;
+ bool encrypted;
+
+ DEBUG(5,("_samr_ChangePasswordUser2: %d\n", __LINE__));
+
+ if (!r->in.account->string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (r->in.server && r->in.server->string) {
+ wks = r->in.server->string;
+ }
+
+ DEBUG(5,("_samr_ChangePasswordUser2: user: %s wks: %s\n", user_name, wks));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(talloc_tos(), r->in.account->string, &user_name);
+ if (!user_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rhost = tsocket_address_inet_addr_string(remote_address,
+ talloc_tos());
+ if (rhost == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ encrypted = dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ status = pass_oem_change(user_name,
+ rhost,
+ r->in.lm_password->data,
+ r->in.lm_verifier->hash,
+ r->in.nt_password->data,
+ r->in.nt_verifier->hash,
+ NULL);
+
+ DEBUG(5,("_samr_ChangePasswordUser2: %d\n", __LINE__));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ return status;
+}
+
+/****************************************************************
+ _samr_OemChangePasswordUser2
+****************************************************************/
+
+NTSTATUS _samr_OemChangePasswordUser2(struct pipes_struct *p,
+ struct samr_OemChangePasswordUser2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ NTSTATUS status;
+ char *user_name = NULL;
+ const char *wks = NULL;
+ char *rhost;
+ bool encrypted;
+
+ DEBUG(5,("_samr_OemChangePasswordUser2: %d\n", __LINE__));
+
+ if (!r->in.account->string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (r->in.server && r->in.server->string) {
+ wks = r->in.server->string;
+ }
+
+ DEBUG(5,("_samr_OemChangePasswordUser2: user: %s wks: %s\n", user_name, wks));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(talloc_tos(), r->in.account->string, &user_name);
+ if (!user_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ if (!r->in.hash || !r->in.password) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rhost = tsocket_address_inet_addr_string(remote_address,
+ talloc_tos());
+ if (rhost == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ encrypted = dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pass_oem_change(user_name,
+ rhost,
+ r->in.password->data,
+ r->in.hash->hash,
+ 0,
+ 0,
+ NULL);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ DEBUG(5,("_samr_OemChangePasswordUser2: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_ChangePasswordUser3
+ ********************************************************************/
+
+NTSTATUS _samr_ChangePasswordUser3(struct pipes_struct *p,
+ struct samr_ChangePasswordUser3 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ NTSTATUS status;
+ char *user_name = NULL;
+ const char *wks = NULL;
+ enum samPwdChangeReason reject_reason;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t tmp;
+ char *rhost;
+
+ DEBUG(5,("_samr_ChangePasswordUser3: %d\n", __LINE__));
+
+ if (!r->in.account->string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (r->in.server && r->in.server->string) {
+ wks = r->in.server->string;
+ }
+
+ DEBUG(5,("_samr_ChangePasswordUser3: user: %s wks: %s\n", user_name, wks));
+
+ /*
+ * Pass the user through the NT -> unix user mapping
+ * function.
+ */
+
+ (void)map_username(talloc_tos(), r->in.account->string, &user_name);
+ if (!user_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rhost = tsocket_address_inet_addr_string(remote_address,
+ talloc_tos());
+ if (rhost == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ status = pass_oem_change(user_name,
+ rhost,
+ r->in.lm_password->data,
+ r->in.lm_verifier->hash,
+ r->in.nt_password->data,
+ r->in.nt_verifier->hash,
+ &reject_reason);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_RESTRICTION)) {
+
+ time_t u_expire, u_min_age;
+ uint32_t account_policy_temp;
+
+ dominfo = talloc_zero(p->mem_ctx, struct samr_DomInfo1);
+ if (!dominfo) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ reject = talloc_zero(p->mem_ctx,
+ struct userPwdChangeFailureInformation);
+ if (!reject) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &tmp);
+ dominfo->min_password_length = tmp;
+
+ pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &tmp);
+ dominfo->password_history_length = tmp;
+
+ pdb_get_account_policy(PDB_POLICY_USER_MUST_LOGON_TO_CHG_PASS,
+ &dominfo->password_properties);
+
+ pdb_get_account_policy(PDB_POLICY_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs((NTTIME *)&dominfo->max_password_age, u_expire);
+ unix_to_nt_time_abs((NTTIME *)&dominfo->min_password_age, u_min_age);
+
+ if (lp_check_password_script(talloc_tos(), lp_sub)
+ && *lp_check_password_script(talloc_tos(), lp_sub)) {
+ dominfo->password_properties |= DOMAIN_PASSWORD_COMPLEX;
+ }
+
+ reject->extendedFailureReason = reject_reason;
+
+ *r->out.dominfo = dominfo;
+ *r->out.reject = reject;
+ }
+
+ DEBUG(5,("_samr_ChangePasswordUser3: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+makes a SAMR_R_LOOKUP_RIDS structure.
+********************************************************************/
+
+static bool make_samr_lookup_rids(TALLOC_CTX *ctx, uint32_t num_names,
+ const char **names,
+ struct lsa_String **lsa_name_array_p)
+{
+ struct lsa_String *lsa_name_array = NULL;
+ uint32_t i;
+
+ *lsa_name_array_p = NULL;
+
+ if (num_names != 0) {
+ lsa_name_array = talloc_zero_array(ctx, struct lsa_String, num_names);
+ if (!lsa_name_array) {
+ return false;
+ }
+ }
+
+ for (i = 0; i < num_names; i++) {
+ DEBUG(10, ("names[%d]:%s\n", i, names[i] && *names[i] ? names[i] : ""));
+ init_lsa_String(&lsa_name_array[i], names[i]);
+ }
+
+ *lsa_name_array_p = lsa_name_array;
+
+ return true;
+}
+
+/*******************************************************************
+ _samr_LookupRids
+ ********************************************************************/
+
+NTSTATUS _samr_LookupRids(struct pipes_struct *p,
+ struct samr_LookupRids *r)
+{
+ struct samr_info *dinfo;
+ NTSTATUS status;
+ const char **names;
+ enum lsa_SidType *attrs = NULL;
+ uint32_t *wire_attrs = NULL;
+ int num_rids = (int)r->in.num_rids;
+ int i;
+ struct lsa_Strings names_array;
+ struct samr_Ids types_array;
+ struct lsa_String *lsa_names = NULL;
+
+ DEBUG(5,("_samr_LookupRids: %d\n", __LINE__));
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ 0 /* Don't know the acc_bits yet */,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (num_rids > 1000) {
+ DEBUG(0, ("Got asked for %d rids (more than 1000) -- according "
+ "to samba4 idl this is not possible\n", num_rids));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (num_rids) {
+ names = talloc_zero_array(p->mem_ctx, const char *, num_rids);
+ attrs = talloc_zero_array(p->mem_ctx, enum lsa_SidType, num_rids);
+ wire_attrs = talloc_zero_array(p->mem_ctx, uint32_t, num_rids);
+
+ if ((names == NULL) || (attrs == NULL) || (wire_attrs==NULL))
+ return NT_STATUS_NO_MEMORY;
+ } else {
+ names = NULL;
+ attrs = NULL;
+ wire_attrs = NULL;
+ }
+
+ become_root(); /* lookup_sid can require root privs */
+ status = pdb_lookup_rids(&dinfo->sid, num_rids, r->in.rids,
+ names, attrs);
+ unbecome_root();
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) && (num_rids == 0)) {
+ status = NT_STATUS_OK;
+ }
+
+ if (!make_samr_lookup_rids(p->mem_ctx, num_rids, names,
+ &lsa_names)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Convert from enum lsa_SidType to uint32_t for wire format. */
+ for (i = 0; i < num_rids; i++) {
+ wire_attrs[i] = (uint32_t)attrs[i];
+ }
+
+ names_array.count = num_rids;
+ names_array.names = lsa_names;
+
+ types_array.count = num_rids;
+ types_array.ids = wire_attrs;
+
+ *r->out.names = names_array;
+ *r->out.types = types_array;
+
+ DEBUG(5,("_samr_LookupRids: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_OpenUser
+********************************************************************/
+
+NTSTATUS _samr_OpenUser(struct pipes_struct *p,
+ struct samr_OpenUser *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct samu *sampass=NULL;
+ struct dom_sid sid;
+ struct samr_info *dinfo;
+ struct security_descriptor *psd = NULL;
+ uint32_t acc_granted;
+ uint32_t des_access = r->in.access_mask;
+ uint32_t extra_access = 0;
+ size_t sd_size;
+ bool ret;
+ NTSTATUS nt_status;
+
+ /* These two privileges, if != SEC_PRIV_INVALID, indicate
+ * privileges that the user must have to complete this
+ * operation in defience of the fixed ACL */
+ enum sec_privilege needed_priv_1, needed_priv_2;
+ NTSTATUS status;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ( !(sampass = samu_new( p->mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* append the user's RID to it */
+
+ if (!sid_compose(&sid, &dinfo->sid, r->in.rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /* check if access can be granted as requested by client. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping, &sid, SAMR_USR_RIGHTS_WRITE_PW);
+ se_map_generic(&des_access, &usr_generic_mapping);
+
+ /*
+ * Get the sampass first as we need to check privileges
+ * based on what kind of user object this is.
+ * But don't reveal info too early if it didn't exist.
+ */
+
+ become_root();
+ ret=pdb_getsampwsid(sampass, &sid);
+ unbecome_root();
+
+ needed_priv_1 = SEC_PRIV_INVALID;
+ needed_priv_2 = SEC_PRIV_INVALID;
+ /*
+ * We do the override access checks on *open*, not at
+ * SetUserInfo time.
+ */
+ if (ret) {
+ uint32_t acb_info = pdb_get_acct_ctrl(sampass);
+
+ if (acb_info & ACB_WSTRUST) {
+ /*
+ * SeMachineAccount is needed to add
+ * GENERIC_RIGHTS_USER_WRITE to a machine
+ * account.
+ */
+ needed_priv_1 = SEC_PRIV_MACHINE_ACCOUNT;
+ }
+ if (acb_info & ACB_NORMAL) {
+ /*
+ * SeAddUsers is needed to add
+ * GENERIC_RIGHTS_USER_WRITE to a normal
+ * account.
+ */
+ needed_priv_1 = SEC_PRIV_ADD_USERS;
+ }
+ /*
+ * Cheat - we have not set a specific privilege for
+ * server (BDC) or domain trust account, so allow
+ * GENERIC_RIGHTS_USER_WRITE if pipe user is in
+ * DOMAIN_RID_ADMINS.
+ */
+ if (acb_info & (ACB_SVRTRUST|ACB_DOMTRUST)) {
+ if (lp_enable_privileges() &&
+ nt_token_check_domain_rid(
+ session_info->security_token,
+ DOMAIN_RID_ADMINS)) {
+ des_access &= ~GENERIC_RIGHTS_USER_WRITE;
+ extra_access = GENERIC_RIGHTS_USER_WRITE;
+ DEBUG(4,("_samr_OpenUser: Allowing "
+ "GENERIC_RIGHTS_USER_WRITE for "
+ "rid admins\n"));
+ }
+ }
+ }
+
+ TALLOC_FREE(sampass);
+
+ nt_status = access_check_object(psd, session_info->security_token,
+ needed_priv_1, needed_priv_2,
+ GENERIC_RIGHTS_USER_WRITE, des_access,
+ &acc_granted, "_samr_OpenUser");
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ /* check that the SID exists in our domain. */
+ if (ret == False) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* If we did the rid admins hack above, allow access. */
+ acc_granted |= extra_access;
+
+ status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_USER,
+ acc_granted,
+ &sid,
+ NULL,
+ r->out.user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ *************************************************************************/
+
+static NTSTATUS init_samr_parameters_string(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ struct lsa_BinaryString **_r)
+{
+ struct lsa_BinaryString *r;
+
+ if (!blob || !_r) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ r = talloc_zero(mem_ctx, struct lsa_BinaryString);
+ if (!r) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->array = talloc_zero_array(mem_ctx, uint16_t, blob->length/2);
+ if (!r->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(r->array, blob->data, blob->length);
+ r->size = blob->length;
+ r->length = blob->length;
+
+ if (!r->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *_r = r;
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ *************************************************************************/
+
+static struct samr_LogonHours get_logon_hours_from_pdb(TALLOC_CTX *mem_ctx,
+ struct samu *pw)
+{
+ struct samr_LogonHours hours;
+ const int units_per_week = 168;
+
+ ZERO_STRUCT(hours);
+ hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
+ if (!hours.bits) {
+ return hours;
+ }
+
+ hours.units_per_week = units_per_week;
+ memset(hours.bits, 0xFF, units_per_week);
+
+ if (pdb_get_hours(pw)) {
+ memcpy(hours.bits, pdb_get_hours(pw),
+ MIN(pdb_get_hours_len(pw), units_per_week));
+ }
+
+ return hours;
+}
+
+/*************************************************************************
+ get_user_info_1.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_1(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo1 *r,
+ struct samu *pw,
+ struct dom_sid *domain_sid)
+{
+ const struct dom_sid *sid_group;
+ uint32_t primary_gid;
+
+ become_root();
+ sid_group = pdb_get_group_sid(pw);
+ unbecome_root();
+
+ if (!sid_peek_check_rid(domain_sid, sid_group, &primary_gid)) {
+ struct dom_sid_buf buf1, buf2;
+
+ DEBUG(0, ("get_user_info_1: User %s has Primary Group SID %s, \n"
+ "which conflicts with the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_group, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ r->account_name.string = talloc_strdup(mem_ctx, pdb_get_username(pw));
+ r->full_name.string = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+ r->primary_gid = primary_gid;
+ r->description.string = talloc_strdup(mem_ctx, pdb_get_acct_desc(pw));
+ r->comment.string = talloc_strdup(mem_ctx, pdb_get_comment(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_2.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_2(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo2 *r,
+ struct samu *pw)
+{
+ r->comment.string = talloc_strdup(mem_ctx, pdb_get_comment(pw));
+ r->reserved.string = NULL;
+ r->country_code = pdb_get_country_code(pw);
+ r->code_page = pdb_get_code_page(pw);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_3.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_3(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo3 *r,
+ struct samu *pw,
+ struct dom_sid *domain_sid)
+{
+ const struct dom_sid *sid_user, *sid_group;
+ uint32_t rid, primary_gid;
+ struct dom_sid_buf buf1, buf2;
+
+ sid_user = pdb_get_user_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, sid_user, &rid)) {
+ DEBUG(0, ("get_user_info_3: User %s has SID %s, \nwhich conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_user, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ sid_group = pdb_get_group_sid(pw);
+ unbecome_root();
+
+ if (!sid_peek_check_rid(domain_sid, sid_group, &primary_gid)) {
+ DEBUG(0, ("get_user_info_3: User %s has Primary Group SID %s, \n"
+ "which conflicts with the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_group, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ unix_to_nt_time(&r->last_logon, pdb_get_logon_time(pw));
+ unix_to_nt_time(&r->last_logoff, pdb_get_logoff_time(pw));
+ unix_to_nt_time(&r->last_password_change, pdb_get_pass_last_set_time(pw));
+ unix_to_nt_time(&r->allow_password_change, pdb_get_pass_can_change_time(pw));
+ unix_to_nt_time(&r->force_password_change, pdb_get_pass_must_change_time(pw));
+
+ r->account_name.string = talloc_strdup(mem_ctx, pdb_get_username(pw));
+ r->full_name.string = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+ r->home_directory.string= talloc_strdup(mem_ctx, pdb_get_homedir(pw));
+ r->home_drive.string = talloc_strdup(mem_ctx, pdb_get_dir_drive(pw));
+ r->logon_script.string = talloc_strdup(mem_ctx, pdb_get_logon_script(pw));
+ r->profile_path.string = talloc_strdup(mem_ctx, pdb_get_profile_path(pw));
+ r->workstations.string = talloc_strdup(mem_ctx, pdb_get_workstations(pw));
+
+ r->logon_hours = get_logon_hours_from_pdb(mem_ctx, pw);
+ r->rid = rid;
+ r->primary_gid = primary_gid;
+ r->acct_flags = pdb_get_acct_ctrl(pw);
+ r->bad_password_count = pdb_get_bad_password_count(pw);
+ r->logon_count = pdb_get_logon_count(pw);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_4.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_4(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo4 *r,
+ struct samu *pw)
+{
+ r->logon_hours = get_logon_hours_from_pdb(mem_ctx, pw);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_5.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_5(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo5 *r,
+ struct samu *pw,
+ struct dom_sid *domain_sid)
+{
+ const struct dom_sid *sid_user, *sid_group;
+ uint32_t rid, primary_gid;
+ struct dom_sid_buf buf1, buf2;
+
+ sid_user = pdb_get_user_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, sid_user, &rid)) {
+ DEBUG(0, ("get_user_info_5: User %s has SID %s, \nwhich conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_user, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ sid_group = pdb_get_group_sid(pw);
+ unbecome_root();
+
+ if (!sid_peek_check_rid(domain_sid, sid_group, &primary_gid)) {
+ DEBUG(0, ("get_user_info_5: User %s has Primary Group SID %s, \n"
+ "which conflicts with the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_group, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ unix_to_nt_time(&r->last_logon, pdb_get_logon_time(pw));
+ unix_to_nt_time(&r->last_logoff, pdb_get_logoff_time(pw));
+ unix_to_nt_time(&r->acct_expiry, pdb_get_kickoff_time(pw));
+ unix_to_nt_time(&r->last_password_change, pdb_get_pass_last_set_time(pw));
+
+ r->account_name.string = talloc_strdup(mem_ctx, pdb_get_username(pw));
+ r->full_name.string = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+ r->home_directory.string= talloc_strdup(mem_ctx, pdb_get_homedir(pw));
+ r->home_drive.string = talloc_strdup(mem_ctx, pdb_get_dir_drive(pw));
+ r->logon_script.string = talloc_strdup(mem_ctx, pdb_get_logon_script(pw));
+ r->profile_path.string = talloc_strdup(mem_ctx, pdb_get_profile_path(pw));
+ r->description.string = talloc_strdup(mem_ctx, pdb_get_acct_desc(pw));
+ r->workstations.string = talloc_strdup(mem_ctx, pdb_get_workstations(pw));
+
+ r->logon_hours = get_logon_hours_from_pdb(mem_ctx, pw);
+ r->rid = rid;
+ r->primary_gid = primary_gid;
+ r->acct_flags = pdb_get_acct_ctrl(pw);
+ r->bad_password_count = pdb_get_bad_password_count(pw);
+ r->logon_count = pdb_get_logon_count(pw);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_6.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_6(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo6 *r,
+ struct samu *pw)
+{
+ r->account_name.string = talloc_strdup(mem_ctx, pdb_get_username(pw));
+ r->full_name.string = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_7. Safe. Only gives out account_name.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_7(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo7 *r,
+ struct samu *smbpass)
+{
+ r->account_name.string = talloc_strdup(mem_ctx, pdb_get_username(smbpass));
+ if (!r->account_name.string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_8.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_8(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo8 *r,
+ struct samu *pw)
+{
+ r->full_name.string = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_9. Only gives out primary group SID.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_9(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo9 *r,
+ struct samu *smbpass)
+{
+ r->primary_gid = pdb_get_group_rid(smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_10.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_10(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo10 *r,
+ struct samu *pw)
+{
+ r->home_directory.string= talloc_strdup(mem_ctx, pdb_get_homedir(pw));
+ r->home_drive.string = talloc_strdup(mem_ctx, pdb_get_dir_drive(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_11.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_11(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo11 *r,
+ struct samu *pw)
+{
+ r->logon_script.string = talloc_strdup(mem_ctx, pdb_get_logon_script(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_12.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_12(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo12 *r,
+ struct samu *pw)
+{
+ r->profile_path.string = talloc_strdup(mem_ctx, pdb_get_profile_path(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_13.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_13(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo13 *r,
+ struct samu *pw)
+{
+ r->description.string = talloc_strdup(mem_ctx, pdb_get_acct_desc(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_14.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_14(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo14 *r,
+ struct samu *pw)
+{
+ r->workstations.string = talloc_strdup(mem_ctx, pdb_get_workstations(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_16. Safe. Only gives out acb bits.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_16(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo16 *r,
+ struct samu *smbpass)
+{
+ r->acct_flags = pdb_get_acct_ctrl(smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_17.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_17(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo17 *r,
+ struct samu *pw)
+{
+ unix_to_nt_time(&r->acct_expiry, pdb_get_kickoff_time(pw));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_18. OK - this is the killer as it gives out password info.
+ Ensure that this is only allowed on an encrypted connection with a root
+ user. JRA.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_18(struct pipes_struct *p,
+ TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo18 *r,
+ struct dom_sid *user_sid)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct samu *smbpass=NULL;
+ bool ret;
+ const uint8_t *nt_pass = NULL;
+ const uint8_t *lm_pass = NULL;
+
+ ZERO_STRUCTP(r);
+
+ if (p->transport != NCALRPC) {
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!security_token_is_system(session_info->security_token)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /*
+ * Do *NOT* do become_root()/unbecome_root() here ! JRA.
+ */
+
+ if ( !(smbpass = samu_new( mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = pdb_getsampwsid(smbpass, user_sid);
+
+ if (ret == False) {
+ struct dom_sid_buf buf;
+ DEBUG(4, ("User %s not found\n",
+ dom_sid_str_buf(user_sid, &buf)));
+ TALLOC_FREE(smbpass);
+ return root_mode() ? NT_STATUS_NO_SUCH_USER : NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG(3,("User:[%s] 0x%x\n", pdb_get_username(smbpass), pdb_get_acct_ctrl(smbpass) ));
+
+ if ( pdb_get_acct_ctrl(smbpass) & ACB_DISABLED) {
+ TALLOC_FREE(smbpass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ lm_pass = pdb_get_lanman_passwd(smbpass);
+ if (lm_pass != NULL) {
+ memcpy(r->lm_pwd.hash, lm_pass, 16);
+ r->lm_pwd_active = true;
+ }
+
+ nt_pass = pdb_get_nt_passwd(smbpass);
+ if (nt_pass != NULL) {
+ memcpy(r->nt_pwd.hash, nt_pass, 16);
+ r->nt_pwd_active = true;
+ }
+ r->password_expired = 0; /* FIXME */
+
+ TALLOC_FREE(smbpass);
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ get_user_info_20
+ *************************************************************************/
+
+static NTSTATUS get_user_info_20(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo20 *r,
+ struct samu *sampass)
+{
+ const char *munged_dial = NULL;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct lsa_BinaryString *parameters = NULL;
+
+ ZERO_STRUCTP(r);
+
+ munged_dial = pdb_get_munged_dial(sampass);
+
+ DEBUG(3,("User:[%s] has [%s] (length: %d)\n", pdb_get_username(sampass),
+ munged_dial, (int)strlen(munged_dial)));
+
+ if (munged_dial) {
+ blob = base64_decode_data_blob(munged_dial);
+ } else {
+ blob = data_blob_string_const_null("");
+ }
+
+ status = init_samr_parameters_string(mem_ctx, &blob, &parameters);
+ data_blob_free(&blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->parameters = *parameters;
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************************
+ get_user_info_21
+ *************************************************************************/
+
+static NTSTATUS get_user_info_21(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo21 *r,
+ struct samu *pw,
+ struct dom_sid *domain_sid,
+ uint32_t acc_granted)
+{
+ NTSTATUS status;
+ const struct dom_sid *sid_user, *sid_group;
+ uint32_t rid, primary_gid;
+ NTTIME force_password_change;
+ time_t must_change_time;
+ struct lsa_BinaryString *parameters = NULL;
+ const char *munged_dial = NULL;
+ DATA_BLOB blob;
+ struct dom_sid_buf buf1, buf2;
+
+ ZERO_STRUCTP(r);
+
+ sid_user = pdb_get_user_sid(pw);
+
+ if (!sid_peek_check_rid(domain_sid, sid_user, &rid)) {
+ DEBUG(0, ("get_user_info_21: User %s has SID %s, \nwhich conflicts with "
+ "the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_user, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ sid_group = pdb_get_group_sid(pw);
+ unbecome_root();
+
+ if (!sid_peek_check_rid(domain_sid, sid_group, &primary_gid)) {
+ DEBUG(0, ("get_user_info_21: User %s has Primary Group SID %s, \n"
+ "which conflicts with the domain sid %s. Failing operation.\n",
+ pdb_get_username(pw),
+ dom_sid_str_buf(sid_group, &buf1),
+ dom_sid_str_buf(domain_sid, &buf2)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ unix_to_nt_time(&r->last_logon, pdb_get_logon_time(pw));
+ unix_to_nt_time(&r->last_logoff, pdb_get_logoff_time(pw));
+ unix_to_nt_time(&r->acct_expiry, pdb_get_kickoff_time(pw));
+ unix_to_nt_time(&r->last_password_change, pdb_get_pass_last_set_time(pw));
+ unix_to_nt_time(&r->allow_password_change, pdb_get_pass_can_change_time(pw));
+
+ must_change_time = pdb_get_pass_must_change_time(pw);
+ if (pdb_is_password_change_time_max(must_change_time)) {
+ unix_to_nt_time_abs(&force_password_change, must_change_time);
+ } else {
+ unix_to_nt_time(&force_password_change, must_change_time);
+ }
+
+ munged_dial = pdb_get_munged_dial(pw);
+ if (munged_dial) {
+ blob = base64_decode_data_blob(munged_dial);
+ } else {
+ blob = data_blob_string_const_null("");
+ }
+
+ status = init_samr_parameters_string(mem_ctx, &blob, &parameters);
+ data_blob_free(&blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->force_password_change = force_password_change;
+
+ r->account_name.string = talloc_strdup(mem_ctx, pdb_get_username(pw));
+ r->full_name.string = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+ r->home_directory.string = talloc_strdup(mem_ctx, pdb_get_homedir(pw));
+ r->home_drive.string = talloc_strdup(mem_ctx, pdb_get_dir_drive(pw));
+ r->logon_script.string = talloc_strdup(mem_ctx, pdb_get_logon_script(pw));
+ r->profile_path.string = talloc_strdup(mem_ctx, pdb_get_profile_path(pw));
+ r->description.string = talloc_strdup(mem_ctx, pdb_get_acct_desc(pw));
+ r->workstations.string = talloc_strdup(mem_ctx, pdb_get_workstations(pw));
+ r->comment.string = talloc_strdup(mem_ctx, pdb_get_comment(pw));
+
+ r->logon_hours = get_logon_hours_from_pdb(mem_ctx, pw);
+ r->parameters = *parameters;
+ r->rid = rid;
+ r->primary_gid = primary_gid;
+ r->acct_flags = pdb_get_acct_ctrl(pw);
+ r->bad_password_count = pdb_get_bad_password_count(pw);
+ r->logon_count = pdb_get_logon_count(pw);
+ r->fields_present = pdb_build_fields_present(pw);
+ r->password_expired = (pdb_get_pass_must_change_time(pw) == 0) ?
+ PASS_MUST_CHANGE_AT_NEXT_LOGON : 0;
+ r->country_code = pdb_get_country_code(pw);
+ r->code_page = pdb_get_code_page(pw);
+ r->lm_password_set = 0;
+ r->nt_password_set = 0;
+
+#if 0
+
+ /*
+ Look at a user on a real NT4 PDC with usrmgr, press
+ 'ok'. Then you will see that fields_present is set to
+ 0x08f827fa. Look at the user immediately after that again,
+ and you will see that 0x00fffff is returned. This solves
+ the problem that you get access denied after having looked
+ at the user.
+ -- Volker
+ */
+
+#endif
+
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_QueryUserInfo
+ ********************************************************************/
+
+NTSTATUS _samr_QueryUserInfo(struct pipes_struct *p,
+ struct samr_QueryUserInfo *r)
+{
+ NTSTATUS status;
+ union samr_UserInfo *user_info = NULL;
+ struct samr_info *uinfo;
+ struct dom_sid domain_sid;
+ uint32_t rid;
+ bool ret = false;
+ struct samu *pwd = NULL;
+ uint32_t acc_required, acc_granted;
+ struct dom_sid_buf buf;
+
+ switch (r->in.level) {
+ case 1: /* UserGeneralInformation */
+ /* USER_READ_GENERAL */
+ acc_required = SAMR_USER_ACCESS_GET_NAME_ETC;
+ break;
+ case 2: /* UserPreferencesInformation */
+ /* USER_READ_PREFERENCES | USER_READ_GENERAL */
+ acc_required = SAMR_USER_ACCESS_GET_LOCALE |
+ SAMR_USER_ACCESS_GET_NAME_ETC;
+ break;
+ case 3: /* UserLogonInformation */
+ /* USER_READ_GENERAL | USER_READ_PREFERENCES | USER_READ_LOGON | USER_READ_ACCOUNT */
+ acc_required = SAMR_USER_ACCESS_GET_NAME_ETC |
+ SAMR_USER_ACCESS_GET_LOCALE |
+ SAMR_USER_ACCESS_GET_LOGONINFO |
+ SAMR_USER_ACCESS_GET_ATTRIBUTES;
+ break;
+ case 4: /* UserLogonHoursInformation */
+ /* USER_READ_LOGON */
+ acc_required = SAMR_USER_ACCESS_GET_LOGONINFO;
+ break;
+ case 5: /* UserAccountInformation */
+ /* USER_READ_GENERAL | USER_READ_PREFERENCES | USER_READ_LOGON | USER_READ_ACCOUNT */
+ acc_required = SAMR_USER_ACCESS_GET_NAME_ETC |
+ SAMR_USER_ACCESS_GET_LOCALE |
+ SAMR_USER_ACCESS_GET_LOGONINFO |
+ SAMR_USER_ACCESS_GET_ATTRIBUTES;
+ break;
+ case 6: /* UserNameInformation */
+ case 7: /* UserAccountNameInformation */
+ case 8: /* UserFullNameInformation */
+ case 9: /* UserPrimaryGroupInformation */
+ case 13: /* UserAdminCommentInformation */
+ /* USER_READ_GENERAL */
+ acc_required = SAMR_USER_ACCESS_GET_NAME_ETC;
+ break;
+ case 10: /* UserHomeInformation */
+ case 11: /* UserScriptInformation */
+ case 12: /* UserProfileInformation */
+ case 14: /* UserWorkStationsInformation */
+ /* USER_READ_LOGON */
+ acc_required = SAMR_USER_ACCESS_GET_LOGONINFO;
+ break;
+ case 16: /* UserControlInformation */
+ case 17: /* UserExpiresInformation */
+ case 20: /* UserParametersInformation */
+ /* USER_READ_ACCOUNT */
+ acc_required = SAMR_USER_ACCESS_GET_ATTRIBUTES;
+ break;
+ case 21: /* UserAllInformation */
+ /* FIXME! - gd */
+ acc_required = SAMR_USER_ACCESS_GET_ATTRIBUTES;
+ break;
+ case 18: /* UserInternal1Information */
+ /* FIXME! - gd */
+ acc_required = SAMR_USER_ACCESS_GET_ATTRIBUTES;
+ break;
+ case 23: /* UserInternal4Information */
+ case 24: /* UserInternal4InformationNew */
+ case 25: /* UserInternal4InformationNew */
+ case 26: /* UserInternal5InformationNew */
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ uinfo = samr_policy_handle_find(p,
+ r->in.user_handle,
+ SAMR_HANDLE_USER,
+ acc_required,
+ &acc_granted,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ domain_sid = uinfo->sid;
+
+ sid_split_rid(&domain_sid, &rid);
+
+ if (!sid_check_is_in_our_sam(&uinfo->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ DEBUG(5,("_samr_QueryUserInfo: sid:%s\n",
+ dom_sid_str_buf(&uinfo->sid, &buf)));
+
+ user_info = talloc_zero(p->mem_ctx, union samr_UserInfo);
+ if (!user_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5,("_samr_QueryUserInfo: user info level: %d\n", r->in.level));
+
+ if (!(pwd = samu_new(p->mem_ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(pwd, &uinfo->sid);
+ unbecome_root();
+
+ if (ret == false) {
+ DEBUG(4,("User %s not found\n",
+ dom_sid_str_buf(&uinfo->sid, &buf)));
+ TALLOC_FREE(pwd);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(pwd)));
+
+ samr_clear_sam_passwd(pwd);
+
+ switch (r->in.level) {
+ case 1:
+ status = get_user_info_1(p->mem_ctx, &user_info->info1, pwd, &domain_sid);
+ break;
+ case 2:
+ status = get_user_info_2(p->mem_ctx, &user_info->info2, pwd);
+ break;
+ case 3:
+ status = get_user_info_3(p->mem_ctx, &user_info->info3, pwd, &domain_sid);
+ break;
+ case 4:
+ status = get_user_info_4(p->mem_ctx, &user_info->info4, pwd);
+ break;
+ case 5:
+ status = get_user_info_5(p->mem_ctx, &user_info->info5, pwd, &domain_sid);
+ break;
+ case 6:
+ status = get_user_info_6(p->mem_ctx, &user_info->info6, pwd);
+ break;
+ case 7:
+ status = get_user_info_7(p->mem_ctx, &user_info->info7, pwd);
+ break;
+ case 8:
+ status = get_user_info_8(p->mem_ctx, &user_info->info8, pwd);
+ break;
+ case 9:
+ status = get_user_info_9(p->mem_ctx, &user_info->info9, pwd);
+ break;
+ case 10:
+ status = get_user_info_10(p->mem_ctx, &user_info->info10, pwd);
+ break;
+ case 11:
+ status = get_user_info_11(p->mem_ctx, &user_info->info11, pwd);
+ break;
+ case 12:
+ status = get_user_info_12(p->mem_ctx, &user_info->info12, pwd);
+ break;
+ case 13:
+ status = get_user_info_13(p->mem_ctx, &user_info->info13, pwd);
+ break;
+ case 14:
+ status = get_user_info_14(p->mem_ctx, &user_info->info14, pwd);
+ break;
+ case 16:
+ status = get_user_info_16(p->mem_ctx, &user_info->info16, pwd);
+ break;
+ case 17:
+ status = get_user_info_17(p->mem_ctx, &user_info->info17, pwd);
+ break;
+ case 18:
+ /* level 18 is special */
+ status = get_user_info_18(p, p->mem_ctx, &user_info->info18,
+ &uinfo->sid);
+ break;
+ case 20:
+ status = get_user_info_20(p->mem_ctx, &user_info->info20, pwd);
+ break;
+ case 21:
+ status = get_user_info_21(p->mem_ctx, &user_info->info21, pwd, &domain_sid, acc_granted);
+ break;
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ *r->out.info = user_info;
+
+ done:
+ TALLOC_FREE(pwd);
+
+ DEBUG(5,("_samr_QueryUserInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_QueryUserInfo2(struct pipes_struct *p,
+ struct samr_QueryUserInfo2 *r)
+{
+ struct samr_QueryUserInfo u;
+
+ u.in.user_handle = r->in.user_handle;
+ u.in.level = r->in.level;
+ u.out.info = r->out.info;
+
+ return _samr_QueryUserInfo(p, &u);
+}
+
+/*******************************************************************
+ _samr_GetGroupsForUser
+ ********************************************************************/
+
+NTSTATUS _samr_GetGroupsForUser(struct pipes_struct *p,
+ struct samr_GetGroupsForUser *r)
+{
+ struct samr_info *uinfo;
+ struct samu *sam_pass=NULL;
+ struct dom_sid *sids;
+ struct samr_RidWithAttribute dom_gid;
+ struct samr_RidWithAttribute *gids = NULL;
+ uint32_t primary_group_rid;
+ uint32_t num_groups = 0;
+ gid_t *unix_gids;
+ uint32_t i, num_gids;
+ bool ret;
+ NTSTATUS result;
+ bool success = False;
+ struct dom_sid_buf buf;
+
+ struct samr_RidWithAttributeArray *rids = NULL;
+
+ /*
+ * from the SID in the request:
+ * we should send back the list of DOMAIN GROUPS
+ * the user is a member of
+ *
+ * and only the DOMAIN GROUPS
+ * no ALIASES !!! neither aliases of the domain
+ * nor aliases of the builtin SID
+ *
+ * JFM, 12/2/2001
+ */
+
+ DEBUG(5,("_samr_GetGroupsForUser: %d\n", __LINE__));
+
+ uinfo = samr_policy_handle_find(p,
+ r->in.user_handle,
+ SAMR_HANDLE_USER,
+ SAMR_USER_ACCESS_GET_GROUPS,
+ NULL,
+ &result);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ rids = talloc_zero(p->mem_ctx, struct samr_RidWithAttributeArray);
+ if (!rids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!sid_check_is_in_our_sam(&uinfo->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ if ( !(sam_pass = samu_new( p->mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(sam_pass, &uinfo->sid);
+ unbecome_root();
+
+ if (!ret) {
+ DEBUG(10, ("pdb_getsampwsid failed for %s\n",
+ dom_sid_str_buf(&uinfo->sid, &buf)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ sids = NULL;
+
+ /* make both calls inside the root block */
+ become_root();
+ result = pdb_enum_group_memberships(p->mem_ctx, sam_pass,
+ &sids, &unix_gids, &num_groups);
+ if ( NT_STATUS_IS_OK(result) ) {
+ success = sid_peek_check_rid(get_global_sam_sid(),
+ pdb_get_group_sid(sam_pass),
+ &primary_group_rid);
+ }
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("pdb_enum_group_memberships failed for %s\n",
+ dom_sid_str_buf(&uinfo->sid, &buf)));
+ return result;
+ }
+
+ if ( !success ) {
+ DEBUG(5, ("Group sid %s for user %s not in our domain\n",
+ dom_sid_str_buf(pdb_get_group_sid(sam_pass), &buf),
+ pdb_get_username(sam_pass)));
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ gids = NULL;
+ num_gids = 0;
+
+ dom_gid.attributes = SE_GROUP_DEFAULT_FLAGS;
+ dom_gid.rid = primary_group_rid;
+ ADD_TO_ARRAY(p->mem_ctx, struct samr_RidWithAttribute, dom_gid, &gids, &num_gids);
+
+ for (i=0; i<num_groups; i++) {
+
+ if (!sid_peek_check_rid(get_global_sam_sid(),
+ &(sids[i]), &dom_gid.rid)) {
+ DEBUG(10, ("Found sid %s not in our domain\n",
+ dom_sid_str_buf(&sids[i], &buf)));
+ continue;
+ }
+
+ if (dom_gid.rid == primary_group_rid) {
+ /* We added the primary group directly from the
+ * sam_account. The other SIDs are unique from
+ * enum_group_memberships */
+ continue;
+ }
+
+ ADD_TO_ARRAY(p->mem_ctx, struct samr_RidWithAttribute, dom_gid, &gids, &num_gids);
+ }
+
+ rids->count = num_gids;
+ rids->rids = gids;
+
+ *r->out.rids = rids;
+
+ DEBUG(5,("_samr_GetGroupsForUser: %d\n", __LINE__));
+
+ return result;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static uint32_t samr_get_server_role(void)
+{
+ uint32_t role = ROLE_DOMAIN_PDC;
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC) {
+ role = ROLE_DOMAIN_BDC;
+ }
+
+ return role;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_1(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t account_policy_temp;
+ time_t u_expire, u_min_age;
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &account_policy_temp);
+ r->min_password_length = account_policy_temp;
+
+ pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &account_policy_temp);
+ r->password_history_length = account_policy_temp;
+
+ pdb_get_account_policy(PDB_POLICY_USER_MUST_LOGON_TO_CHG_PASS,
+ &r->password_properties);
+
+ pdb_get_account_policy(PDB_POLICY_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs((NTTIME *)&r->max_password_age, u_expire);
+ unix_to_nt_time_abs((NTTIME *)&r->min_password_age, u_min_age);
+
+ if (lp_check_password_script(talloc_tos(), lp_sub) && *lp_check_password_script(talloc_tos(), lp_sub)){
+ r->password_properties |= DOMAIN_PASSWORD_COMPLEX;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_2(TALLOC_CTX *mem_ctx,
+ struct samr_DomGeneralInformation *r,
+ struct samr_info *dinfo)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t u_logout;
+ time_t seq_num;
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ r->num_users = count_sam_users(dinfo->disp_info, ACB_NORMAL);
+ r->num_groups = count_sam_groups(dinfo->disp_info);
+ r->num_aliases = count_sam_aliases(dinfo->disp_info);
+
+ pdb_get_account_policy(PDB_POLICY_TIME_TO_LOGOUT, &u_logout);
+
+ unix_to_nt_time_abs(&r->force_logoff_time, u_logout);
+
+ if (!pdb_get_seq_num(&seq_num)) {
+ seq_num = time(NULL);
+ }
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ r->oem_information.string = lp_server_string(r, lp_sub);
+ r->domain_name.string = lp_workgroup();
+ r->primary.string = lp_netbios_name();
+ r->sequence_num = seq_num;
+ r->domain_server_state = DOMAIN_SERVER_ENABLED;
+ r->role = (enum samr_Role) samr_get_server_role();
+ r->unknown3 = 1;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_3(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo3 *r)
+{
+ uint32_t u_logout;
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ {
+ uint32_t ul;
+ pdb_get_account_policy(PDB_POLICY_TIME_TO_LOGOUT, &ul);
+ u_logout = (time_t)ul;
+ }
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&r->force_logoff_time, u_logout);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_4(TALLOC_CTX *mem_ctx,
+ struct samr_DomOEMInformation *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ r->oem_information.string = lp_server_string(r, lp_sub);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_5(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo5 *r)
+{
+ r->domain_name.string = get_global_sam_name();
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_6(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo6 *r)
+{
+ /* NT returns its own name when a PDC. win2k and later
+ * only the name of the PDC if itself is a BDC (samba4
+ * idl) */
+ r->primary.string = lp_netbios_name();
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_7(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo7 *r)
+{
+ r->role = (enum samr_Role) samr_get_server_role();
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_8(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo8 *r)
+{
+ time_t seq_num;
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ if (!pdb_get_seq_num(&seq_num)) {
+ seq_num = time(NULL);
+ }
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ r->sequence_num = seq_num;
+ r->domain_create_time = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_9(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo9 *r)
+{
+ r->domain_server_state = DOMAIN_SERVER_ENABLED;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_11(TALLOC_CTX *mem_ctx,
+ struct samr_DomGeneralInformation2 *r,
+ struct samr_info *dinfo)
+{
+ NTSTATUS status;
+ uint32_t account_policy_temp;
+ time_t u_lock_duration, u_reset_time;
+
+ status = query_dom_info_2(mem_ctx, &r->general, dinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* AS ROOT !!! */
+
+ become_root();
+
+ pdb_get_account_policy(PDB_POLICY_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+ u_lock_duration = account_policy_temp;
+ if (u_lock_duration != -1) {
+ u_lock_duration *= 60;
+ }
+
+ pdb_get_account_policy(PDB_POLICY_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ pdb_get_account_policy(PDB_POLICY_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ r->lockout_threshold = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&r->lockout_duration, u_lock_duration);
+ unix_to_nt_time_abs(&r->lockout_window, u_reset_time);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_12(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *r)
+{
+ uint32_t account_policy_temp;
+ time_t u_lock_duration, u_reset_time;
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ pdb_get_account_policy(PDB_POLICY_LOCK_ACCOUNT_DURATION, &account_policy_temp);
+ u_lock_duration = account_policy_temp;
+ if (u_lock_duration != -1) {
+ u_lock_duration *= 60;
+ }
+
+ pdb_get_account_policy(PDB_POLICY_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ pdb_get_account_policy(PDB_POLICY_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ r->lockout_threshold = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&r->lockout_duration, u_lock_duration);
+ unix_to_nt_time_abs(&r->lockout_window, u_reset_time);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS query_dom_info_13(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo13 *r)
+{
+ time_t seq_num;
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ if (!pdb_get_seq_num(&seq_num)) {
+ seq_num = time(NULL);
+ }
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ r->sequence_num = seq_num;
+ r->domain_create_time = 0;
+ r->modified_count_at_last_promotion = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_QueryDomainInfo
+ ********************************************************************/
+
+NTSTATUS _samr_QueryDomainInfo(struct pipes_struct *p,
+ struct samr_QueryDomainInfo *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct samr_info *dinfo;
+ union samr_DomainInfo *dom_info;
+
+ uint32_t acc_required;
+
+ DEBUG(5,("_samr_QueryDomainInfo: %d\n", __LINE__));
+
+ switch (r->in.level) {
+ case 1: /* DomainPasswordInformation */
+ case 12: /* DomainLockoutInformation */
+ /* DOMAIN_READ_PASSWORD_PARAMETERS */
+ acc_required = SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1;
+ break;
+ case 11: /* DomainGeneralInformation2 */
+ /* DOMAIN_READ_PASSWORD_PARAMETERS |
+ * DOMAIN_READ_OTHER_PARAMETERS */
+ acc_required = SAMR_DOMAIN_ACCESS_LOOKUP_INFO_1 |
+ SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2;
+ break;
+ case 2: /* DomainGeneralInformation */
+ case 3: /* DomainLogoffInformation */
+ case 4: /* DomainOemInformation */
+ case 5: /* DomainReplicationInformation */
+ case 6: /* DomainReplicationInformation */
+ case 7: /* DomainServerRoleInformation */
+ case 8: /* DomainModifiedInformation */
+ case 9: /* DomainStateInformation */
+ case 10: /* DomainUasInformation */
+ case 13: /* DomainModifiedInformation2 */
+ /* DOMAIN_READ_OTHER_PARAMETERS */
+ acc_required = SAMR_DOMAIN_ACCESS_LOOKUP_INFO_2;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ acc_required,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dom_info = talloc_zero(p->mem_ctx, union samr_DomainInfo);
+ if (!dom_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ status = query_dom_info_1(p->mem_ctx, &dom_info->info1);
+ break;
+ case 2:
+ status = query_dom_info_2(p->mem_ctx, &dom_info->general, dinfo);
+ break;
+ case 3:
+ status = query_dom_info_3(p->mem_ctx, &dom_info->info3);
+ break;
+ case 4:
+ status = query_dom_info_4(p->mem_ctx, &dom_info->oem);
+ break;
+ case 5:
+ status = query_dom_info_5(p->mem_ctx, &dom_info->info5);
+ break;
+ case 6:
+ status = query_dom_info_6(p->mem_ctx, &dom_info->info6);
+ break;
+ case 7:
+ status = query_dom_info_7(p->mem_ctx, &dom_info->info7);
+ break;
+ case 8:
+ status = query_dom_info_8(p->mem_ctx, &dom_info->info8);
+ break;
+ case 9:
+ status = query_dom_info_9(p->mem_ctx, &dom_info->info9);
+ break;
+ case 11:
+ status = query_dom_info_11(p->mem_ctx, &dom_info->general2, dinfo);
+ break;
+ case 12:
+ status = query_dom_info_12(p->mem_ctx, &dom_info->info12);
+ break;
+ case 13:
+ status = query_dom_info_13(p->mem_ctx, &dom_info->info13);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.info = dom_info;
+
+ DEBUG(5,("_samr_QueryDomainInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/* W2k3 seems to use the same check for all 3 objects that can be created via
+ * SAMR, if you try to create for example "Dialup" as an alias it says
+ * "NT_STATUS_USER_EXISTS". This is racy, but we can't really lock the user
+ * database. */
+
+static NTSTATUS can_create(TALLOC_CTX *mem_ctx, const char *new_name)
+{
+ enum lsa_SidType type;
+ bool result;
+
+ DEBUG(10, ("Checking whether [%s] can be created\n", new_name));
+
+ become_root();
+ /* Lookup in our local databases (LOOKUP_NAME_REMOTE not set)
+ * whether the name already exists */
+ result = lookup_name(mem_ctx, new_name, LOOKUP_NAME_LOCAL,
+ NULL, NULL, NULL, &type);
+ unbecome_root();
+
+ if (!result) {
+ DEBUG(10, ("%s does not exist, can create it\n", new_name));
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5, ("trying to create %s, exists as %s\n",
+ new_name, sid_type_lookup(type)));
+
+ if (type == SID_NAME_DOM_GRP) {
+ return NT_STATUS_GROUP_EXISTS;
+ }
+ if (type == SID_NAME_ALIAS) {
+ return NT_STATUS_ALIAS_EXISTS;
+ }
+
+ /* Yes, the default is NT_STATUS_USER_EXISTS */
+ return NT_STATUS_USER_EXISTS;
+}
+
+/*******************************************************************
+ _samr_CreateUser2
+ ********************************************************************/
+
+NTSTATUS _samr_CreateUser2(struct pipes_struct *p,
+ struct samr_CreateUser2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const char *account = NULL;
+ struct dom_sid sid;
+ uint32_t acb_info = r->in.acct_flags;
+ struct samr_info *dinfo;
+ NTSTATUS nt_status;
+ uint32_t acc_granted;
+ struct security_descriptor *psd;
+ size_t sd_size;
+ /* check this, when giving away 'add computer to domain' privs */
+ uint32_t des_access = GENERIC_RIGHTS_USER_ALL_ACCESS;
+ bool can_add_account = False;
+
+ /* Which privilege is needed to override the ACL? */
+ enum sec_privilege needed_priv = SEC_PRIV_INVALID;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_CREATE_USER,
+ NULL,
+ &nt_status);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (sid_check_is_builtin(&dinfo->sid)) {
+ DEBUG(5,("_samr_CreateUser2: Refusing user create in BUILTIN\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!(acb_info == ACB_NORMAL || acb_info == ACB_DOMTRUST ||
+ acb_info == ACB_WSTRUST || acb_info == ACB_SVRTRUST)) {
+ /* Match Win2k, and return NT_STATUS_INVALID_PARAMETER if
+ this parameter is not an account type */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ account = r->in.account_name->string;
+ if (account == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = can_create(p->mem_ctx, account);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* determine which user right we need to check based on the acb_info */
+
+ if (root_mode()) {
+ can_add_account = true;
+ } else if (acb_info & ACB_WSTRUST) {
+ needed_priv = SEC_PRIV_MACHINE_ACCOUNT;
+ can_add_account = security_token_has_privilege(
+ session_info->security_token, needed_priv);
+ } else if (acb_info & ACB_NORMAL &&
+ (account[strlen(account)-1] != '$')) {
+ /* usrmgr.exe (and net rpc trustdom add) creates a normal user
+ account for domain trusts and changes the ACB flags later */
+ needed_priv = SEC_PRIV_ADD_USERS;
+ can_add_account = security_token_has_privilege(
+ session_info->security_token, needed_priv);
+ } else if (lp_enable_privileges()) {
+ /* implicit assumption of a BDC or domain trust account here
+ * (we already check the flags earlier) */
+ /* only Domain Admins can add a BDC or domain trust */
+ can_add_account = nt_token_check_domain_rid(
+ session_info->security_token,
+ DOMAIN_RID_ADMINS );
+ }
+
+ DEBUG(5, ("_samr_CreateUser2: %s can add this account : %s\n",
+ uidtoname(session_info->unix_token->uid),
+ can_add_account ? "True":"False" ));
+
+ if (!can_add_account) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /********** BEGIN Admin BLOCK **********/
+
+ (void)winbind_off();
+ become_root();
+ nt_status = pdb_create_user(p->mem_ctx, account, acb_info,
+ r->out.rid);
+ unbecome_root();
+ (void)winbind_on();
+
+ /********** END Admin BLOCK **********/
+
+ /* now check for failure */
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ /* Get the user's SID */
+
+ sid_compose(&sid, get_global_sam_sid(), *r->out.rid);
+
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping,
+ &sid, SAMR_USR_RIGHTS_WRITE_PW);
+ se_map_generic(&des_access, &usr_generic_mapping);
+
+ /*
+ * JRA - TESTME. We just created this user so we
+ * had rights to create them. Do we need to check
+ * any further access on this object ? Can't we
+ * just assume we have all the rights we need ?
+ */
+
+ nt_status = access_check_object(psd, session_info->security_token,
+ needed_priv, SEC_PRIV_INVALID,
+ GENERIC_RIGHTS_USER_WRITE, des_access,
+ &acc_granted, "_samr_CreateUser2");
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ return nt_status;
+ }
+
+ nt_status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_USER,
+ acc_granted,
+ &sid,
+ NULL,
+ r->out.user_handle);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* After a "set" ensure we have no cached display info. */
+ force_flush_samr_cache(&sid);
+
+ *r->out.access_granted = acc_granted;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_CreateUser(struct pipes_struct *p,
+ struct samr_CreateUser *r)
+{
+ struct samr_CreateUser2 c;
+ uint32_t access_granted;
+
+ c.in.domain_handle = r->in.domain_handle;
+ c.in.account_name = r->in.account_name;
+ c.in.acct_flags = ACB_NORMAL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.user_handle = r->out.user_handle;
+ c.out.access_granted = &access_granted;
+ c.out.rid = r->out.rid;
+
+ return _samr_CreateUser2(p, &c);
+}
+
+/*******************************************************************
+ _samr_Connect
+ ********************************************************************/
+
+NTSTATUS _samr_Connect(struct pipes_struct *p,
+ struct samr_Connect *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ uint32_t acc_granted;
+ uint32_t des_access = r->in.access_mask;
+ NTSTATUS status;
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _samr_Connect\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* don't give away the farm but this is probably ok. The SAMR_ACCESS_ENUM_DOMAINS
+ was observed from a win98 client trying to enumerate users (when configured
+ user level access control on shares) --jerry */
+
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ se_map_generic( &des_access, &sam_generic_mapping );
+
+ acc_granted = des_access & (SAMR_ACCESS_ENUM_DOMAINS
+ |SAMR_ACCESS_LOOKUP_DOMAIN);
+
+ /* set up the SAMR connect_anon response */
+ status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_CONNECT,
+ acc_granted,
+ NULL,
+ NULL,
+ r->out.connect_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_Connect2
+ ********************************************************************/
+
+NTSTATUS _samr_Connect2(struct pipes_struct *p,
+ struct samr_Connect2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *psd = NULL;
+ uint32_t acc_granted;
+ uint32_t des_access = r->in.access_mask;
+ NTSTATUS nt_status;
+ size_t sd_size;
+ const char *fn = "_samr_Connect2";
+
+ switch (dce_call->pkt.u.request.opnum) {
+ case NDR_SAMR_CONNECT2:
+ fn = "_samr_Connect2";
+ break;
+ case NDR_SAMR_CONNECT3:
+ fn = "_samr_Connect3";
+ break;
+ case NDR_SAMR_CONNECT4:
+ fn = "_samr_Connect4";
+ break;
+ case NDR_SAMR_CONNECT5:
+ fn = "_samr_Connect5";
+ break;
+ }
+
+ DEBUG(5,("%s: %d\n", fn, __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to %s\n", fn));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &sam_generic_mapping, NULL, 0);
+ se_map_generic(&des_access, &sam_generic_mapping);
+
+ nt_status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_INVALID, SEC_PRIV_INVALID,
+ 0, des_access, &acc_granted, fn);
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ nt_status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_CONNECT,
+ acc_granted,
+ NULL,
+ NULL,
+ r->out.connect_handle);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ DEBUG(5,("%s: %d\n", fn, __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+ _samr_Connect3
+****************************************************************/
+
+NTSTATUS _samr_Connect3(struct pipes_struct *p,
+ struct samr_Connect3 *r)
+{
+ struct samr_Connect2 c;
+
+ c.in.system_name = r->in.system_name;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return _samr_Connect2(p, &c);
+}
+
+/*******************************************************************
+ _samr_Connect4
+ ********************************************************************/
+
+NTSTATUS _samr_Connect4(struct pipes_struct *p,
+ struct samr_Connect4 *r)
+{
+ struct samr_Connect2 c;
+
+ c.in.system_name = r->in.system_name;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return _samr_Connect2(p, &c);
+}
+
+/*******************************************************************
+ _samr_Connect5
+ ********************************************************************/
+
+NTSTATUS _samr_Connect5(struct pipes_struct *p,
+ struct samr_Connect5 *r)
+{
+ NTSTATUS status;
+ struct samr_Connect2 c;
+ struct samr_ConnectInfo1 info1;
+
+ info1.client_version = SAMR_CONNECT_AFTER_W2K;
+ info1.supported_features = 0;
+
+ c.in.system_name = r->in.system_name;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ *r->out.level_out = 1;
+
+ status = _samr_Connect2(p, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info_out->info1 = info1;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ _samr_LookupDomain
+ **********************************************************************/
+
+NTSTATUS _samr_LookupDomain(struct pipes_struct *p,
+ struct samr_LookupDomain *r)
+{
+ NTSTATUS status;
+ const char *domain_name;
+ struct dom_sid *sid = NULL;
+ struct dom_sid_buf buf;
+
+ /* win9x user manager likes to use SAMR_ACCESS_ENUM_DOMAINS here.
+ Reverted that change so we will work with RAS servers again */
+
+ (void)samr_policy_handle_find(p,
+ r->in.connect_handle,
+ SAMR_HANDLE_CONNECT,
+ SAMR_ACCESS_LOOKUP_DOMAIN,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ domain_name = r->in.domain_name->string;
+ if (!domain_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sid = talloc_zero(p->mem_ctx, struct dom_sid2);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (strequal(domain_name, builtin_domain_name())) {
+ sid_copy(sid, &global_sid_Builtin);
+ } else {
+ if (!secrets_fetch_domain_sid(domain_name, sid)) {
+ status = NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ DEBUG(2,("Returning domain sid for domain %s -> %s\n", domain_name,
+ dom_sid_str_buf(sid, &buf)));
+
+ *r->out.sid = sid;
+
+ return status;
+}
+
+/**********************************************************************
+ _samr_EnumDomains
+ **********************************************************************/
+
+NTSTATUS _samr_EnumDomains(struct pipes_struct *p,
+ struct samr_EnumDomains *r)
+{
+ NTSTATUS status;
+ uint32_t num_entries = 2;
+ struct samr_SamEntry *entry_array = NULL;
+ struct samr_SamArray *sam;
+
+ (void)samr_policy_handle_find(p,
+ r->in.connect_handle,
+ SAMR_HANDLE_CONNECT,
+ SAMR_ACCESS_ENUM_DOMAINS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sam = talloc_zero(p->mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ entry_array = talloc_zero_array(p->mem_ctx,
+ struct samr_SamEntry,
+ num_entries);
+ if (!entry_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ entry_array[0].idx = 0;
+ init_lsa_String(&entry_array[0].name, get_global_sam_name());
+
+ entry_array[1].idx = 1;
+ init_lsa_String(&entry_array[1].name, "Builtin");
+
+ sam->count = num_entries;
+ sam->entries = entry_array;
+
+ *r->out.sam = sam;
+ *r->out.num_entries = num_entries;
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_OpenAlias
+ ********************************************************************/
+
+NTSTATUS _samr_OpenAlias(struct pipes_struct *p,
+ struct samr_OpenAlias *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dom_sid sid;
+ uint32_t alias_rid = r->in.rid;
+ struct samr_info *dinfo;
+ struct security_descriptor *psd = NULL;
+ uint32_t acc_granted;
+ uint32_t des_access = r->in.access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* append the alias' RID to it */
+
+ if (!sid_compose(&sid, &dinfo->sid, alias_rid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ /*check if access can be granted as requested by client. */
+
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &ali_generic_mapping, NULL, 0);
+ se_map_generic(&des_access,&ali_generic_mapping);
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_ADD_USERS, SEC_PRIV_INVALID,
+ GENERIC_RIGHTS_ALIAS_ALL_ACCESS,
+ des_access, &acc_granted, "_samr_OpenAlias");
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ {
+ /* Check we actually have the requested alias */
+ enum lsa_SidType type;
+ bool result;
+ gid_t gid;
+
+ become_root();
+ result = lookup_sid(NULL, &sid, NULL, NULL, &type);
+ unbecome_root();
+
+ if (!result || (type != SID_NAME_ALIAS)) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ /* make sure there is a mapping */
+
+ if ( !sid_to_gid( &sid, &gid ) ) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+
+ }
+
+ status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_ALIAS,
+ acc_granted,
+ &sid,
+ NULL,
+ r->out.alias_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_2
+ ********************************************************************/
+
+static NTSTATUS set_user_info_2(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo2 *id2,
+ struct samu *pwd)
+{
+ if (id2 == NULL) {
+ DEBUG(5,("set_user_info_2: NULL id2\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id2_to_sam_passwd(pwd, id2);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_4
+ ********************************************************************/
+
+static NTSTATUS set_user_info_4(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo4 *id4,
+ struct samu *pwd)
+{
+ if (id4 == NULL) {
+ DEBUG(5,("set_user_info_2: NULL id4\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id4_to_sam_passwd(pwd, id4);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_6
+ ********************************************************************/
+
+static NTSTATUS set_user_info_6(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo6 *id6,
+ struct samu *pwd)
+{
+ if (id6 == NULL) {
+ DEBUG(5,("set_user_info_6: NULL id6\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id6_to_sam_passwd(pwd, id6);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_7
+ ********************************************************************/
+
+static NTSTATUS set_user_info_7(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo7 *id7,
+ struct samu *pwd)
+{
+ NTSTATUS rc;
+
+ if (id7 == NULL) {
+ DEBUG(5, ("set_user_info_7: NULL id7\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!id7->account_name.string) {
+ DEBUG(5, ("set_user_info_7: failed to get new username\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* check to see if the new username already exists. Note: we can't
+ reliably lock all backends, so there is potentially the
+ possibility that a user can be created in between this check and
+ the rename. The rename should fail, but may not get the
+ exact same failure status code. I think this is small enough
+ of a window for this type of operation and the results are
+ simply that the rename fails with a slightly different status
+ code (like UNSUCCESSFUL instead of ALREADY_EXISTS). */
+
+ rc = can_create(mem_ctx, id7->account_name.string);
+
+ /* when there is nothing to change, we're done here */
+ if (NT_STATUS_EQUAL(rc, NT_STATUS_USER_EXISTS) &&
+ strequal(id7->account_name.string, pdb_get_username(pwd))) {
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(rc)) {
+ return rc;
+ }
+
+ rc = pdb_rename_sam_account(pwd, id7->account_name.string);
+
+ return rc;
+}
+
+/*******************************************************************
+ set_user_info_8
+ ********************************************************************/
+
+static NTSTATUS set_user_info_8(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo8 *id8,
+ struct samu *pwd)
+{
+ if (id8 == NULL) {
+ DEBUG(5,("set_user_info_8: NULL id8\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id8_to_sam_passwd(pwd, id8);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_10
+ ********************************************************************/
+
+static NTSTATUS set_user_info_10(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo10 *id10,
+ struct samu *pwd)
+{
+ if (id10 == NULL) {
+ DEBUG(5,("set_user_info_8: NULL id10\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id10_to_sam_passwd(pwd, id10);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_11
+ ********************************************************************/
+
+static NTSTATUS set_user_info_11(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo11 *id11,
+ struct samu *pwd)
+{
+ if (id11 == NULL) {
+ DEBUG(5,("set_user_info_11: NULL id11\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id11_to_sam_passwd(pwd, id11);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_12
+ ********************************************************************/
+
+static NTSTATUS set_user_info_12(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo12 *id12,
+ struct samu *pwd)
+{
+ if (id12 == NULL) {
+ DEBUG(5,("set_user_info_12: NULL id12\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id12_to_sam_passwd(pwd, id12);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_13
+ ********************************************************************/
+
+static NTSTATUS set_user_info_13(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo13 *id13,
+ struct samu *pwd)
+{
+ if (id13 == NULL) {
+ DEBUG(5,("set_user_info_13: NULL id13\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id13_to_sam_passwd(pwd, id13);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_14
+ ********************************************************************/
+
+static NTSTATUS set_user_info_14(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo14 *id14,
+ struct samu *pwd)
+{
+ if (id14 == NULL) {
+ DEBUG(5,("set_user_info_14: NULL id14\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id14_to_sam_passwd(pwd, id14);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_16
+ ********************************************************************/
+
+static NTSTATUS set_user_info_16(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo16 *id16,
+ struct samu *pwd)
+{
+ if (id16 == NULL) {
+ DEBUG(5,("set_user_info_16: NULL id16\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id16_to_sam_passwd(pwd, id16);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_17
+ ********************************************************************/
+
+static NTSTATUS set_user_info_17(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo17 *id17,
+ struct samu *pwd)
+{
+ if (id17 == NULL) {
+ DEBUG(5,("set_user_info_17: NULL id17\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id17_to_sam_passwd(pwd, id17);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_18
+ ********************************************************************/
+
+static NTSTATUS set_user_info_18(struct samr_UserInfo18 *id18,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ struct samu *pwd)
+{
+ int rc;
+
+ if (id18 == NULL) {
+ DEBUG(2, ("set_user_info_18: id18 is NULL\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id18->nt_pwd_active || id18->lm_pwd_active) {
+ if (!session_key->length) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ }
+
+ if (id18->nt_pwd_active) {
+ DATA_BLOB in = data_blob_const(id18->nt_pwd.hash, 16);
+ uint8_t outbuf[16] = { 0, };
+ DATA_BLOB out = data_blob_const(outbuf, sizeof(outbuf));
+
+ rc = sess_crypt_blob(&out, &in, session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ if (!pdb_set_nt_passwd(pwd, out.data, PDB_CHANGED)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ pdb_set_pass_last_set_time(pwd, time(NULL), PDB_CHANGED);
+ }
+
+ if (id18->lm_pwd_active) {
+ DATA_BLOB in = data_blob_const(id18->lm_pwd.hash, 16);
+ uint8_t outbuf[16] = { 0, };
+ DATA_BLOB out = data_blob_const(outbuf, sizeof(outbuf));
+
+ rc = sess_crypt_blob(&out, &in, session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ if (!pdb_set_lanman_passwd(pwd, out.data, PDB_CHANGED)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ pdb_set_pass_last_set_time(pwd, time(NULL), PDB_CHANGED);
+ }
+
+ copy_id18_to_sam_passwd(pwd, id18);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_20
+ ********************************************************************/
+
+static NTSTATUS set_user_info_20(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo20 *id20,
+ struct samu *pwd)
+{
+ if (id20 == NULL) {
+ DEBUG(5,("set_user_info_20: NULL id20\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id20_to_sam_passwd(pwd, id20);
+
+ return pdb_update_sam_account(pwd);
+}
+
+/*******************************************************************
+ set_user_info_21
+ ********************************************************************/
+
+static NTSTATUS set_user_info_21(struct samr_UserInfo21 *id21,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *session_key,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+ int rc;
+
+ if (id21 == NULL) {
+ DEBUG(5, ("set_user_info_21: NULL id21\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id21->fields_present == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id21->fields_present & SAMR_FIELD_LAST_PWD_CHANGE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (id21->fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ if (id21->nt_password_set) {
+ DATA_BLOB in = data_blob_const(
+ id21->nt_owf_password.array, 16);
+ uint8_t outbuf[16] = { 0, };
+ DATA_BLOB out = data_blob_const(
+ outbuf, sizeof(outbuf));
+
+ if ((id21->nt_owf_password.length != 16) ||
+ (id21->nt_owf_password.size != 16)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!session_key->length) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ rc = sess_crypt_blob(&out, &in, session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ pdb_set_nt_passwd(pwd, out.data, PDB_CHANGED);
+ pdb_set_pass_last_set_time(pwd, time(NULL), PDB_CHANGED);
+ }
+ }
+
+ if (id21->fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ if (id21->lm_password_set) {
+ DATA_BLOB in = data_blob_const(
+ id21->lm_owf_password.array, 16);
+ uint8_t outbuf[16] = { 0, };
+ DATA_BLOB out = data_blob_const(
+ outbuf, sizeof(outbuf));
+
+ if ((id21->lm_owf_password.length != 16) ||
+ (id21->lm_owf_password.size != 16)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!session_key->length) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ rc = sess_crypt_blob(&out, &in, session_key, SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ pdb_set_lanman_passwd(pwd, out.data, PDB_CHANGED);
+ pdb_set_pass_last_set_time(pwd, time(NULL), PDB_CHANGED);
+ }
+ }
+
+ /* we need to separately check for an account rename first */
+
+ if (id21->account_name.string &&
+ (!strequal(id21->account_name.string, pdb_get_username(pwd))))
+ {
+
+ /* check to see if the new username already exists. Note: we can't
+ reliably lock all backends, so there is potentially the
+ possibility that a user can be created in between this check and
+ the rename. The rename should fail, but may not get the
+ exact same failure status code. I think this is small enough
+ of a window for this type of operation and the results are
+ simply that the rename fails with a slightly different status
+ code (like UNSUCCESSFUL instead of ALREADY_EXISTS). */
+
+ status = can_create(mem_ctx, id21->account_name.string);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pdb_rename_sam_account(pwd, id21->account_name.string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("set_user_info_21: failed to rename account: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* set the new username so that later
+ functions can work on the new account */
+ pdb_set_username(pwd, id21->account_name.string, PDB_SET);
+ }
+
+ copy_id21_to_sam_passwd("INFO_21", pwd, id21);
+
+ /*
+ * The funny part about the previous two calls is
+ * that pwd still has the password hashes from the
+ * passdb entry. These have not been updated from
+ * id21. I don't know if they need to be set. --jerry
+ */
+
+ if ( IS_SAM_CHANGED(pwd, PDB_GROUPSID) ) {
+ status = pdb_set_unix_primary_group(mem_ctx, pwd);
+ if ( !NT_STATUS_IS_OK(status) ) {
+ return status;
+ }
+ }
+
+ /* Don't worry about writing out the user account since the
+ primary group SID is generated solely from the user's Unix
+ primary group. */
+
+ /* write the change out */
+ if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_23
+ ********************************************************************/
+
+static NTSTATUS set_user_info_23(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo23 *id23,
+ const char *rhost,
+ struct samu *pwd)
+{
+ char *plaintext_buf = NULL;
+ size_t len = 0;
+ uint32_t acct_ctrl;
+ NTSTATUS status;
+
+ if (id23 == NULL) {
+ DEBUG(5, ("set_user_info_23: NULL id23\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id23->info.fields_present == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id23->info.fields_present & SAMR_FIELD_LAST_PWD_CHANGE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((id23->info.fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (id23->info.fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT)) {
+
+ DEBUG(5, ("Attempting administrator password change (level 23) for user %s\n",
+ pdb_get_username(pwd)));
+
+ if (!decode_pw_buffer(mem_ctx,
+ id23->password.data,
+ &plaintext_buf,
+ &len,
+ CH_UTF16)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ copy_id23_to_sam_passwd(pwd, id23);
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if ( ( (acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+ ( (acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ( (acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST) ) {
+ DEBUG(5, ("Changing trust account. Not updating /etc/passwd\n"));
+ } else if (plaintext_buf) {
+ /* update the UNIX password */
+ if (lp_unix_password_sync() ) {
+ struct passwd *passwd;
+ if (pdb_get_username(pwd) == NULL) {
+ DEBUG(1, ("chgpasswd: User without name???\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ passwd = Get_Pwnam_alloc(pwd, pdb_get_username(pwd));
+ if (passwd == NULL) {
+ DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
+ }
+
+ if(!chgpasswd(pdb_get_username(pwd), rhost,
+ passwd, "", plaintext_buf, True)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ TALLOC_FREE(passwd);
+ }
+ }
+
+ BURN_STR(plaintext_buf);
+
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID) &&
+ (!NT_STATUS_IS_OK(status = pdb_set_unix_primary_group(mem_ctx,
+ pwd)))) {
+ return status;
+ }
+
+ if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_pw
+ ********************************************************************/
+
+static bool set_user_info_pw(uint8_t *pass, const char *rhost, struct samu *pwd)
+{
+ size_t len = 0;
+ char *plaintext_buf = NULL;
+ uint32_t acct_ctrl;
+
+ DEBUG(5, ("Attempting administrator password change for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ if (!decode_pw_buffer(talloc_tos(),
+ pass,
+ &plaintext_buf,
+ &len,
+ CH_UTF16)) {
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ return False;
+ }
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if ( ( (acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST ) ||
+ ( (acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ( (acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST) ) {
+ DEBUG(5, ("Changing trust account or non-unix-user password, not updating /etc/passwd\n"));
+ } else {
+ /* update the UNIX password */
+ if (lp_unix_password_sync()) {
+ struct passwd *passwd;
+
+ if (pdb_get_username(pwd) == NULL) {
+ DEBUG(1, ("chgpasswd: User without name???\n"));
+ return False;
+ }
+
+ passwd = Get_Pwnam_alloc(pwd, pdb_get_username(pwd));
+ if (passwd == NULL) {
+ DEBUG(1, ("chgpasswd: Username does not exist in system !?!\n"));
+ }
+
+ if(!chgpasswd(pdb_get_username(pwd), rhost, passwd,
+ "", plaintext_buf, True)) {
+ return False;
+ }
+ TALLOC_FREE(passwd);
+ }
+ }
+
+ BURN_STR(plaintext_buf);
+
+ DEBUG(5,("set_user_info_pw: pdb_update_pwd()\n"));
+
+ return True;
+}
+
+static bool
+set_user_info_pw_aes(DATA_BLOB *pw_data, const char *rhost, struct samu *pwd)
+{
+ uint32_t acct_ctrl;
+ DATA_BLOB new_password = {
+ .length = 0,
+ };
+ bool ok;
+
+ DBG_NOTICE("Attempting administrator password change for user %s\n",
+ pdb_get_username(pwd));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ ok = decode_pwd_string_from_buffer514(talloc_tos(),
+ pw_data->data,
+ CH_UTF16,
+ &new_password);
+ if (!ok) {
+ return false;
+ }
+
+ ok = pdb_set_plaintext_passwd(pwd, (char *)new_password.data);
+ if (!ok) {
+ return false;
+ }
+
+ /* if it's a trust account, don't update /etc/passwd */
+ if (((acct_ctrl & ACB_DOMTRUST) == ACB_DOMTRUST) ||
+ ((acct_ctrl & ACB_WSTRUST) == ACB_WSTRUST) ||
+ ((acct_ctrl & ACB_SVRTRUST) == ACB_SVRTRUST)) {
+ DBG_NOTICE("Changing trust account or non-unix-user password, "
+ "not updating /etc/passwd\n");
+ } else {
+ /* update the UNIX password */
+ if (lp_unix_password_sync()) {
+ struct passwd *passwd;
+ const char *username;
+
+ username = pdb_get_username(pwd);
+ if (username == NULL) {
+ DBG_WARNING("User unknown\n");
+ return false;
+ }
+
+ passwd = Get_Pwnam_alloc(pwd, username);
+ if (passwd == NULL) {
+ DBG_WARNING("chgpasswd: Username does not "
+ "exist on system !?!\n");
+ }
+
+ ok = chgpasswd(username,
+ rhost,
+ passwd,
+ "",
+ (char *)new_password.data,
+ true);
+ if (!ok) {
+ return false;
+ }
+ TALLOC_FREE(passwd);
+ }
+ }
+ TALLOC_FREE(new_password.data);
+
+ DBG_NOTICE("pdb_update_pwd()\n");
+
+ return true;
+}
+
+/*******************************************************************
+ set_user_info_24
+ ********************************************************************/
+
+static NTSTATUS set_user_info_24(TALLOC_CTX *mem_ctx,
+ const char *rhost,
+ struct samr_UserInfo24 *id24,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+
+ if (id24 == NULL) {
+ DEBUG(5, ("set_user_info_24: NULL id24\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!set_user_info_pw(id24->password.data, rhost, pwd)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ copy_id24_to_sam_passwd(pwd, id24);
+
+ status = pdb_update_sam_account(pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_25
+ ********************************************************************/
+
+static NTSTATUS set_user_info_25(TALLOC_CTX *mem_ctx,
+ const char *rhost,
+ struct samr_UserInfo25 *id25,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+
+ if (id25 == NULL) {
+ DEBUG(5, ("set_user_info_25: NULL id25\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id25->info.fields_present == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id25->info.fields_present & SAMR_FIELD_LAST_PWD_CHANGE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((id25->info.fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (id25->info.fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT)) {
+
+ if (!set_user_info_pw(id25->password.data, rhost, pwd)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ copy_id25_to_sam_passwd(pwd, id25);
+
+ /* write the change out */
+ if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
+ return status;
+ }
+
+ /*
+ * We need to "pdb_update_sam_account" before the unix primary group
+ * is set, because the idealx scripts would also change the
+ * sambaPrimaryGroupSid using the ldap replace method. pdb_ldap uses
+ * the delete explicit / add explicit, which would then fail to find
+ * the previous primaryGroupSid value.
+ */
+
+ if ( IS_SAM_CHANGED(pwd, PDB_GROUPSID) ) {
+ status = pdb_set_unix_primary_group(mem_ctx, pwd);
+ if ( !NT_STATUS_IS_OK(status) ) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_26
+ ********************************************************************/
+
+static NTSTATUS set_user_info_26(TALLOC_CTX *mem_ctx,
+ const char *rhost,
+ struct samr_UserInfo26 *id26,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+
+ if (id26 == NULL) {
+ DEBUG(5, ("set_user_info_26: NULL id26\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!set_user_info_pw(id26->password.data, rhost, pwd)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ copy_pwd_expired_to_sam_passwd(pwd, id26->password_expired);
+
+ status = pdb_update_sam_account(pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS set_user_info_31(TALLOC_CTX *mem_ctx,
+ const char *rhost,
+ DATA_BLOB *pw_data,
+ uint8_t password_expired,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+ bool ok;
+
+ if (pw_data->length == 0 || pw_data->length > 514) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ ok = set_user_info_pw_aes(pw_data, rhost, pwd);
+ if (!ok) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ copy_pwd_expired_to_sam_passwd(pwd, password_expired);
+
+ status = pdb_update_sam_account(pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS set_user_info_32(TALLOC_CTX *mem_ctx,
+ const char *rhost,
+ DATA_BLOB *pw_data,
+ struct samr_UserInfo32 *id32,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+ bool ok;
+
+ if (pw_data->length == 0 || pw_data->length > 514) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (id32 == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id32->info.fields_present == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (id32->info.fields_present & SAMR_FIELD_LAST_PWD_CHANGE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((id32->info.fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (id32->info.fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT)) {
+ ok = set_user_info_pw_aes(pw_data, rhost, pwd);
+ if (!ok) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ copy_id32_to_sam_passwd(pwd, id32);
+
+ status = pdb_update_sam_account(pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * We need to "pdb_update_sam_account" before the unix primary group
+ * is set, because the idealx scripts would also change the
+ * sambaPrimaryGroupSid using the ldap replace method. pdb_ldap uses
+ * the delete explicit / add explicit, which would then fail to find
+ * the previous primaryGroupSid value.
+ */
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID)) {
+ status = pdb_set_unix_primary_group(mem_ctx, pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************
+**************************************************************/
+
+static uint32_t samr_set_user_info_map_fields_to_access_mask(uint32_t fields)
+{
+ uint32_t acc_required = 0;
+
+ /* USER_ALL_USERNAME */
+ if (fields & SAMR_FIELD_ACCOUNT_NAME)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_FULLNAME */
+ if (fields & SAMR_FIELD_FULL_NAME)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_PRIMARYGROUPID */
+ if (fields & SAMR_FIELD_PRIMARY_GID)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_HOMEDIRECTORY */
+ if (fields & SAMR_FIELD_HOME_DIRECTORY)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_HOMEDIRECTORYDRIVE */
+ if (fields & SAMR_FIELD_HOME_DRIVE)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_SCRIPTPATH */
+ if (fields & SAMR_FIELD_LOGON_SCRIPT)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_PROFILEPATH */
+ if (fields & SAMR_FIELD_PROFILE_PATH)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_ADMINCOMMENT */
+ if (fields & SAMR_FIELD_COMMENT)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_WORKSTATIONS */
+ if (fields & SAMR_FIELD_WORKSTATIONS)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_LOGONHOURS */
+ if (fields & SAMR_FIELD_LOGON_HOURS)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_ACCOUNTEXPIRES */
+ if (fields & SAMR_FIELD_ACCT_EXPIRY)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_USERACCOUNTCONTROL */
+ if (fields & SAMR_FIELD_ACCT_FLAGS)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_PARAMETERS */
+ if (fields & SAMR_FIELD_PARAMETERS)
+ acc_required |= SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ /* USER_ALL_USERCOMMENT */
+ if (fields & SAMR_FIELD_COMMENT)
+ acc_required |= SAMR_USER_ACCESS_SET_LOC_COM;
+ /* USER_ALL_COUNTRYCODE */
+ if (fields & SAMR_FIELD_COUNTRY_CODE)
+ acc_required |= SAMR_USER_ACCESS_SET_LOC_COM;
+ /* USER_ALL_CODEPAGE */
+ if (fields & SAMR_FIELD_CODE_PAGE)
+ acc_required |= SAMR_USER_ACCESS_SET_LOC_COM;
+ /* USER_ALL_NTPASSWORDPRESENT */
+ if (fields & SAMR_FIELD_NT_PASSWORD_PRESENT)
+ acc_required |= SAMR_USER_ACCESS_SET_PASSWORD;
+ /* USER_ALL_LMPASSWORDPRESENT */
+ if (fields & SAMR_FIELD_LM_PASSWORD_PRESENT)
+ acc_required |= SAMR_USER_ACCESS_SET_PASSWORD;
+ /* USER_ALL_PASSWORDEXPIRED */
+ if (fields & SAMR_FIELD_EXPIRED_FLAG)
+ acc_required |= SAMR_USER_ACCESS_SET_PASSWORD;
+
+ return acc_required;
+}
+
+static NTSTATUS arc4_decrypt_data(DATA_BLOB session_key,
+ uint8_t *data,
+ size_t data_size)
+{
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t my_session_key = {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+ int rc;
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &my_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ rc = gnutls_cipher_decrypt(cipher_hnd,
+ data,
+ data_size);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto out;
+ }
+
+ status = NT_STATUS_OK;
+out:
+ return status;
+}
+
+/*******************************************************************
+ samr_SetUserInfo
+ ********************************************************************/
+
+NTSTATUS _samr_SetUserInfo(struct pipes_struct *p,
+ struct samr_SetUserInfo *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct samr_info *uinfo;
+ NTSTATUS status;
+ struct samu *pwd = NULL;
+ union samr_UserInfo *info = r->in.info;
+ uint32_t acc_required = 0;
+ uint32_t fields = 0;
+ bool ret;
+ char *rhost;
+ DATA_BLOB session_key;
+ struct dom_sid_buf buf;
+ struct loadparm_context *lp_ctx = NULL;
+ bool encrypted;
+
+ lp_ctx = loadparm_init_s3(p->mem_ctx, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* This is tricky. A WinXP domain join sets
+ (SAMR_USER_ACCESS_SET_PASSWORD|SAMR_USER_ACCESS_SET_ATTRIBUTES|SAMR_USER_ACCESS_GET_ATTRIBUTES)
+ The MMC lusrmgr plugin includes these perms and more in the SamrOpenUser(). But the
+ standard Win32 API calls just ask for SAMR_USER_ACCESS_SET_PASSWORD in the SamrOpenUser().
+ This should be enough for levels 18, 24, 25,& 26. Info level 23 can set more so
+ we'll use the set from the WinXP join as the basis. */
+
+ switch (r->in.level) {
+ case 2: /* UserPreferencesInformation */
+ /* USER_WRITE_ACCOUNT | USER_WRITE_PREFERENCES */
+ acc_required = SAMR_USER_ACCESS_SET_ATTRIBUTES | SAMR_USER_ACCESS_SET_LOC_COM;
+ break;
+ case 4: /* UserLogonHoursInformation */
+ case 6: /* UserNameInformation */
+ case 7: /* UserAccountNameInformation */
+ case 8: /* UserFullNameInformation */
+ case 9: /* UserPrimaryGroupInformation */
+ case 10: /* UserHomeInformation */
+ case 11: /* UserScriptInformation */
+ case 12: /* UserProfileInformation */
+ case 13: /* UserAdminCommentInformation */
+ case 14: /* UserWorkStationsInformation */
+ case 16: /* UserControlInformation */
+ case 17: /* UserExpiresInformation */
+ case 20: /* UserParametersInformation */
+ /* USER_WRITE_ACCOUNT */
+ acc_required = SAMR_USER_ACCESS_SET_ATTRIBUTES;
+ break;
+ case 18: /* UserInternal1Information */
+ /* FIXME: gd, this is a guess */
+ acc_required = SAMR_USER_ACCESS_SET_PASSWORD;
+ break;
+ case 21: /* UserAllInformation */
+ fields = info->info21.fields_present;
+ acc_required = samr_set_user_info_map_fields_to_access_mask(fields);
+ break;
+ case 23: /* UserInternal4Information */
+ fields = info->info23.info.fields_present;
+ acc_required = samr_set_user_info_map_fields_to_access_mask(fields);
+ break;
+ case 25: /* UserInternal4InformationNew */
+ fields = info->info25.info.fields_present;
+ acc_required = samr_set_user_info_map_fields_to_access_mask(fields);
+ break;
+ case 24: /* UserInternal5Information */
+ case 26: /* UserInternal5InformationNew */
+ case 31: /* UserInternal5InformationNew */
+ acc_required = SAMR_USER_ACCESS_SET_PASSWORD;
+ break;
+ case 32:
+ fields = info->info32.info.fields_present;
+ acc_required =
+ samr_set_user_info_map_fields_to_access_mask(fields);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ uinfo = samr_policy_handle_find(p,
+ r->in.user_handle,
+ SAMR_HANDLE_USER,
+ acc_required,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5, ("_samr_SetUserInfo: sid:%s, level:%d\n",
+ dom_sid_str_buf(&uinfo->sid, &buf),
+ r->in.level));
+
+ if (info == NULL) {
+ DEBUG(5, ("_samr_SetUserInfo: NULL info level\n"));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!(pwd = samu_new(NULL))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(pwd, &uinfo->sid);
+ unbecome_root();
+
+ if (!ret) {
+ TALLOC_FREE(pwd);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ rhost = tsocket_address_inet_addr_string(remote_address,
+ talloc_tos());
+ if (rhost == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* ================ BEGIN Privilege BLOCK ================ */
+
+ become_root();
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+
+ switch (r->in.level) {
+
+ case 2:
+ status = set_user_info_2(p->mem_ctx,
+ &info->info2, pwd);
+ break;
+
+ case 4:
+ status = set_user_info_4(p->mem_ctx,
+ &info->info4, pwd);
+ break;
+
+ case 6:
+ status = set_user_info_6(p->mem_ctx,
+ &info->info6, pwd);
+ break;
+
+ case 7:
+ status = set_user_info_7(p->mem_ctx,
+ &info->info7, pwd);
+ break;
+
+ case 8:
+ status = set_user_info_8(p->mem_ctx,
+ &info->info8, pwd);
+ break;
+
+ case 10:
+ status = set_user_info_10(p->mem_ctx,
+ &info->info10, pwd);
+ break;
+
+ case 11:
+ status = set_user_info_11(p->mem_ctx,
+ &info->info11, pwd);
+ break;
+
+ case 12:
+ status = set_user_info_12(p->mem_ctx,
+ &info->info12, pwd);
+ break;
+
+ case 13:
+ status = set_user_info_13(p->mem_ctx,
+ &info->info13, pwd);
+ break;
+
+ case 14:
+ status = set_user_info_14(p->mem_ctx,
+ &info->info14, pwd);
+ break;
+
+ case 16:
+ status = set_user_info_16(p->mem_ctx,
+ &info->info16, pwd);
+ break;
+
+ case 17:
+ status = set_user_info_17(p->mem_ctx,
+ &info->info17, pwd);
+ break;
+
+ case 18:
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ /* Used by AS/U JRA. */
+ status = set_user_info_18(&info->info18,
+ p->mem_ctx,
+ &session_key,
+ pwd);
+ break;
+
+ case 20:
+ status = set_user_info_20(p->mem_ctx,
+ &info->info20, pwd);
+ break;
+
+ case 21:
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ status = set_user_info_21(&info->info21,
+ p->mem_ctx,
+ &session_key,
+ pwd);
+ break;
+
+ case 23:
+ encrypted =
+ dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ status = NT_STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ /*
+ * This can be allowed as it requires a session key
+ * which we only have if we have a SMB session.
+ */
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ status = arc4_decrypt_data(session_key,
+ info->info23.password.data,
+ 516);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, info->info23.password.data, 516);
+#endif
+
+ status = set_user_info_23(p->mem_ctx,
+ &info->info23,
+ rhost,
+ pwd);
+ break;
+
+ case 24:
+ encrypted =
+ dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ status = NT_STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ /*
+ * This can be allowed as it requires a session key
+ * which we only have if we have a SMB session.
+ */
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ status = arc4_decrypt_data(session_key,
+ info->info24.password.data,
+ 516);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, info->info24.password.data, 516);
+#endif
+
+ status = set_user_info_24(p->mem_ctx,
+ rhost,
+ &info->info24, pwd);
+ break;
+
+ case 25:
+ encrypted =
+ dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ status = NT_STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ /*
+ * This can be allowed as it requires a session key
+ * which we only have if we have a SMB session.
+ */
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ status = decode_rc4_passwd_buffer(&session_key,
+ &info->info25.password);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, info->info25.password.data, 532);
+#endif
+
+ status = set_user_info_25(p->mem_ctx,
+ rhost,
+ &info->info25, pwd);
+ break;
+
+ case 26:
+ encrypted =
+ dcerpc_is_transport_encrypted(session_info);
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED &&
+ !encrypted) {
+ status = NT_STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ status = session_extract_session_key(
+ session_info, &session_key, KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ /*
+ * This can be allowed as it requires a session key
+ * which we only have if we have a SMB session.
+ */
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ status = decode_rc4_passwd_buffer(&session_key,
+ &info->info26.password);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, info->info26.password.data, 516);
+#endif
+
+ status = set_user_info_26(p->mem_ctx,
+ rhost,
+ &info->info26, pwd);
+ break;
+ case 31: {
+ DATA_BLOB new_password = data_blob_null;
+ const DATA_BLOB ciphertext = data_blob_const(
+ info->info31.password.cipher,
+ info->info31.password.cipher_len);
+ DATA_BLOB iv = data_blob_const(
+ info->info31.password.salt,
+ sizeof(info->info31.password.salt));
+
+ status = session_extract_session_key(session_info,
+ &session_key,
+ KEY_USE_16BYTES);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ status =
+ samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(
+ p->mem_ctx,
+ &ciphertext,
+ &session_key,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ &iv,
+ info->info31.password.auth_data,
+ &new_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("samba_gnutls_aead_aes_256_cbc_hmac_"
+ "sha512_decrypt "
+ "failed with %s\n",
+ nt_errstr(status));
+ status = NT_STATUS_WRONG_PASSWORD;
+ break;
+ }
+
+ status = set_user_info_31(p->mem_ctx,
+ rhost,
+ &new_password,
+ info->info31.password_expired,
+ pwd);
+ data_blob_clear(&new_password);
+
+ break;
+ }
+ case 32: {
+ DATA_BLOB new_password = data_blob_null;
+ const DATA_BLOB ciphertext = data_blob_const(
+ info->info32.password.cipher,
+ info->info32.password.cipher_len);
+ DATA_BLOB iv = data_blob_const(
+ info->info32.password.salt,
+ sizeof(info->info32.password.salt));
+
+ status = session_extract_session_key(session_info,
+ &session_key,
+ KEY_USE_16BYTES);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ status =
+ samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(
+ p->mem_ctx,
+ &ciphertext,
+ &session_key,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ &iv,
+ info->info32.password.auth_data,
+ &new_password);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ break;
+ }
+
+ status = set_user_info_32(p->mem_ctx,
+ rhost,
+ &new_password,
+ &info->info32,
+ pwd);
+ data_blob_clear_free(&new_password);
+
+ break;
+ }
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ TALLOC_FREE(pwd);
+
+ unbecome_root();
+
+ /* ================ END Privilege BLOCK ================ */
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(&uinfo->sid);
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_SetUserInfo2
+ ********************************************************************/
+
+NTSTATUS _samr_SetUserInfo2(struct pipes_struct *p,
+ struct samr_SetUserInfo2 *r)
+{
+ struct samr_SetUserInfo q;
+
+ q.in.user_handle = r->in.user_handle;
+ q.in.level = r->in.level;
+ q.in.info = r->in.info;
+
+ return _samr_SetUserInfo(p, &q);
+}
+
+/*********************************************************************
+ _samr_GetAliasMembership
+*********************************************************************/
+
+NTSTATUS _samr_GetAliasMembership(struct pipes_struct *p,
+ struct samr_GetAliasMembership *r)
+{
+ size_t num_alias_rids;
+ uint32_t *alias_rids;
+ struct samr_info *dinfo;
+ size_t i;
+
+ NTSTATUS status;
+
+ struct dom_sid *members;
+
+ DEBUG(5,("_samr_GetAliasMembership: %d\n", __LINE__));
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS
+ | SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_check_is_our_sam(&dinfo->sid) &&
+ !sid_check_is_builtin(&dinfo->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ if (r->in.sids->num_sids) {
+ members = talloc_array(p->mem_ctx, struct dom_sid, r->in.sids->num_sids);
+
+ if (members == NULL)
+ return NT_STATUS_NO_MEMORY;
+ } else {
+ members = NULL;
+ }
+
+ for (i=0; i<r->in.sids->num_sids; i++)
+ sid_copy(&members[i], r->in.sids->sids[i].sid);
+
+ alias_rids = NULL;
+ num_alias_rids = 0;
+
+ become_root();
+ status = pdb_enum_alias_memberships(p->mem_ctx, &dinfo->sid, members,
+ r->in.sids->num_sids,
+ &alias_rids, &num_alias_rids);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.rids->count = num_alias_rids;
+ r->out.rids->ids = alias_rids;
+
+ if (r->out.rids->ids == NULL) {
+ /* Windows domain clients don't accept a NULL ptr here */
+ r->out.rids->ids = talloc_zero(p->mem_ctx, uint32_t);
+ }
+ if (r->out.rids->ids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_GetMembersInAlias
+*********************************************************************/
+
+NTSTATUS _samr_GetMembersInAlias(struct pipes_struct *p,
+ struct samr_GetMembersInAlias *r)
+{
+ struct samr_info *ainfo;
+ NTSTATUS status;
+ size_t i;
+ size_t num_sids = 0;
+ struct lsa_SidPtr *sids = NULL;
+ struct dom_sid *pdb_sids = NULL;
+ struct dom_sid_buf buf;
+
+ ainfo = samr_policy_handle_find(p,
+ r->in.alias_handle,
+ SAMR_HANDLE_ALIAS,
+ SAMR_ALIAS_ACCESS_GET_MEMBERS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", dom_sid_str_buf(&ainfo->sid, &buf)));
+
+ become_root();
+ status = pdb_enum_aliasmem(&ainfo->sid, talloc_tos(), &pdb_sids,
+ &num_sids);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (num_sids) {
+ sids = talloc_zero_array(p->mem_ctx, struct lsa_SidPtr, num_sids);
+ if (sids == NULL) {
+ TALLOC_FREE(pdb_sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ sids[i].sid = dom_sid_dup(p->mem_ctx, &pdb_sids[i]);
+ if (!sids[i].sid) {
+ TALLOC_FREE(pdb_sids);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ r->out.sids->num_sids = num_sids;
+ r->out.sids->sids = sids;
+
+ TALLOC_FREE(pdb_sids);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_QueryGroupMember
+*********************************************************************/
+
+NTSTATUS _samr_QueryGroupMember(struct pipes_struct *p,
+ struct samr_QueryGroupMember *r)
+{
+ struct samr_info *ginfo;
+ size_t i, num_members;
+
+ uint32_t *rid=NULL;
+ uint32_t *attr=NULL;
+
+ NTSTATUS status;
+ struct samr_RidAttrArray *rids = NULL;
+ struct dom_sid_buf buf;
+
+ ginfo = samr_policy_handle_find(p,
+ r->in.group_handle,
+ SAMR_HANDLE_GROUP,
+ SAMR_GROUP_ACCESS_GET_MEMBERS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rids = talloc_zero(p->mem_ctx, struct samr_RidAttrArray);
+ if (!rids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10, ("sid is %s\n", dom_sid_str_buf(&ginfo->sid, &buf)));
+
+ if (!sid_check_is_in_our_sam(&ginfo->sid)) {
+ DEBUG(3, ("sid %s is not in our domain\n",
+ dom_sid_str_buf(&ginfo->sid, &buf)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ DEBUG(10, ("lookup on Domain SID\n"));
+
+ become_root();
+ status = pdb_enum_group_members(p->mem_ctx, &ginfo->sid,
+ &rid, &num_members);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (num_members) {
+ attr=talloc_zero_array(p->mem_ctx, uint32_t, num_members);
+ if (attr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ attr = NULL;
+ }
+
+ for (i=0; i<num_members; i++) {
+ attr[i] = SE_GROUP_DEFAULT_FLAGS;
+ }
+
+ rids->count = num_members;
+ rids->attributes = attr;
+ rids->rids = rid;
+
+ *r->out.rids = rids;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_AddAliasMember
+*********************************************************************/
+
+NTSTATUS _samr_AddAliasMember(struct pipes_struct *p,
+ struct samr_AddAliasMember *r)
+{
+ struct samr_info *ainfo;
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+
+ ainfo = samr_policy_handle_find(p,
+ r->in.alias_handle,
+ SAMR_HANDLE_ALIAS,
+ SAMR_ALIAS_ACCESS_ADD_MEMBER,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", dom_sid_str_buf(&ainfo->sid, &buf)));
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_add_aliasmem(&ainfo->sid, r->in.sid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(&ainfo->sid);
+ }
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_DeleteAliasMember
+*********************************************************************/
+
+NTSTATUS _samr_DeleteAliasMember(struct pipes_struct *p,
+ struct samr_DeleteAliasMember *r)
+{
+ struct samr_info *ainfo;
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+
+ ainfo = samr_policy_handle_find(p,
+ r->in.alias_handle,
+ SAMR_HANDLE_ALIAS,
+ SAMR_ALIAS_ACCESS_REMOVE_MEMBER,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("_samr_del_aliasmem:sid is %s\n",
+ dom_sid_str_buf(&ainfo->sid, &buf)));
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_del_aliasmem(&ainfo->sid, r->in.sid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(&ainfo->sid);
+ }
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_AddGroupMember
+*********************************************************************/
+
+NTSTATUS _samr_AddGroupMember(struct pipes_struct *p,
+ struct samr_AddGroupMember *r)
+{
+ struct samr_info *ginfo;
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+ uint32_t group_rid;
+
+ ginfo = samr_policy_handle_find(p,
+ r->in.group_handle,
+ SAMR_HANDLE_GROUP,
+ SAMR_GROUP_ACCESS_ADD_MEMBER,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", dom_sid_str_buf(&ginfo->sid, &buf)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &ginfo->sid,
+ &group_rid)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_add_groupmem(p->mem_ctx, group_rid, r->in.rid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ force_flush_samr_cache(&ginfo->sid);
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_DeleteGroupMember
+*********************************************************************/
+
+NTSTATUS _samr_DeleteGroupMember(struct pipes_struct *p,
+ struct samr_DeleteGroupMember *r)
+
+{
+ struct samr_info *ginfo;
+ NTSTATUS status;
+ uint32_t group_rid;
+
+ /*
+ * delete the group member named r->in.rid
+ * who is a member of the sid associated with the handle
+ * the rid is a user's rid as the group is a domain group.
+ */
+
+ ginfo = samr_policy_handle_find(p,
+ r->in.group_handle,
+ SAMR_HANDLE_GROUP,
+ SAMR_GROUP_ACCESS_REMOVE_MEMBER,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &ginfo->sid,
+ &group_rid)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_del_groupmem(p->mem_ctx, group_rid, r->in.rid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ force_flush_samr_cache(&ginfo->sid);
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_DeleteUser
+*********************************************************************/
+
+NTSTATUS _samr_DeleteUser(struct pipes_struct *p,
+ struct samr_DeleteUser *r)
+{
+ struct samr_info *uinfo;
+ NTSTATUS status;
+ struct samu *sam_pass=NULL;
+ bool ret;
+
+ DEBUG(5, ("_samr_DeleteUser: %d\n", __LINE__));
+
+ uinfo = samr_policy_handle_find(p,
+ r->in.user_handle,
+ SAMR_HANDLE_USER,
+ SEC_STD_DELETE,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_check_is_in_our_sam(&uinfo->sid))
+ return NT_STATUS_CANNOT_DELETE;
+
+ /* check if the user exists before trying to delete */
+ if ( !(sam_pass = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(sam_pass, &uinfo->sid);
+ unbecome_root();
+
+ if(!ret) {
+ struct dom_sid_buf buf;
+ DEBUG(5,("_samr_DeleteUser: User %s doesn't exist.\n",
+ dom_sid_str_buf(&uinfo->sid, &buf)));
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_delete_user(p->mem_ctx, sam_pass);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ DEBUG(5,("_samr_DeleteUser: Failed to delete entry for "
+ "user %s: %s.\n", pdb_get_username(sam_pass),
+ nt_errstr(status)));
+ TALLOC_FREE(sam_pass);
+ return status;
+ }
+
+
+ TALLOC_FREE(sam_pass);
+
+ force_flush_samr_cache(&uinfo->sid);
+
+ if (!close_policy_hnd(p, r->in.user_handle))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ ZERO_STRUCTP(r->out.user_handle);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_DeleteDomainGroup
+*********************************************************************/
+
+NTSTATUS _samr_DeleteDomainGroup(struct pipes_struct *p,
+ struct samr_DeleteDomainGroup *r)
+{
+ struct samr_info *ginfo;
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+ uint32_t group_rid;
+
+ DEBUG(5, ("samr_DeleteDomainGroup: %d\n", __LINE__));
+
+ ginfo = samr_policy_handle_find(p,
+ r->in.group_handle,
+ SAMR_HANDLE_GROUP,
+ SEC_STD_DELETE,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", dom_sid_str_buf(&ginfo->sid, &buf)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &ginfo->sid,
+ &group_rid)) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_delete_dom_group(p->mem_ctx, group_rid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ DEBUG(5,("_samr_DeleteDomainGroup: Failed to delete mapping "
+ "entry for group %s: %s\n",
+ dom_sid_str_buf(&ginfo->sid, &buf),
+ nt_errstr(status)));
+ return status;
+ }
+
+ force_flush_samr_cache(&ginfo->sid);
+
+ if (!close_policy_hnd(p, r->in.group_handle))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_DeleteDomAlias
+*********************************************************************/
+
+NTSTATUS _samr_DeleteDomAlias(struct pipes_struct *p,
+ struct samr_DeleteDomAlias *r)
+{
+ struct samr_info *ainfo;
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+
+ DEBUG(5, ("_samr_DeleteDomAlias: %d\n", __LINE__));
+
+ ainfo = samr_policy_handle_find(p,
+ r->in.alias_handle,
+ SAMR_HANDLE_ALIAS,
+ SEC_STD_DELETE,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", dom_sid_str_buf(&ainfo->sid, &buf)));
+
+ /* Don't let Windows delete builtin groups */
+
+ if ( sid_check_is_in_builtin( &ainfo->sid ) ) {
+ return NT_STATUS_SPECIAL_ACCOUNT;
+ }
+
+ if (!sid_check_is_in_our_sam(&ainfo->sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ DEBUG(10, ("lookup on Local SID\n"));
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ /* Have passdb delete the alias */
+ status = pdb_delete_alias(&ainfo->sid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if ( !NT_STATUS_IS_OK(status))
+ return status;
+
+ force_flush_samr_cache(&ainfo->sid);
+
+ if (!close_policy_hnd(p, r->in.alias_handle))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_CreateDomainGroup
+*********************************************************************/
+
+NTSTATUS _samr_CreateDomainGroup(struct pipes_struct *p,
+ struct samr_CreateDomainGroup *r)
+
+{
+ NTSTATUS status;
+ const char *name;
+ struct samr_info *dinfo;
+ struct dom_sid sid;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_CREATE_GROUP,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_check_is_our_sam(&dinfo->sid)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ name = r->in.name->string;
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = can_create(p->mem_ctx, name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ /* check that we successfully create the UNIX group */
+ status = pdb_create_dom_group(p->mem_ctx, name, r->out.rid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ /* check if we should bail out here */
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ sid_compose(&sid, &dinfo->sid, *r->out.rid);
+
+ status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_GROUP,
+ GENERIC_RIGHTS_GROUP_ALL_ACCESS,
+ &sid,
+ NULL,
+ r->out.group_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ force_flush_samr_cache(&dinfo->sid);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_CreateDomAlias
+*********************************************************************/
+
+NTSTATUS _samr_CreateDomAlias(struct pipes_struct *p,
+ struct samr_CreateDomAlias *r)
+{
+ struct dom_sid info_sid;
+ const char *name = NULL;
+ struct samr_info *dinfo;
+ gid_t gid;
+ NTSTATUS result;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_CREATE_ALIAS,
+ NULL,
+ &result);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (!sid_check_is_our_sam(&dinfo->sid)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ name = r->in.alias_name->string;
+
+ result = can_create(p->mem_ctx, name);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ /* Have passdb create the alias */
+ result = pdb_create_alias(name, r->out.rid);
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("pdb_create_alias failed: %s\n",
+ nt_errstr(result)));
+ return result;
+ }
+
+ sid_compose(&info_sid, &dinfo->sid, *r->out.rid);
+
+ if (!sid_to_gid(&info_sid, &gid)) {
+ DEBUG(10, ("Could not find alias just created\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* check if the group has been successfully created */
+ if ( getgrgid(gid) == NULL ) {
+ DEBUG(1, ("getgrgid(%u) of just created alias failed\n",
+ (unsigned int)gid));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ result = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_ALIAS,
+ GENERIC_RIGHTS_ALIAS_ALL_ACCESS,
+ &info_sid,
+ NULL,
+ r->out.alias_handle);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ force_flush_samr_cache(&info_sid);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_QueryGroupInfo
+*********************************************************************/
+
+NTSTATUS _samr_QueryGroupInfo(struct pipes_struct *p,
+ struct samr_QueryGroupInfo *r)
+{
+ struct samr_info *ginfo;
+ NTSTATUS status;
+ GROUP_MAP *map;
+ union samr_GroupInfo *info = NULL;
+ bool ret;
+ uint32_t attributes = SE_GROUP_DEFAULT_FLAGS;
+ const char *group_name = NULL;
+ const char *group_description = NULL;
+
+ ginfo = samr_policy_handle_find(p,
+ r->in.group_handle,
+ SAMR_HANDLE_GROUP,
+ SAMR_GROUP_ACCESS_LOOKUP_INFO,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ map = talloc_zero(p->mem_ctx, GROUP_MAP);
+ if (!map) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = get_domain_group_from_sid(ginfo->sid, map);
+ unbecome_root();
+ if (!ret)
+ return NT_STATUS_INVALID_HANDLE;
+
+ group_name = talloc_move(r, &map->nt_name);
+ group_description = talloc_move(r, &map->comment);
+
+ TALLOC_FREE(map);
+
+ info = talloc_zero(p->mem_ctx, union samr_GroupInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1: {
+ uint32_t *members;
+ size_t num_members;
+
+ become_root();
+ status = pdb_enum_group_members(
+ p->mem_ctx, &ginfo->sid, &members,
+ &num_members);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info->all.name.string = group_name;
+ info->all.attributes = attributes;
+ info->all.num_members = num_members;
+ info->all.description.string = group_description;
+ break;
+ }
+ case 2:
+ info->name.string = group_name;
+ break;
+ case 3:
+ info->attributes.attributes = attributes;
+ break;
+ case 4:
+ info->description.string = group_description;
+ break;
+ case 5: {
+ /*
+ uint32_t *members;
+ size_t num_members;
+ */
+
+ /*
+ become_root();
+ status = pdb_enum_group_members(
+ p->mem_ctx, &ginfo->sid, &members,
+ &num_members);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ */
+ info->all2.name.string = group_name;
+ info->all2.attributes = attributes;
+ info->all2.num_members = 0; /* num_members - in w2k3 this is always 0 */
+ info->all2.description.string = group_description;
+
+ break;
+ }
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_SetGroupInfo
+*********************************************************************/
+
+NTSTATUS _samr_SetGroupInfo(struct pipes_struct *p,
+ struct samr_SetGroupInfo *r)
+{
+ struct samr_info *ginfo;
+ GROUP_MAP *map;
+ NTSTATUS status;
+ bool ret;
+
+ ginfo = samr_policy_handle_find(p,
+ r->in.group_handle,
+ SAMR_HANDLE_GROUP,
+ SAMR_GROUP_ACCESS_SET_INFO,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ map = talloc_zero(p->mem_ctx, GROUP_MAP);
+ if (!map) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = get_domain_group_from_sid(ginfo->sid, map);
+ unbecome_root();
+ if (!ret)
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ switch (r->in.level) {
+ case 2:
+ map->nt_name = talloc_strdup(map,
+ r->in.info->name.string);
+ if (!map->nt_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ case 3:
+ break;
+ case 4:
+ map->comment = talloc_strdup(map,
+ r->in.info->description.string);
+ if (!map->comment) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_update_group_mapping_entry(map);
+ unbecome_root();
+
+ /******** End SeAddUsers BLOCK *********/
+
+ TALLOC_FREE(map);
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(&ginfo->sid);
+ }
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_SetAliasInfo
+*********************************************************************/
+
+NTSTATUS _samr_SetAliasInfo(struct pipes_struct *p,
+ struct samr_SetAliasInfo *r)
+{
+ struct samr_info *ainfo;
+ struct acct_info *info;
+ NTSTATUS status;
+
+ ainfo = samr_policy_handle_find(p,
+ r->in.alias_handle,
+ SAMR_HANDLE_ALIAS,
+ SAMR_ALIAS_ACCESS_SET_INFO,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info = talloc_zero(p->mem_ctx, struct acct_info);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* get the current group information */
+
+ become_root();
+ status = pdb_get_aliasinfo(&ainfo->sid, info);
+ unbecome_root();
+
+ if ( !NT_STATUS_IS_OK(status))
+ return status;
+
+ switch (r->in.level) {
+ case ALIASINFONAME:
+ {
+ char *group_name;
+
+ /* We currently do not support renaming groups in the
+ the BUILTIN domain. Refer to util_builtin.c to understand
+ why. The eventually needs to be fixed to be like Windows
+ where you can rename builtin groups, just not delete them */
+
+ if ( sid_check_is_in_builtin( &ainfo->sid ) ) {
+ return NT_STATUS_SPECIAL_ACCOUNT;
+ }
+
+ /* There has to be a valid name (and it has to be different) */
+
+ if ( !r->in.info->name.string )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ /* If the name is the same just reply "ok". Yes this
+ doesn't allow you to change the case of a group name. */
+
+ if (strequal(r->in.info->name.string, info->acct_name)) {
+ return NT_STATUS_OK;
+ }
+
+ talloc_free(info->acct_name);
+ info->acct_name = talloc_strdup(info, r->in.info->name.string);
+ if (!info->acct_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the name doesn't already exist as a user
+ or local group */
+
+ group_name = talloc_asprintf(p->mem_ctx,
+ "%s\\%s",
+ lp_netbios_name(),
+ info->acct_name);
+ if (group_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = can_create( p->mem_ctx, group_name );
+ talloc_free(group_name);
+ if ( !NT_STATUS_IS_OK( status ) )
+ return status;
+ break;
+ }
+ case ALIASINFODESCRIPTION:
+ TALLOC_FREE(info->acct_desc);
+ if (r->in.info->description.string) {
+ info->acct_desc = talloc_strdup(info,
+ r->in.info->description.string);
+ } else {
+ info->acct_desc = talloc_strdup(info, "");
+ }
+ if (!info->acct_desc) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ become_root();
+ status = pdb_set_aliasinfo(&ainfo->sid, info);
+ unbecome_root();
+
+ /******** End SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status))
+ force_flush_samr_cache(&ainfo->sid);
+
+ return status;
+}
+
+/****************************************************************
+ _samr_GetDomPwInfo
+****************************************************************/
+
+NTSTATUS _samr_GetDomPwInfo(struct pipes_struct *p,
+ struct samr_GetDomPwInfo *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t min_password_length = 0;
+ uint32_t password_properties = 0;
+
+ /* Perform access check. Since this rpc does not require a
+ policy handle it will not be caught by the access checks on
+ SAMR_CONNECT or SAMR_CONNECT_ANON. */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _samr_GetDomPwInfo\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ become_root();
+ pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN,
+ &min_password_length);
+ pdb_get_account_policy(PDB_POLICY_USER_MUST_LOGON_TO_CHG_PASS,
+ &password_properties);
+ unbecome_root();
+
+ if (lp_check_password_script(talloc_tos(), lp_sub) && *lp_check_password_script(talloc_tos(), lp_sub)) {
+ password_properties |= DOMAIN_PASSWORD_COMPLEX;
+ }
+
+ r->out.info->min_password_length = min_password_length;
+ r->out.info->password_properties = password_properties;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_OpenGroup
+*********************************************************************/
+
+NTSTATUS _samr_OpenGroup(struct pipes_struct *p,
+ struct samr_OpenGroup *r)
+
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dom_sid info_sid;
+ struct dom_sid_buf buf;
+ GROUP_MAP *map;
+ struct samr_info *dinfo;
+ struct security_descriptor *psd = NULL;
+ uint32_t acc_granted;
+ uint32_t des_access = r->in.access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+ bool ret;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*check if access can be granted as requested by client. */
+ map_max_allowed_access(session_info->security_token,
+ session_info->unix_token,
+ &des_access);
+
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &grp_generic_mapping, NULL, 0);
+ se_map_generic(&des_access,&grp_generic_mapping);
+
+ status = access_check_object(psd, session_info->security_token,
+ SEC_PRIV_ADD_USERS, SEC_PRIV_INVALID, GENERIC_RIGHTS_GROUP_ALL_ACCESS,
+ des_access, &acc_granted, "_samr_OpenGroup");
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ /* this should not be hard-coded like this */
+
+ if (!sid_check_is_our_sam(&dinfo->sid)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sid_compose(&info_sid, &dinfo->sid, r->in.rid);
+
+ DEBUG(10, ("_samr_OpenGroup:Opening SID: %s\n",
+ dom_sid_str_buf(&info_sid, &buf)));
+
+ map = talloc_zero(p->mem_ctx, GROUP_MAP);
+ if (!map) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* check if that group really exists */
+ become_root();
+ ret = get_domain_group_from_sid(info_sid, map);
+ unbecome_root();
+ if (!ret)
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ TALLOC_FREE(map);
+
+ status = create_samr_policy_handle(p->mem_ctx,
+ p,
+ SAMR_HANDLE_GROUP,
+ acc_granted,
+ &info_sid,
+ NULL,
+ r->out.group_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_RemoveMemberFromForeignDomain
+*********************************************************************/
+
+NTSTATUS _samr_RemoveMemberFromForeignDomain(struct pipes_struct *p,
+ struct samr_RemoveMemberFromForeignDomain *r)
+{
+ struct samr_info *dinfo;
+ struct dom_sid_buf buf;
+ NTSTATUS result;
+
+ DEBUG(5,("_samr_RemoveMemberFromForeignDomain: removing SID [%s]\n",
+ dom_sid_str_buf(r->in.sid, &buf)));
+
+ /* Find the policy handle. Open a policy on it. */
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_OPEN_ACCOUNT,
+ NULL,
+ &result);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ DEBUG(8, ("_samr_RemoveMemberFromForeignDomain: sid is %s\n",
+ dom_sid_str_buf(&dinfo->sid, &buf)));
+
+ /* we can only delete a user from a group since we don't have
+ nested groups anyways. So in the latter case, just say OK */
+
+ /* TODO: The above comment nowadays is bogus. Since we have nested
+ * groups now, and aliases members are never reported out of the unix
+ * group membership, the "just say OK" makes this call a no-op. For
+ * us. This needs fixing however. */
+
+ /* I've only ever seen this in the wild when deleting a user from
+ * usrmgr.exe. domain_sid is the builtin domain, and the sid to delete
+ * is the user about to be deleted. I very much suspect this is the
+ * only application of this call. To verify this, let people report
+ * other cases. */
+
+ if (!sid_check_is_builtin(&dinfo->sid)) {
+ struct dom_sid_buf buf2;
+ DBG_WARNING("domain_sid = %s, "
+ "global_sam_sid() = %s\n"
+ "please report to "
+ "samba-technical@lists.samba.org!\n",
+ dom_sid_str_buf(&dinfo->sid, &buf),
+ dom_sid_str_buf(get_global_sam_sid(), &buf2));
+ return NT_STATUS_OK;
+ }
+
+ force_flush_samr_cache(&dinfo->sid);
+
+ result = NT_STATUS_OK;
+
+ return result;
+}
+
+/*******************************************************************
+ _samr_QueryDomainInfo2
+ ********************************************************************/
+
+NTSTATUS _samr_QueryDomainInfo2(struct pipes_struct *p,
+ struct samr_QueryDomainInfo2 *r)
+{
+ struct samr_QueryDomainInfo q;
+
+ q.in.domain_handle = r->in.domain_handle;
+ q.in.level = r->in.level;
+
+ q.out.info = r->out.info;
+
+ return _samr_QueryDomainInfo(p, &q);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS set_dom_info_1(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *r)
+{
+ time_t u_expire, u_min_age;
+
+ u_expire = nt_time_to_unix_abs((NTTIME *)&r->max_password_age);
+ u_min_age = nt_time_to_unix_abs((NTTIME *)&r->min_password_age);
+
+ pdb_set_account_policy(PDB_POLICY_MIN_PASSWORD_LEN,
+ (uint32_t)r->min_password_length);
+ pdb_set_account_policy(PDB_POLICY_PASSWORD_HISTORY,
+ (uint32_t)r->password_history_length);
+ pdb_set_account_policy(PDB_POLICY_USER_MUST_LOGON_TO_CHG_PASS,
+ (uint32_t)r->password_properties);
+ pdb_set_account_policy(PDB_POLICY_MAX_PASSWORD_AGE, (int)u_expire);
+ pdb_set_account_policy(PDB_POLICY_MIN_PASSWORD_AGE, (int)u_min_age);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS set_dom_info_3(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo3 *r)
+{
+ time_t u_logout;
+
+ u_logout = nt_time_to_unix_abs((NTTIME *)&r->force_logoff_time);
+
+ pdb_set_account_policy(PDB_POLICY_TIME_TO_LOGOUT, (int)u_logout);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static NTSTATUS set_dom_info_12(TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *r)
+{
+ time_t u_lock_duration, u_reset_time;
+
+ /*
+ * It is not possible to set lockout_duration < lockout_window.
+ * (The test is the other way around since the negative numbers
+ * are stored...)
+ *
+ * This constraint is documented here for the samr rpc service:
+ * MS-SAMR 3.1.1.6 Attribute Constraints for Originating Updates
+ * http://msdn.microsoft.com/en-us/library/cc245667%28PROT.10%29.aspx
+ *
+ * And here for the ldap backend:
+ * MS-ADTS 3.1.1.5.3.2 Constraints
+ * http://msdn.microsoft.com/en-us/library/cc223462(PROT.10).aspx
+ */
+ if (r->lockout_duration > r->lockout_window) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ u_lock_duration = nt_time_to_unix_abs((NTTIME *)&r->lockout_duration);
+ if (u_lock_duration != -1) {
+ u_lock_duration /= 60;
+ }
+
+ u_reset_time = nt_time_to_unix_abs((NTTIME *)&r->lockout_window)/60;
+
+ pdb_set_account_policy(PDB_POLICY_LOCK_ACCOUNT_DURATION, (int)u_lock_duration);
+ pdb_set_account_policy(PDB_POLICY_RESET_COUNT_TIME, (int)u_reset_time);
+ pdb_set_account_policy(PDB_POLICY_BAD_ATTEMPT_LOCKOUT,
+ (uint32_t)r->lockout_threshold);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_SetDomainInfo
+ ********************************************************************/
+
+NTSTATUS _samr_SetDomainInfo(struct pipes_struct *p,
+ struct samr_SetDomainInfo *r)
+{
+ NTSTATUS status;
+ uint32_t acc_required = 0;
+
+ DEBUG(5,("_samr_SetDomainInfo: %d\n", __LINE__));
+
+ switch (r->in.level) {
+ case 1: /* DomainPasswordInformation */
+ case 12: /* DomainLockoutInformation */
+ /* DOMAIN_WRITE_PASSWORD_PARAMETERS */
+ acc_required = SAMR_DOMAIN_ACCESS_SET_INFO_1;
+ break;
+ case 3: /* DomainLogoffInformation */
+ case 4: /* DomainOemInformation */
+ /* DOMAIN_WRITE_OTHER_PARAMETERS */
+ acc_required = SAMR_DOMAIN_ACCESS_SET_INFO_2;
+ break;
+ case 6: /* DomainReplicationInformation */
+ case 9: /* DomainStateInformation */
+ case 7: /* DomainServerRoleInformation */
+ /* DOMAIN_ADMINISTER_SERVER */
+ acc_required = SAMR_DOMAIN_ACCESS_SET_INFO_3;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ (void)samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ acc_required,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_SetDomainInfo: level: %d\n", r->in.level));
+
+ switch (r->in.level) {
+ case 1:
+ status = set_dom_info_1(p->mem_ctx, &r->in.info->info1);
+ break;
+ case 3:
+ status = set_dom_info_3(p->mem_ctx, &r->in.info->info3);
+ break;
+ case 4:
+ break;
+ case 6:
+ break;
+ case 7:
+ break;
+ case 9:
+ break;
+ case 12:
+ status = set_dom_info_12(p->mem_ctx, &r->in.info->info12);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_SetDomainInfo: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+ _samr_GetDisplayEnumerationIndex
+****************************************************************/
+
+NTSTATUS _samr_GetDisplayEnumerationIndex(struct pipes_struct *p,
+ struct samr_GetDisplayEnumerationIndex *r)
+{
+ struct samr_info *dinfo;
+ uint32_t max_entries = (uint32_t) -1;
+ uint32_t enum_context = 0;
+ uint32_t i, num_account = 0;
+ struct samr_displayentry *entries = NULL;
+ NTSTATUS status;
+
+ DEBUG(5,("_samr_GetDisplayEnumerationIndex: %d\n", __LINE__));
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ((r->in.level < 1) || (r->in.level > 3)) {
+ DEBUG(0,("_samr_GetDisplayEnumerationIndex: "
+ "Unknown info level (%u)\n",
+ r->in.level));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ become_root();
+
+ /* The following done as ROOT. Don't return without unbecome_root(). */
+
+ switch (r->in.level) {
+ case 1:
+ if (dinfo->disp_info->users == NULL) {
+ dinfo->disp_info->users = pdb_search_users(
+ dinfo->disp_info, ACB_NORMAL);
+ if (dinfo->disp_info->users == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "starting user enumeration at index %u\n",
+ (unsigned int)enum_context));
+ } else {
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "using cached user enumeration at index %u\n",
+ (unsigned int)enum_context));
+ }
+ num_account = pdb_search_entries(dinfo->disp_info->users,
+ enum_context, max_entries,
+ &entries);
+ break;
+ case 2:
+ if (dinfo->disp_info->machines == NULL) {
+ dinfo->disp_info->machines = pdb_search_users(
+ dinfo->disp_info, ACB_WSTRUST|ACB_SVRTRUST);
+ if (dinfo->disp_info->machines == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "starting machine enumeration at index %u\n",
+ (unsigned int)enum_context));
+ } else {
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "using cached machine enumeration at index %u\n",
+ (unsigned int)enum_context));
+ }
+ num_account = pdb_search_entries(dinfo->disp_info->machines,
+ enum_context, max_entries,
+ &entries);
+ break;
+ case 3:
+ if (dinfo->disp_info->groups == NULL) {
+ dinfo->disp_info->groups = pdb_search_groups(
+ dinfo->disp_info);
+ if (dinfo->disp_info->groups == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "starting group enumeration at index %u\n",
+ (unsigned int)enum_context));
+ } else {
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "using cached group enumeration at index %u\n",
+ (unsigned int)enum_context));
+ }
+ num_account = pdb_search_entries(dinfo->disp_info->groups,
+ enum_context, max_entries,
+ &entries);
+ break;
+ default:
+ unbecome_root();
+ smb_panic("info class changed");
+ break;
+ }
+
+ unbecome_root();
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(dinfo->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: looking for :%s\n",
+ r->in.name->string));
+
+ for (i=0; i<num_account; i++) {
+ if (strequal(entries[i].account_name, r->in.name->string)) {
+ DEBUG(10,("_samr_GetDisplayEnumerationIndex: "
+ "found %s at idx %d\n",
+ r->in.name->string, i));
+ *r->out.idx = i;
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* assuming account_name lives at the very end */
+ *r->out.idx = num_account;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+}
+
+/****************************************************************
+ _samr_GetDisplayEnumerationIndex2
+****************************************************************/
+
+NTSTATUS _samr_GetDisplayEnumerationIndex2(struct pipes_struct *p,
+ struct samr_GetDisplayEnumerationIndex2 *r)
+{
+ struct samr_GetDisplayEnumerationIndex q;
+
+ q.in.domain_handle = r->in.domain_handle;
+ q.in.level = r->in.level;
+ q.in.name = r->in.name;
+
+ q.out.idx = r->out.idx;
+
+ return _samr_GetDisplayEnumerationIndex(p, &q);
+}
+
+/****************************************************************
+ _samr_RidToSid
+****************************************************************/
+
+NTSTATUS _samr_RidToSid(struct pipes_struct *p,
+ struct samr_RidToSid *r)
+{
+ struct samr_info *dinfo;
+ NTSTATUS status;
+ struct dom_sid sid;
+
+ dinfo = samr_policy_handle_find(p,
+ r->in.domain_handle,
+ SAMR_HANDLE_DOMAIN,
+ 0,
+ NULL,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_compose(&sid, &dinfo->sid, r->in.rid)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.sid = dom_sid_dup(p->mem_ctx, &sid);
+ if (!*r->out.sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static enum samr_ValidationStatus samr_ValidatePassword_Change(TALLOC_CTX *mem_ctx,
+ const struct samr_PwInfo *dom_pw_info,
+ const struct samr_ValidatePasswordReq2 *req,
+ struct samr_ValidatePasswordRepCtr *rep)
+{
+ NTSTATUS status;
+
+ if (req->password.string == NULL) {
+ return SAMR_VALIDATION_STATUS_SUCCESS;
+ }
+ if (strlen(req->password.string) < dom_pw_info->min_password_length) {
+ ZERO_STRUCT(rep->info);
+ return SAMR_VALIDATION_STATUS_PWD_TOO_SHORT;
+ }
+ if (dom_pw_info->password_properties & DOMAIN_PASSWORD_COMPLEX) {
+ status = check_password_complexity(req->account.string,
+ NULL, /* full_name */
+ req->password.string,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCT(rep->info);
+ return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
+ }
+ }
+
+ return SAMR_VALIDATION_STATUS_SUCCESS;
+}
+
+/****************************************************************
+****************************************************************/
+
+static enum samr_ValidationStatus samr_ValidatePassword_Reset(TALLOC_CTX *mem_ctx,
+ const struct samr_PwInfo *dom_pw_info,
+ const struct samr_ValidatePasswordReq3 *req,
+ struct samr_ValidatePasswordRepCtr *rep)
+{
+ NTSTATUS status;
+
+ if (req->password.string == NULL) {
+ return SAMR_VALIDATION_STATUS_SUCCESS;
+ }
+ if (strlen(req->password.string) < dom_pw_info->min_password_length) {
+ ZERO_STRUCT(rep->info);
+ return SAMR_VALIDATION_STATUS_PWD_TOO_SHORT;
+ }
+ if (dom_pw_info->password_properties & DOMAIN_PASSWORD_COMPLEX) {
+ status = check_password_complexity(req->account.string,
+ NULL, /* full_name */
+ req->password.string,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCT(rep->info);
+ return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
+ }
+ }
+
+ return SAMR_VALIDATION_STATUS_SUCCESS;
+}
+
+/****************************************************************
+ _samr_ValidatePassword
+****************************************************************/
+
+NTSTATUS _samr_ValidatePassword(struct pipes_struct *p,
+ struct samr_ValidatePassword *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+ union samr_ValidatePasswordRep *rep;
+ NTSTATUS status;
+ struct samr_GetDomPwInfo pw;
+ struct samr_PwInfo dom_pw_info;
+
+ if (p->transport != NCACN_IP_TCP && p->transport != NCALRPC) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ dcesrv_call_auth_info(dce_call, NULL, &auth_level);
+
+ if (auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (r->in.level < 1 || r->in.level > 3) {
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ pw.in.domain_name = NULL;
+ pw.out.info = &dom_pw_info;
+
+ status = _samr_GetDomPwInfo(p, &pw);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rep = talloc_zero(p->mem_ctx, union samr_ValidatePasswordRep);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ case 2:
+ rep->ctr2.status = samr_ValidatePassword_Change(p->mem_ctx,
+ &dom_pw_info,
+ &r->in.req->req2,
+ &rep->ctr2);
+ break;
+ case 3:
+ rep->ctr3.status = samr_ValidatePassword_Reset(p->mem_ctx,
+ &dom_pw_info,
+ &r->in.req->req3,
+ &rep->ctr3);
+ break;
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(rep);
+ return status;
+ }
+
+ *r->out.rep = rep;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_Shutdown(struct pipes_struct *p,
+ struct samr_Shutdown *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_SetMemberAttributesOfGroup(struct pipes_struct *p,
+ struct samr_SetMemberAttributesOfGroup *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_TestPrivateFunctionsDomain(struct pipes_struct *p,
+ struct samr_TestPrivateFunctionsDomain *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_TestPrivateFunctionsUser(struct pipes_struct *p,
+ struct samr_TestPrivateFunctionsUser *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_AddMultipleMembersToAlias(struct pipes_struct *p,
+ struct samr_AddMultipleMembersToAlias *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_RemoveMultipleMembersFromAlias(struct pipes_struct *p,
+ struct samr_RemoveMultipleMembersFromAlias *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_SetBootKeyInformation(struct pipes_struct *p,
+ struct samr_SetBootKeyInformation *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_GetBootKeyInformation(struct pipes_struct *p,
+ struct samr_GetBootKeyInformation *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_SetDsrmPassword(struct pipes_struct *p,
+ struct samr_SetDsrmPassword *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+void _samr_Opnum68NotUsedOnWire(struct pipes_struct *p,
+ struct samr_Opnum68NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _samr_Opnum69NotUsedOnWire(struct pipes_struct *p,
+ struct samr_Opnum69NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _samr_Opnum70NotUsedOnWire(struct pipes_struct *p,
+ struct samr_Opnum70NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _samr_Opnum71NotUsedOnWire(struct pipes_struct *p,
+ struct samr_Opnum71NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+void _samr_Opnum72NotUsedOnWire(struct pipes_struct *p,
+ struct samr_Opnum72NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+NTSTATUS _samr_ChangePasswordUser4(struct pipes_struct *p,
+ struct samr_ChangePasswordUser4 *r)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ char *rhost = NULL;
+ struct samu *sampass = NULL;
+ char *username = NULL;
+ uint32_t acct_ctrl = 0;
+ const uint8_t *nt_pw = NULL;
+ gnutls_datum_t nt_key;
+ gnutls_datum_t salt = {
+ .data = r->in.password->salt,
+ .size = sizeof(r->in.password->salt),
+ };
+ uint8_t cdk_data[16] = {0};
+ DATA_BLOB cdk = {
+ .data = cdk_data,
+ .length = sizeof(cdk_data),
+ };
+ char *new_passwd = NULL;
+ bool updated_badpw = false;
+ NTSTATUS update_login_attempts_status;
+ char *mutex_name_by_user = NULL;
+ struct named_mutex *mtx = NULL;
+ NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
+ bool ok;
+ int rc;
+
+ r->out.result = NT_STATUS_WRONG_PASSWORD;
+
+ DBG_NOTICE("_samr_ChangePasswordUser4\n");
+
+ if (r->in.account->string == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (r->in.password == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.password->PBKDF2Iterations < 5000 ||
+ r->in.password->PBKDF2Iterations > 1000000) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ (void)map_username(frame, r->in.account->string, &username);
+ if (username == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rhost = tsocket_address_inet_addr_string(remote_address, frame);
+ if (rhost == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sampass = samu_new(frame);
+ if (sampass == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ become_root();
+ ok = pdb_getsampwnam(sampass, username);
+ unbecome_root();
+ if (!ok) {
+ status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (acct_ctrl & ACB_AUTOLOCK) {
+ status = NT_STATUS_ACCOUNT_LOCKED_OUT;
+ goto done;
+ }
+
+ nt_pw = pdb_get_nt_passwd(sampass);
+ nt_key = (gnutls_datum_t){
+ .data = discard_const_p(uint8_t, nt_pw),
+ .size = NT_HASH_LEN,
+ };
+
+ rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+ &nt_key,
+ &salt,
+ r->in.password->PBKDF2Iterations,
+ cdk.data,
+ cdk.length);
+ if (rc < 0) {
+ BURN_DATA(cdk_data);
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto done;
+ }
+
+ status = samr_set_password_aes(frame,
+ &cdk,
+ r->in.password,
+ &new_passwd);
+ BURN_DATA(cdk_data);
+
+ /*
+ * We must re-load the sam account information under a mutex
+ * lock to ensure we don't miss any concurrent account lockout
+ * changes.
+ */
+
+ /* Clear out old sampass info. */
+ TALLOC_FREE(sampass);
+
+ sampass = samu_new(frame);
+ if (sampass == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ mutex_name_by_user = talloc_asprintf(frame,
+ "check_sam_security_mutex_%s",
+ username);
+ if (mutex_name_by_user == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* Grab the named mutex under root with 30 second timeout. */
+ become_root();
+ mtx = grab_named_mutex(frame, mutex_name_by_user, 30);
+ if (mtx != NULL) {
+ /* Re-load the account information if we got the mutex. */
+ ok = pdb_getsampwnam(sampass, username);
+ }
+ unbecome_root();
+
+ /* Everything from here on until mtx is freed is done under the mutex.*/
+
+ if (mtx == NULL) {
+ DBG_ERR("Acquisition of mutex %s failed "
+ "for user %s\n",
+ mutex_name_by_user,
+ username);
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ if (!ok) {
+ /*
+ * Re-load of account failed. This could only happen if the
+ * user was deleted in the meantime.
+ */
+ DBG_NOTICE("reload of user '%s' in passdb failed.\n",
+ username);
+ status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ /*
+ * Check if the account is now locked out - now under the mutex.
+ * This can happen if the server is under
+ * a password guess attack and the ACB_AUTOLOCK is set by
+ * another process.
+ */
+ if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+ DBG_NOTICE("Account for user %s was locked out.\n", username);
+ status = NT_STATUS_ACCOUNT_LOCKED_OUT;
+ goto done;
+ }
+
+ /*
+ * Notify passdb backend of login success/failure. If not
+ * NT_STATUS_OK the backend doesn't like the login
+ */
+ update_login_attempts_status = pdb_update_login_attempts(
+ sampass, NT_STATUS_IS_OK(status));
+
+ if (!NT_STATUS_IS_OK(status)) {
+ bool increment_bad_pw_count = false;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD) &&
+ (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ NT_STATUS_IS_OK(update_login_attempts_status))
+ {
+ increment_bad_pw_count = true;
+ }
+
+ if (increment_bad_pw_count) {
+ pdb_increment_bad_password_count(sampass);
+ updated_badpw = true;
+ } else {
+ pdb_update_bad_password_count(sampass,
+ &updated_badpw);
+ }
+ } else {
+ if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ (pdb_get_bad_password_count(sampass) > 0))
+ {
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ updated_badpw = true;
+ }
+ }
+
+ if (updated_badpw) {
+ NTSTATUS update_status;
+ become_root();
+ update_status = pdb_update_sam_account(sampass);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(update_status)) {
+ DEBUG(1, ("Failed to modify entry: %s\n",
+ nt_errstr(update_status)));
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ become_root();
+ status = change_oem_password(sampass,
+ rhost,
+ NULL,
+ new_passwd,
+ true,
+ NULL);
+ unbecome_root();
+ TALLOC_FREE(new_passwd);
+
+done:
+ TALLOC_FREE(frame);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ return status;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_samr_scompat.c"
diff --git a/source3/rpc_server/samr/srv_samr_util.c b/source3/rpc_server/samr/srv_samr_util.c
new file mode 100644
index 0000000..fa35ce6
--- /dev/null
+++ b/source3/rpc_server/samr/srv_samr_util.c
@@ -0,0 +1,756 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAMR Pipe utility functions.
+
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Guenther Deschner 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../librpc/gen_ndr/samr.h"
+#include "rpc_server/samr/srv_samr_util.h"
+#include "passdb.h"
+#include "lib/util/base64.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define STRING_CHANGED (old_string && !new_string) ||\
+ (!old_string && new_string) ||\
+ (old_string && new_string && (strcmp(old_string, new_string) != 0))
+
+#define STRING_CHANGED_NC(s1,s2) ((s1) && !(s2)) ||\
+ (!(s1) && (s2)) ||\
+ ((s1) && (s2) && (strcmp((s1), (s2)) != 0))
+
+/*************************************************************
+ Copies a struct samr_UserInfo2 to a struct samu
+**************************************************************/
+
+void copy_id2_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo2 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_COMMENT |
+ SAMR_FIELD_COUNTRY_CODE |
+ SAMR_FIELD_CODE_PAGE;
+ i.comment = from->comment;
+ i.country_code = from->country_code;
+ i.code_page = from->code_page;
+
+ copy_id21_to_sam_passwd("INFO_2", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo4 to a struct samu
+**************************************************************/
+
+void copy_id4_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo4 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_LOGON_HOURS;
+ i.logon_hours = from->logon_hours;
+
+ copy_id21_to_sam_passwd("INFO_4", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo6 to a struct samu
+**************************************************************/
+
+void copy_id6_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo6 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_ACCOUNT_NAME |
+ SAMR_FIELD_FULL_NAME;
+ i.account_name = from->account_name;
+ i.full_name = from->full_name;
+
+ copy_id21_to_sam_passwd("INFO_6", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo8 to a struct samu
+**************************************************************/
+
+void copy_id8_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo8 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_FULL_NAME;
+ i.full_name = from->full_name;
+
+ copy_id21_to_sam_passwd("INFO_8", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo10 to a struct samu
+**************************************************************/
+
+void copy_id10_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo10 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_HOME_DIRECTORY |
+ SAMR_FIELD_HOME_DRIVE;
+ i.home_directory = from->home_directory;
+ i.home_drive = from->home_drive;
+
+ copy_id21_to_sam_passwd("INFO_10", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo11 to a struct samu
+**************************************************************/
+
+void copy_id11_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo11 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_LOGON_SCRIPT;
+ i.logon_script = from->logon_script;
+
+ copy_id21_to_sam_passwd("INFO_11", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo12 to a struct samu
+**************************************************************/
+
+void copy_id12_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo12 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_PROFILE_PATH;
+ i.profile_path = from->profile_path;
+
+ copy_id21_to_sam_passwd("INFO_12", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo13 to a struct samu
+**************************************************************/
+
+void copy_id13_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo13 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_DESCRIPTION;
+ i.description = from->description;
+
+ copy_id21_to_sam_passwd("INFO_13", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo14 to a struct samu
+**************************************************************/
+
+void copy_id14_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo14 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_WORKSTATIONS;
+ i.workstations = from->workstations;
+
+ copy_id21_to_sam_passwd("INFO_14", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo16 to a struct samu
+**************************************************************/
+
+void copy_id16_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo16 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_ACCT_FLAGS;
+ i.acct_flags = from->acct_flags;
+
+ copy_id21_to_sam_passwd("INFO_16", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo17 to a struct samu
+**************************************************************/
+
+void copy_id17_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo17 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_ACCT_EXPIRY;
+ i.acct_expiry = from->acct_expiry;
+
+ copy_id21_to_sam_passwd("INFO_17", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo18 to a struct samu
+**************************************************************/
+
+void copy_id18_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo18 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_EXPIRED_FLAG;
+ i.password_expired = from->password_expired;
+
+ copy_id21_to_sam_passwd("INFO_18", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo20 to a struct samu
+**************************************************************/
+
+void copy_id20_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo20 *from)
+{
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ if (from->parameters.array) {
+ const char *old_string;
+ char *new_string = NULL;
+ old_string = pdb_get_munged_dial(to);
+ mung = data_blob_const(from->parameters.array,
+ from->parameters.length);
+
+ if (mung.length != 0) {
+ new_string = base64_encode_data_blob(talloc_tos(),
+ mung);
+ SMB_ASSERT(new_string != NULL);
+ }
+
+ DEBUG(10,("INFO_20 PARAMETERS: %s -> %s\n",
+ old_string, new_string));
+ if (STRING_CHANGED_NC(old_string,new_string)) {
+ pdb_set_munged_dial(to, new_string, PDB_CHANGED);
+ }
+
+ TALLOC_FREE(new_string);
+ }
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo21 to a struct samu
+**************************************************************/
+
+void copy_id21_to_sam_passwd(const char *log_prefix,
+ struct samu *to,
+ struct samr_UserInfo21 *from)
+{
+ time_t unix_time, stored_time;
+ const char *old_string, *new_string;
+ const char *l;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ if (log_prefix) {
+ l = log_prefix;
+ } else {
+ l = "INFO_21";
+ }
+
+ if (from->fields_present & SAMR_FIELD_LAST_LOGON) {
+ unix_time = nt_time_to_unix(from->last_logon);
+ stored_time = pdb_get_logon_time(to);
+ DEBUG(10,("%s SAMR_FIELD_LAST_LOGON: %lu -> %lu\n", l,
+ (long unsigned int)stored_time,
+ (long unsigned int)unix_time));
+ if (stored_time != unix_time) {
+ pdb_set_logon_time(to, unix_time, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_LAST_LOGOFF) {
+ unix_time = nt_time_to_unix(from->last_logoff);
+ stored_time = pdb_get_logoff_time(to);
+ DEBUG(10,("%s SAMR_FIELD_LAST_LOGOFF: %lu -> %lu\n", l,
+ (long unsigned int)stored_time,
+ (long unsigned int)unix_time));
+ if (stored_time != unix_time) {
+ pdb_set_logoff_time(to, unix_time, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_ACCT_EXPIRY) {
+ unix_time = nt_time_to_unix(from->acct_expiry);
+ stored_time = pdb_get_kickoff_time(to);
+ DEBUG(10,("%s SAMR_FIELD_ACCT_EXPIRY: %lu -> %lu\n", l,
+ (long unsigned int)stored_time,
+ (long unsigned int)unix_time));
+ if (stored_time != unix_time) {
+ pdb_set_kickoff_time(to, unix_time , PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_LAST_PWD_CHANGE) {
+ unix_time = nt_time_to_unix(from->last_password_change);
+ stored_time = pdb_get_pass_last_set_time(to);
+ DEBUG(10,("%s SAMR_FIELD_LAST_PWD_CHANGE: %lu -> %lu\n", l,
+ (long unsigned int)stored_time,
+ (long unsigned int)unix_time));
+ if (stored_time != unix_time) {
+ pdb_set_pass_last_set_time(to, unix_time, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_ACCOUNT_NAME) &&
+ (from->account_name.string)) {
+ old_string = pdb_get_username(to);
+ new_string = from->account_name.string;
+ DEBUG(10,("%s SAMR_FIELD_ACCOUNT_NAME: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_username(to, new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_FULL_NAME) &&
+ (from->full_name.string)) {
+ old_string = pdb_get_fullname(to);
+ new_string = from->full_name.string;
+ DEBUG(10,("%s SAMR_FIELD_FULL_NAME: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_fullname(to, new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_HOME_DIRECTORY) &&
+ (from->home_directory.string)) {
+ old_string = pdb_get_homedir(to);
+ new_string = from->home_directory.string;
+ DEBUG(10,("%s SAMR_FIELD_HOME_DIRECTORY: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_homedir(to, new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_HOME_DRIVE) &&
+ (from->home_drive.string)) {
+ old_string = pdb_get_dir_drive(to);
+ new_string = from->home_drive.string;
+ DEBUG(10,("%s SAMR_FIELD_HOME_DRIVE: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_dir_drive(to, new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_LOGON_SCRIPT) &&
+ (from->logon_script.string)) {
+ old_string = pdb_get_logon_script(to);
+ new_string = from->logon_script.string;
+ DEBUG(10,("%s SAMR_FIELD_LOGON_SCRIPT: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_logon_script(to , new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_PROFILE_PATH) &&
+ (from->profile_path.string)) {
+ old_string = pdb_get_profile_path(to);
+ new_string = from->profile_path.string;
+ DEBUG(10,("%s SAMR_FIELD_PROFILE_PATH: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_profile_path(to , new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_DESCRIPTION) &&
+ (from->description.string)) {
+ old_string = pdb_get_acct_desc(to);
+ new_string = from->description.string;
+ DEBUG(10,("%s SAMR_FIELD_DESCRIPTION: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_acct_desc(to, new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_WORKSTATIONS) &&
+ (from->workstations.string)) {
+ old_string = pdb_get_workstations(to);
+ new_string = from->workstations.string;
+ DEBUG(10,("%s SAMR_FIELD_WORKSTATIONS: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_workstations(to , new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_COMMENT) &&
+ (from->comment.string)) {
+ old_string = pdb_get_comment(to);
+ new_string = from->comment.string;
+ DEBUG(10,("%s SAMR_FIELD_COMMENT: %s -> %s\n", l,
+ old_string, new_string));
+ if (STRING_CHANGED) {
+ pdb_set_comment(to, new_string, PDB_CHANGED);
+ }
+ }
+
+ if ((from->fields_present & SAMR_FIELD_PARAMETERS) &&
+ (from->parameters.array)) {
+ char *newstr = NULL;
+ DATA_BLOB mung;
+ old_string = pdb_get_munged_dial(to);
+
+ mung = data_blob_const(from->parameters.array,
+ from->parameters.length);
+
+ if (mung.length != 0) {
+ newstr = base64_encode_data_blob(talloc_tos(), mung);
+ SMB_ASSERT(newstr != NULL);
+ }
+ DEBUG(10,("%s SAMR_FIELD_PARAMETERS: %s -> %s\n", l,
+ old_string, newstr));
+ if (STRING_CHANGED_NC(old_string,newstr)) {
+ pdb_set_munged_dial(to, newstr, PDB_CHANGED);
+ }
+
+ TALLOC_FREE(newstr);
+ }
+
+ if (from->fields_present & SAMR_FIELD_RID) {
+ if (from->rid == 0) {
+ DEBUG(10,("%s: Asked to set User RID to 0 !? Skipping change!\n", l));
+ } else if (from->rid != pdb_get_user_rid(to)) {
+ DEBUG(10,("%s SAMR_FIELD_RID: %u -> %u NOT UPDATED!\n", l,
+ pdb_get_user_rid(to), from->rid));
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_PRIMARY_GID) {
+ if (from->primary_gid == 0) {
+ DEBUG(10,("%s: Asked to set Group RID to 0 !? Skipping change!\n", l));
+ } else if (from->primary_gid != pdb_get_group_rid(to)) {
+ DEBUG(10,("%s SAMR_FIELD_PRIMARY_GID: %u -> %u\n", l,
+ pdb_get_group_rid(to), from->primary_gid));
+ pdb_set_group_sid_from_rid(to,
+ from->primary_gid, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_ACCT_FLAGS) {
+ DEBUG(10,("%s SAMR_FIELD_ACCT_FLAGS: %08X -> %08X\n", l,
+ pdb_get_acct_ctrl(to), from->acct_flags));
+ if (from->acct_flags != pdb_get_acct_ctrl(to)) {
+
+ /* You cannot autolock an unlocked account via
+ * setuserinfo calls, so make sure to remove the
+ * ACB_AUTOLOCK bit here - gd */
+
+ if ((from->acct_flags & ACB_AUTOLOCK) &&
+ !(pdb_get_acct_ctrl(to) & ACB_AUTOLOCK)) {
+ from->acct_flags &= ~ACB_AUTOLOCK;
+ }
+
+ if (!(from->acct_flags & ACB_AUTOLOCK) &&
+ (pdb_get_acct_ctrl(to) & ACB_AUTOLOCK)) {
+ /* We're unlocking a previously locked user. Reset bad password counts.
+ Patch from Jianliang Lu. <Jianliang.Lu@getronics.com> */
+ pdb_set_bad_password_count(to, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(to, 0, PDB_CHANGED);
+ }
+ pdb_set_acct_ctrl(to, from->acct_flags, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_LOGON_HOURS) {
+ char oldstr[44]; /* hours strings are 42 bytes. */
+ char newstr[44];
+ DEBUG(15,("%s SAMR_FIELD_LOGON_HOURS (units_per_week): %08X -> %08X\n", l,
+ pdb_get_logon_divs(to), from->logon_hours.units_per_week));
+ if (from->logon_hours.units_per_week != pdb_get_logon_divs(to)) {
+ pdb_set_logon_divs(to,
+ from->logon_hours.units_per_week, PDB_CHANGED);
+ }
+
+ DEBUG(15,("%s SAMR_FIELD_LOGON_HOURS (units_per_week/8): %08X -> %08X\n", l,
+ pdb_get_hours_len(to),
+ from->logon_hours.units_per_week/8));
+ if (from->logon_hours.units_per_week/8 != pdb_get_hours_len(to)) {
+ pdb_set_hours_len(to,
+ from->logon_hours.units_per_week/8, PDB_CHANGED);
+ }
+
+ DEBUG(15,("%s SAMR_FIELD_LOGON_HOURS (bits): %s -> %s\n", l,
+ pdb_get_hours(to), from->logon_hours.bits));
+ pdb_sethexhours(oldstr, pdb_get_hours(to));
+ pdb_sethexhours(newstr, from->logon_hours.bits);
+ if (!strequal(oldstr, newstr)) {
+ pdb_set_hours(to, from->logon_hours.bits,
+ from->logon_hours.units_per_week/8,
+ PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_BAD_PWD_COUNT) {
+ DEBUG(10,("%s SAMR_FIELD_BAD_PWD_COUNT: %08X -> %08X\n", l,
+ pdb_get_bad_password_count(to), from->bad_password_count));
+ if (from->bad_password_count != pdb_get_bad_password_count(to)) {
+ pdb_set_bad_password_count(to,
+ from->bad_password_count, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_NUM_LOGONS) {
+ DEBUG(10,("%s SAMR_FIELD_NUM_LOGONS: %08X -> %08X\n", l,
+ pdb_get_logon_count(to), from->logon_count));
+ if (from->logon_count != pdb_get_logon_count(to)) {
+ pdb_set_logon_count(to, from->logon_count, PDB_CHANGED);
+ }
+ }
+
+ /* If the must change flag is set, the last set time goes to zero.
+ the must change and can change fields also do, but they are
+ calculated from policy, not set from the wire */
+
+ if (from->fields_present & SAMR_FIELD_EXPIRED_FLAG) {
+ DEBUG(10,("%s SAMR_FIELD_EXPIRED_FLAG: %02X\n", l,
+ from->password_expired));
+ if (from->password_expired != 0) {
+ /* Only allow the set_time to zero (which means
+ "User Must Change Password on Next Login"
+ if the user object allows password change. */
+ if (pdb_get_pass_can_change(to)) {
+ pdb_set_pass_last_set_time(to, 0, PDB_CHANGED);
+ } else {
+ DEBUG(10,("%s Disallowing set of 'User Must "
+ "Change Password on Next Login' as "
+ "user object disallows this.\n", l));
+ }
+ } else {
+ /* A subtlety here: some windows commands will
+ clear the expired flag even though it's not
+ set, and we don't want to reset the time
+ in these caess. "net user /dom <user> /active:y"
+ for example, to clear an autolocked acct.
+ We must check to see if it's expired first. jmcd */
+
+ uint32_t pwd_max_age = 0;
+ time_t now = time(NULL);
+
+ pdb_get_account_policy(PDB_POLICY_MAX_PASSWORD_AGE, &pwd_max_age);
+
+ if (pwd_max_age == (uint32_t)-1 || pwd_max_age == 0) {
+ pwd_max_age = get_time_t_max();
+ }
+
+ stored_time = pdb_get_pass_last_set_time(to);
+
+ /* we will only *set* a pwdlastset date when
+ a) the last pwdlastset time was 0 (user was forced to
+ change password).
+ b) the users password has not expired. gd. */
+
+ if ((stored_time == 0) ||
+ ((now - stored_time) > pwd_max_age)) {
+ pdb_set_pass_last_set_time(to, now, PDB_CHANGED);
+ }
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_COUNTRY_CODE) {
+ DEBUG(10,("%s SAMR_FIELD_COUNTRY_CODE: %08X -> %08X\n", l,
+ pdb_get_country_code(to), from->country_code));
+ if (from->country_code != pdb_get_country_code(to)) {
+ pdb_set_country_code(to,
+ from->country_code, PDB_CHANGED);
+ }
+ }
+
+ if (from->fields_present & SAMR_FIELD_CODE_PAGE) {
+ DEBUG(10,("%s SAMR_FIELD_CODE_PAGE: %08X -> %08X\n", l,
+ pdb_get_code_page(to), from->code_page));
+ if (from->code_page != pdb_get_code_page(to)) {
+ pdb_set_code_page(to,
+ from->code_page, PDB_CHANGED);
+ }
+ }
+}
+
+
+/*************************************************************
+ Copies a struct samr_UserInfo23 to a struct samu
+**************************************************************/
+
+void copy_id23_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo23 *from)
+{
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ copy_id21_to_sam_passwd("INFO 23", to, &from->info);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo24 to a struct samu
+**************************************************************/
+
+void copy_id24_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo24 *from)
+{
+ struct samr_UserInfo21 i;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ ZERO_STRUCT(i);
+
+ i.fields_present = SAMR_FIELD_EXPIRED_FLAG;
+ i.password_expired = from->password_expired;
+
+ copy_id21_to_sam_passwd("INFO_24", to, &i);
+}
+
+/*************************************************************
+ Copies a struct samr_UserInfo25 to a struct samu
+**************************************************************/
+
+void copy_id25_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo25 *from)
+{
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ copy_id21_to_sam_passwd("INFO_25", to, &from->info);
+}
+
+void copy_id32_to_sam_passwd(struct samu *to, struct samr_UserInfo32 *from)
+{
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ copy_id21_to_sam_passwd("INFO_32", to, &from->info);
+}
+
+void copy_pwd_expired_to_sam_passwd(struct samu *to,
+ uint8_t password_expired)
+{
+ struct samr_UserInfo21 i = {
+ .fields_present = SAMR_FIELD_EXPIRED_FLAG,
+ .password_expired = password_expired,
+ };
+
+ if (to == NULL) {
+ return;
+ }
+
+ copy_id21_to_sam_passwd("INFO_GENERIC", to, &i);
+}
diff --git a/source3/rpc_server/samr/srv_samr_util.h b/source3/rpc_server/samr/srv_samr_util.h
new file mode 100644
index 0000000..5e839ac
--- /dev/null
+++ b/source3/rpc_server/samr/srv_samr_util.h
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ SAMR Pipe utility functions.
+
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1998
+ Copyright (C) Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Guenther Deschner 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* The following definitions come from rpc_server/srv_samr_util.c */
+
+struct samu;
+
+void copy_id2_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo2 *from);
+void copy_id4_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo4 *from);
+void copy_id6_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo6 *from);
+void copy_id8_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo8 *from);
+void copy_id10_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo10 *from);
+void copy_id11_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo11 *from);
+void copy_id12_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo12 *from);
+void copy_id13_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo13 *from);
+void copy_id14_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo14 *from);
+void copy_id16_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo16 *from);
+void copy_id17_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo17 *from);
+void copy_id18_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo18 *from);
+void copy_id20_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo20 *from);
+void copy_id21_to_sam_passwd(const char *log_prefix,
+ struct samu *to,
+ struct samr_UserInfo21 *from);
+void copy_id23_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo23 *from);
+void copy_id24_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo24 *from);
+void copy_id25_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo25 *from);
+void copy_id32_to_sam_passwd(struct samu *to, struct samr_UserInfo32 *from);
+void copy_pwd_expired_to_sam_passwd(struct samu *to,
+ uint8_t password_expired);
+
+/* The following definitions come from rpc_server/srv_samr_chgpasswd.c */
+
+bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
+ const char *oldpass, const char *newpass, bool as_root);
+NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
+ char *old_passwd, char *new_passwd,
+ bool as_root,
+ enum samPwdChangeReason *samr_reject_reason);
+NTSTATUS pass_oem_change(char *user, const char *rhost,
+ uchar password_encrypted_with_lm_hash[516],
+ const uchar old_lm_hash_encrypted[16],
+ uchar password_encrypted_with_nt_hash[516],
+ const uchar old_nt_hash_encrypted[16],
+ enum samPwdChangeReason *reject_reason);
+NTSTATUS check_password_complexity(const char *username,
+ const char *fullname,
+ const char *password,
+ enum samPwdChangeReason *samr_reject_reason);
+NTSTATUS samr_set_password_aes(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *cdk,
+ struct samr_EncryptedPasswordAES *pwbuf,
+ char **new_password_str);
diff --git a/source3/rpc_server/spoolss/iremotewinspool_util.c b/source3/rpc_server/spoolss/iremotewinspool_util.c
new file mode 100644
index 0000000..6b9c726
--- /dev/null
+++ b/source3/rpc_server/spoolss/iremotewinspool_util.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Guenther Deschner 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_winspool.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "rpc_server/spoolss/iremotewinspool_util.h"
+
+#define _PAR_MAPPING(NAME) { .iremotewinspool_opcode = NDR_WINSPOOL_ASYNC ##NAME, .spoolss_opcode = NDR_SPOOLSS_ ##NAME }
+#define _PAR_MAPPING_EX(NAME) { .iremotewinspool_opcode = NDR_WINSPOOL_ASYNC ##NAME, .spoolss_opcode = NDR_SPOOLSS_ ##NAME## EX }
+#define _PAR_MAPPING_2(NAME) { .iremotewinspool_opcode = NDR_WINSPOOL_ASYNC ##NAME, .spoolss_opcode = NDR_SPOOLSS_ ##NAME## 2 }
+
+struct {
+ int iremotewinspool_opcode;
+ int spoolss_opcode;
+} proxy_table[] = {
+
+ /* 3.1.4.1. Printer Management Methods */
+
+ _PAR_MAPPING_EX(OPENPRINTER),
+ _PAR_MAPPING_EX(ADDPRINTER),
+ _PAR_MAPPING(DELETEPRINTER),
+ _PAR_MAPPING(SETPRINTER),
+ _PAR_MAPPING(GETPRINTER),
+ _PAR_MAPPING(GETPRINTERDATA),
+ _PAR_MAPPING(GETPRINTERDATAEX),
+ _PAR_MAPPING(SETPRINTERDATA),
+ _PAR_MAPPING(SETPRINTERDATAEX),
+ _PAR_MAPPING(CLOSEPRINTER),
+ _PAR_MAPPING(ENUMPRINTERDATA),
+ _PAR_MAPPING(ENUMPRINTERDATAEX),
+ _PAR_MAPPING(ENUMPRINTERKEY),
+ _PAR_MAPPING(DELETEPRINTERDATA),
+ _PAR_MAPPING(DELETEPRINTERDATAEX),
+ _PAR_MAPPING(DELETEPRINTERKEY),
+ _PAR_MAPPING(SENDRECVBIDIDATA),
+ _PAR_MAPPING(CREATEPRINTERIC),
+ _PAR_MAPPING(PLAYGDISCRIPTONPRINTERIC),
+ _PAR_MAPPING(DELETEPRINTERIC),
+ _PAR_MAPPING(ENUMPRINTERS),
+ _PAR_MAPPING(ADDPERMACHINECONNECTION),
+ _PAR_MAPPING(DELETEPERMACHINECONNECTION),
+ _PAR_MAPPING(ENUMPERMACHINECONNECTIONS),
+ _PAR_MAPPING(RESETPRINTER),
+
+ /* 3.1.4.2. Printer Driver Management Methods */
+
+ _PAR_MAPPING_2(GETPRINTERDRIVER),
+ _PAR_MAPPING_EX(ADDPRINTERDRIVER),
+ _PAR_MAPPING(ENUMPRINTERDRIVERS),
+ _PAR_MAPPING(GETPRINTERDRIVERDIRECTORY),
+ _PAR_MAPPING(DELETEPRINTERDRIVER),
+ _PAR_MAPPING(DELETEPRINTERDRIVEREX),
+ /* No mapping for: RpcAsyncInstallPrinterDriverFromPackage */
+ /* No mapping for: RpcAsyncUploadPrinterDriverPackage */
+ _PAR_MAPPING(GETCOREPRINTERDRIVERS),
+ /* No mapping for: RpcAsyncCorePrinterDriverInstalled */
+ _PAR_MAPPING(GETPRINTERDRIVERPACKAGEPATH),
+ /* No mapping for: RpcAsyncDeletePrinterDriverPackage */
+
+ /* 3.1.4.3. Printer Port Management Methods */
+
+ _PAR_MAPPING(XCVDATA),
+ _PAR_MAPPING(ENUMPORTS),
+ _PAR_MAPPING_EX(ADDPORT),
+ _PAR_MAPPING(SETPORT),
+
+ /* 3.1.4.4. Printer Processor Management Methods */
+
+ _PAR_MAPPING(ADDPRINTPROCESSOR),
+ _PAR_MAPPING(ENUMPRINTPROCESSORS),
+ _PAR_MAPPING(GETPRINTPROCESSORDIRECTORY),
+ _PAR_MAPPING(DELETEPRINTPROCESSOR),
+ _PAR_MAPPING(ENUMPRINTPROCESSORDATATYPES),
+
+ /* 3.1.4.5. Port Monitor Management Methods */
+
+ _PAR_MAPPING(ENUMMONITORS),
+ _PAR_MAPPING(ADDMONITOR),
+ _PAR_MAPPING(DELETEMONITOR),
+
+ /* 3.1.4.6. Form Management Methods */
+
+ _PAR_MAPPING(ADDFORM),
+ _PAR_MAPPING(DELETEFORM),
+ _PAR_MAPPING(GETFORM),
+ _PAR_MAPPING(SETFORM),
+ _PAR_MAPPING(ENUMFORMS),
+
+ /* 3.1.4.7. Job Management Methods */
+
+ _PAR_MAPPING(SETJOB),
+ _PAR_MAPPING(GETJOB),
+ _PAR_MAPPING(ENUMJOBS),
+ _PAR_MAPPING(ADDJOB),
+ _PAR_MAPPING(SCHEDULEJOB),
+
+ /* 3.1.4.8. Job Printing Methods */
+
+ _PAR_MAPPING(STARTDOCPRINTER),
+ _PAR_MAPPING(STARTPAGEPRINTER),
+ _PAR_MAPPING(WRITEPRINTER),
+ _PAR_MAPPING(ENDPAGEPRINTER),
+ _PAR_MAPPING(ENDDOCPRINTER),
+ _PAR_MAPPING(ABORTPRINTER),
+ _PAR_MAPPING(READPRINTER),
+
+ /* 3.1.4.9. Printing Related Notification Methods */
+
+ /* No mapping for: RpcSyncRegisterForRemoteNotifications */
+ /* No mapping for: RpcSyncUnRegisterForRemoteNotifications */
+ /* No mapping for: RpcSyncRefreshRemoteNotifications */
+ /* No mapping for: RpcAsyncGetRemoteNotifications */
+
+ /* 3.1.4.10. Job Named Property Management Methods */
+
+ _PAR_MAPPING(GETJOBNAMEDPROPERTYVALUE),
+ _PAR_MAPPING(SETJOBNAMEDPROPERTY),
+ _PAR_MAPPING(DELETEJOBNAMEDPROPERTY),
+ _PAR_MAPPING(ENUMJOBNAMEDPROPERTIES),
+
+ /* 3.1.4.11. Branch Office Remote Logging Methods */
+
+ _PAR_MAPPING(LOGJOBINFOFORBRANCHOFFICE),
+};
+
+bool iremotewinspool_map_opcode(uint16_t opcode,
+ uint16_t *proxy_opcode)
+{
+ int i;
+
+ for (i = 0; i <ARRAY_SIZE(proxy_table); i++) {
+ if (proxy_table[i].iremotewinspool_opcode == opcode) {
+ *proxy_opcode = proxy_table[i].spoolss_opcode;
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/source3/rpc_server/spoolss/iremotewinspool_util.h b/source3/rpc_server/spoolss/iremotewinspool_util.h
new file mode 100644
index 0000000..e2de82a
--- /dev/null
+++ b/source3/rpc_server/spoolss/iremotewinspool_util.h
@@ -0,0 +1,21 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Guenther Deschner 2016
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+bool iremotewinspool_map_opcode(uint16_t opcode,
+ uint16_t *proxy_opcode);
diff --git a/source3/rpc_server/spoolss/srv_iremotewinspool.c b/source3/rpc_server/spoolss/srv_iremotewinspool.c
new file mode 100644
index 0000000..5bc4228
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_iremotewinspool.c
@@ -0,0 +1,2382 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * server auto-generated by pidl. DO NOT MODIFY!
+ */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_winspool.h"
+#include "librpc/gen_ndr/ndr_winspool_scompat.h"
+#include "librpc/gen_ndr/ndr_spoolss_scompat.h"
+#include "rpc_server/rpc_config.h"
+#include "rpc_server/rpc_server.h"
+#include "rpc_server/spoolss/iremotewinspool_util.h"
+
+static bool forward_opnum_to_spoolss(uint16_t opnum) {
+ switch (opnum) {
+ case 58: /* winspool_SyncRegisterForRemoteNotifications */
+ case 59: /* winspool_SyncUnRegisterForRemoteNotifications */
+ case 60: /* winspool_SyncRefreshRemoteNotifications */
+ case 61: /* winspool_AsyncGetRemoteNotifications */
+ case 62: /* winspool_AsyncInstallPrinterDriverFromPackage */
+ case 63: /* winspool_AsyncUploadPrinterDriverPackage */
+ case 65: /* winspool_AsyncCorePrinterDriverInstalled */
+ case 67: /* winspool_AsyncDeletePrinterDriverPackage */
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+/* iremotewinspool - dcerpc server boilerplate generated by pidl */
+static NTSTATUS iremotewinspool__op_bind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)
+{
+#ifdef DCESRV_INTERFACE_IREMOTEWINSPOOL_BIND
+ return DCESRV_INTERFACE_IREMOTEWINSPOOL_BIND(context,iface);
+#else
+ return NT_STATUS_OK;
+#endif
+}
+
+static void iremotewinspool__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)
+{
+#ifdef DCESRV_INTERFACE_IREMOTEWINSPOOL_UNBIND
+ DCESRV_INTERFACE_IREMOTEWINSPOOL_UNBIND(context, iface);
+#else
+ return;
+#endif
+}
+
+NTSTATUS iremotewinspool__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;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ uint16_t mapped_opnum;
+
+ dce_call->fault_code = 0;
+
+ if (forward_opnum_to_spoolss(opnum)) {
+ bool ok;
+ ok = iremotewinspool_map_opcode(opnum, &mapped_opnum);
+ if (ok) {
+ dce_call->pkt.u.request.opnum = mapped_opnum;
+ }
+ return spoolss__op_ndr_pull(dce_call, mem_ctx, pull, r);
+ }
+
+ if (opnum >= ndr_table_iremotewinspool.num_calls) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ *r = talloc_named(mem_ctx, ndr_table_iremotewinspool.calls[opnum].struct_size, "struct %s", ndr_table_iremotewinspool.calls[opnum].name);
+ NT_STATUS_HAVE_NO_MEMORY(*r);
+
+ /* unravel the NDR for the packet */
+ ndr_err = ndr_table_iremotewinspool.calls[opnum].ndr_pull(pull, NDR_IN, *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 iremotewinspool__op_dispatch_internal(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r, bool rpcint_call)
+{
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ struct pipes_struct *p = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ bool impersonated = false;
+ bool ok;
+ struct GUID object_uuid;
+
+ ok = dce_call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID;
+ if (!ok) {
+ dce_call->fault_code = DCERPC_NCA_S_UNSUPPORTED_TYPE;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ status = GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &object_uuid);
+ if (!NT_STATUS_IS_OK(status)) {
+ dce_call->fault_code = DCERPC_NCA_S_UNSUPPORTED_TYPE;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if (!GUID_equal(&dce_call->pkt.u.request.object.object, &object_uuid)) {
+ dce_call->fault_code = DCERPC_NCA_S_UNSUPPORTED_TYPE;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if (forward_opnum_to_spoolss(opnum)) {
+ return spoolss__op_dispatch(dce_call, mem_ctx, r);
+ }
+
+ /* Retrieve pipes struct */
+ p = dcesrv_get_pipes_struct(dce_call->conn);
+ p->dce_call = dce_call;
+ p->mem_ctx = mem_ctx;
+ /* Reset pipes struct fault state */
+ p->fault_state = 0;
+
+ /* Impersonate */
+ if (!rpcint_call) {
+ impersonated = become_authenticated_pipe_user(
+ dce_call->auth_state->session_info);
+ if (!impersonated) {
+ dce_call->fault_code = DCERPC_FAULT_ACCESS_DENIED;
+ status = NT_STATUS_NET_WRITE_FAULT;
+ goto fail;
+ }
+ }
+
+ switch (opnum) {
+ case 0: { /* winspool_AsyncOpenPrinter */
+ struct winspool_AsyncOpenPrinter *r2 = (struct winspool_AsyncOpenPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncOpenPrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pHandle = talloc_zero(r2, struct policy_handle);
+ if (r2->out.pHandle == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncOpenPrinter(p, r2);
+ break;
+ }
+ case 1: { /* winspool_AsyncAddPrinter */
+ struct winspool_AsyncAddPrinter *r2 = (struct winspool_AsyncAddPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pHandle = talloc_zero(r2, struct policy_handle);
+ if (r2->out.pHandle == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncAddPrinter(p, r2);
+ break;
+ }
+ case 2: { /* winspool_AsyncSetJob */
+ struct winspool_AsyncSetJob *r2 = (struct winspool_AsyncSetJob *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetJob, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetJob(p, r2);
+ break;
+ }
+ case 3: { /* winspool_AsyncGetJob */
+ struct winspool_AsyncGetJob *r2 = (struct winspool_AsyncGetJob *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetJob, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pJob = r2->in.pJob;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetJob(p, r2);
+ break;
+ }
+ case 4: { /* winspool_AsyncEnumJobs */
+ struct winspool_AsyncEnumJobs *r2 = (struct winspool_AsyncEnumJobs *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumJobs, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pJob = r2->in.pJob;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumJobs(p, r2);
+ break;
+ }
+ case 5: { /* winspool_AsyncAddJob */
+ struct winspool_AsyncAddJob *r2 = (struct winspool_AsyncAddJob *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddJob, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pAddJob = r2->in.pAddJob;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncAddJob(p, r2);
+ break;
+ }
+ case 6: { /* winspool_AsyncScheduleJob */
+ struct winspool_AsyncScheduleJob *r2 = (struct winspool_AsyncScheduleJob *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncScheduleJob, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncScheduleJob(p, r2);
+ break;
+ }
+ case 7: { /* winspool_AsyncDeletePrinter */
+ struct winspool_AsyncDeletePrinter *r2 = (struct winspool_AsyncDeletePrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinter(p, r2);
+ break;
+ }
+ case 8: { /* winspool_AsyncSetPrinter */
+ struct winspool_AsyncSetPrinter *r2 = (struct winspool_AsyncSetPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetPrinter(p, r2);
+ break;
+ }
+ case 9: { /* winspool_AsyncGetPrinter */
+ struct winspool_AsyncGetPrinter *r2 = (struct winspool_AsyncGetPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pPrinter = r2->in.pPrinter;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrinter(p, r2);
+ break;
+ }
+ case 10: { /* winspool_AsyncStartDocPrinter */
+ struct winspool_AsyncStartDocPrinter *r2 = (struct winspool_AsyncStartDocPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncStartDocPrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pJobId = talloc_zero(r2, uint32_t);
+ if (r2->out.pJobId == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncStartDocPrinter(p, r2);
+ break;
+ }
+ case 11: { /* winspool_AsyncStartPagePrinter */
+ struct winspool_AsyncStartPagePrinter *r2 = (struct winspool_AsyncStartPagePrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncStartPagePrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncStartPagePrinter(p, r2);
+ break;
+ }
+ case 12: { /* winspool_AsyncWritePrinter */
+ struct winspool_AsyncWritePrinter *r2 = (struct winspool_AsyncWritePrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncWritePrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pcWritten = talloc_zero(r2, uint32_t);
+ if (r2->out.pcWritten == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncWritePrinter(p, r2);
+ break;
+ }
+ case 13: { /* winspool_AsyncEndPagePrinter */
+ struct winspool_AsyncEndPagePrinter *r2 = (struct winspool_AsyncEndPagePrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEndPagePrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncEndPagePrinter(p, r2);
+ break;
+ }
+ case 14: { /* winspool_AsyncEndDocPrinter */
+ struct winspool_AsyncEndDocPrinter *r2 = (struct winspool_AsyncEndDocPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEndDocPrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncEndDocPrinter(p, r2);
+ break;
+ }
+ case 15: { /* winspool_AsyncAbortPrinter */
+ struct winspool_AsyncAbortPrinter *r2 = (struct winspool_AsyncAbortPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAbortPrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAbortPrinter(p, r2);
+ break;
+ }
+ case 16: { /* winspool_AsyncGetPrinterData */
+ struct winspool_AsyncGetPrinterData *r2 = (struct winspool_AsyncGetPrinterData *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterData, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pType = talloc_zero(r2, uint32_t);
+ if (r2->out.pType == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pData = talloc_zero_array(r2, uint8_t, r2->in.nSize);
+ if (r2->out.pData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrinterData(p, r2);
+ break;
+ }
+ case 17: { /* winspool_AsyncGetPrinterDataEx */
+ struct winspool_AsyncGetPrinterDataEx *r2 = (struct winspool_AsyncGetPrinterDataEx *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDataEx, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pType = talloc_zero(r2, uint32_t);
+ if (r2->out.pType == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pData = talloc_zero_array(r2, uint8_t, r2->in.nSize);
+ if (r2->out.pData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrinterDataEx(p, r2);
+ break;
+ }
+ case 18: { /* winspool_AsyncSetPrinterData */
+ struct winspool_AsyncSetPrinterData *r2 = (struct winspool_AsyncSetPrinterData *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPrinterData, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetPrinterData(p, r2);
+ break;
+ }
+ case 19: { /* winspool_AsyncSetPrinterDataEx */
+ struct winspool_AsyncSetPrinterDataEx *r2 = (struct winspool_AsyncSetPrinterDataEx *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPrinterDataEx, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetPrinterDataEx(p, r2);
+ break;
+ }
+ case 20: { /* winspool_AsyncClosePrinter */
+ struct winspool_AsyncClosePrinter *r2 = (struct winspool_AsyncClosePrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncClosePrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.phPrinter = r2->in.phPrinter;
+ r2->out.result = _winspool_AsyncClosePrinter(p, r2);
+ break;
+ }
+ case 21: { /* winspool_AsyncAddForm */
+ struct winspool_AsyncAddForm *r2 = (struct winspool_AsyncAddForm *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddForm, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAddForm(p, r2);
+ break;
+ }
+ case 22: { /* winspool_AsyncDeleteForm */
+ struct winspool_AsyncDeleteForm *r2 = (struct winspool_AsyncDeleteForm *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeleteForm, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeleteForm(p, r2);
+ break;
+ }
+ case 23: { /* winspool_AsyncGetForm */
+ struct winspool_AsyncGetForm *r2 = (struct winspool_AsyncGetForm *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetForm, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pForm = r2->in.pForm;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetForm(p, r2);
+ break;
+ }
+ case 24: { /* winspool_AsyncSetForm */
+ struct winspool_AsyncSetForm *r2 = (struct winspool_AsyncSetForm *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetForm, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetForm(p, r2);
+ break;
+ }
+ case 25: { /* winspool_AsyncEnumForms */
+ struct winspool_AsyncEnumForms *r2 = (struct winspool_AsyncEnumForms *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumForms, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pForm = r2->in.pForm;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumForms(p, r2);
+ break;
+ }
+ case 26: { /* winspool_AsyncGetPrinterDriver */
+ struct winspool_AsyncGetPrinterDriver *r2 = (struct winspool_AsyncGetPrinterDriver *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDriver, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pDriver = r2->in.pDriver;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pdwServerMaxVersion = talloc_zero(r2, uint32_t);
+ if (r2->out.pdwServerMaxVersion == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pdwServerMinVersion = talloc_zero(r2, uint32_t);
+ if (r2->out.pdwServerMinVersion == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrinterDriver(p, r2);
+ break;
+ }
+ case 27: { /* winspool_AsyncEnumPrinterData */
+ struct winspool_AsyncEnumPrinterData *r2 = (struct winspool_AsyncEnumPrinterData *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterData, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pValueName = talloc_zero_array(r2, uint16_t, r2->in.cbValueName / 2);
+ if (r2->out.pValueName == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbValueName = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbValueName == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pType = talloc_zero(r2, uint32_t);
+ if (r2->out.pType == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pData = talloc_zero_array(r2, uint8_t, r2->in.cbData);
+ if (r2->out.pData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbData = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrinterData(p, r2);
+ break;
+ }
+ case 28: { /* winspool_AsyncEnumPrinterDataEx */
+ struct winspool_AsyncEnumPrinterDataEx *r2 = (struct winspool_AsyncEnumPrinterDataEx *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterDataEx, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pEnumValues = talloc_zero_array(r2, uint8_t, r2->in.cbEnumValues);
+ if (r2->out.pEnumValues == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbEnumValues = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbEnumValues == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pnEnumValues = talloc_zero(r2, uint32_t);
+ if (r2->out.pnEnumValues == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrinterDataEx(p, r2);
+ break;
+ }
+ case 29: { /* winspool_AsyncEnumPrinterKey */
+ struct winspool_AsyncEnumPrinterKey *r2 = (struct winspool_AsyncEnumPrinterKey *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterKey, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pSubkey = talloc_zero_array(r2, uint16_t, r2->in.cbSubkey / 2);
+ if (r2->out.pSubkey == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbSubkey = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbSubkey == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrinterKey(p, r2);
+ break;
+ }
+ case 30: { /* winspool_AsyncDeletePrinterData */
+ struct winspool_AsyncDeletePrinterData *r2 = (struct winspool_AsyncDeletePrinterData *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterData, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinterData(p, r2);
+ break;
+ }
+ case 31: { /* winspool_AsyncDeletePrinterDataEx */
+ struct winspool_AsyncDeletePrinterDataEx *r2 = (struct winspool_AsyncDeletePrinterDataEx *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDataEx, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinterDataEx(p, r2);
+ break;
+ }
+ case 32: { /* winspool_AsyncDeletePrinterKey */
+ struct winspool_AsyncDeletePrinterKey *r2 = (struct winspool_AsyncDeletePrinterKey *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterKey, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinterKey(p, r2);
+ break;
+ }
+ case 33: { /* winspool_AsyncXcvData */
+ struct winspool_AsyncXcvData *r2 = (struct winspool_AsyncXcvData *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncXcvData, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pdwStatus = r2->in.pdwStatus;
+ r2->out.pOutputData = talloc_zero_array(r2, uint8_t, r2->in.cbOutputData);
+ if (r2->out.pOutputData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcbOutputNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbOutputNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncXcvData(p, r2);
+ break;
+ }
+ case 34: { /* winspool_AsyncSendRecvBidiData */
+ struct winspool_AsyncSendRecvBidiData *r2 = (struct winspool_AsyncSendRecvBidiData *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSendRecvBidiData, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.ppRespData = talloc_zero(r2, struct RPC_BIDI_RESPONSE_CONTAINER *);
+ if (r2->out.ppRespData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncSendRecvBidiData(p, r2);
+ break;
+ }
+ case 35: { /* winspool_AsyncCreatePrinterIC */
+ struct winspool_AsyncCreatePrinterIC *r2 = (struct winspool_AsyncCreatePrinterIC *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncCreatePrinterIC, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pHandle = talloc_zero(r2, struct policy_handle);
+ if (r2->out.pHandle == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncCreatePrinterIC(p, r2);
+ break;
+ }
+ case 36: { /* winspool_AsyncPlayGdiScriptOnPrinterIC */
+ struct winspool_AsyncPlayGdiScriptOnPrinterIC *r2 = (struct winspool_AsyncPlayGdiScriptOnPrinterIC *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncPlayGdiScriptOnPrinterIC, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pOut = talloc_zero_array(r2, uint8_t, r2->in.cOut);
+ if (r2->out.pOut == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncPlayGdiScriptOnPrinterIC(p, r2);
+ break;
+ }
+ case 37: { /* winspool_AsyncDeletePrinterIC */
+ struct winspool_AsyncDeletePrinterIC *r2 = (struct winspool_AsyncDeletePrinterIC *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterIC, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.phPrinterIC = r2->in.phPrinterIC;
+ r2->out.result = _winspool_AsyncDeletePrinterIC(p, r2);
+ break;
+ }
+ case 38: { /* winspool_AsyncEnumPrinters */
+ struct winspool_AsyncEnumPrinters *r2 = (struct winspool_AsyncEnumPrinters *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinters, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pPrinterEnum = r2->in.pPrinterEnum;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrinters(p, r2);
+ break;
+ }
+ case 39: { /* winspool_AsyncAddPrinterDriver */
+ struct winspool_AsyncAddPrinterDriver *r2 = (struct winspool_AsyncAddPrinterDriver *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPrinterDriver, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAddPrinterDriver(p, r2);
+ break;
+ }
+ case 40: { /* winspool_AsyncEnumPrinterDrivers */
+ struct winspool_AsyncEnumPrinterDrivers *r2 = (struct winspool_AsyncEnumPrinterDrivers *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterDrivers, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pDrivers = r2->in.pDrivers;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrinterDrivers(p, r2);
+ break;
+ }
+ case 41: { /* winspool_AsyncGetPrinterDriverDirectory */
+ struct winspool_AsyncGetPrinterDriverDirectory *r2 = (struct winspool_AsyncGetPrinterDriverDirectory *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDriverDirectory, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pDriverDirectory = r2->in.pDriverDirectory;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrinterDriverDirectory(p, r2);
+ break;
+ }
+ case 42: { /* winspool_AsyncDeletePrinterDriver */
+ struct winspool_AsyncDeletePrinterDriver *r2 = (struct winspool_AsyncDeletePrinterDriver *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDriver, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinterDriver(p, r2);
+ break;
+ }
+ case 43: { /* winspool_AsyncDeletePrinterDriverEx */
+ struct winspool_AsyncDeletePrinterDriverEx *r2 = (struct winspool_AsyncDeletePrinterDriverEx *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDriverEx, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinterDriverEx(p, r2);
+ break;
+ }
+ case 44: { /* winspool_AsyncAddPrintProcessor */
+ struct winspool_AsyncAddPrintProcessor *r2 = (struct winspool_AsyncAddPrintProcessor *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPrintProcessor, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAddPrintProcessor(p, r2);
+ break;
+ }
+ case 45: { /* winspool_AsyncEnumPrintProcessors */
+ struct winspool_AsyncEnumPrintProcessors *r2 = (struct winspool_AsyncEnumPrintProcessors *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrintProcessors, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pPrintProcessorInfo = r2->in.pPrintProcessorInfo;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrintProcessors(p, r2);
+ break;
+ }
+ case 46: { /* winspool_AsyncGetPrintProcessorDirectory */
+ struct winspool_AsyncGetPrintProcessorDirectory *r2 = (struct winspool_AsyncGetPrintProcessorDirectory *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrintProcessorDirectory, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pPrintProcessorDirectory = r2->in.pPrintProcessorDirectory;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrintProcessorDirectory(p, r2);
+ break;
+ }
+ case 47: { /* winspool_AsyncEnumPorts */
+ struct winspool_AsyncEnumPorts *r2 = (struct winspool_AsyncEnumPorts *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPorts, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pPort = r2->in.pPort;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPorts(p, r2);
+ break;
+ }
+ case 48: { /* winspool_AsyncEnumMonitors */
+ struct winspool_AsyncEnumMonitors *r2 = (struct winspool_AsyncEnumMonitors *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumMonitors, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pMonitor = r2->in.pMonitor;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumMonitors(p, r2);
+ break;
+ }
+ case 49: { /* winspool_AsyncAddPort */
+ struct winspool_AsyncAddPort *r2 = (struct winspool_AsyncAddPort *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPort, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAddPort(p, r2);
+ break;
+ }
+ case 50: { /* winspool_AsyncSetPort */
+ struct winspool_AsyncSetPort *r2 = (struct winspool_AsyncSetPort *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPort, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetPort(p, r2);
+ break;
+ }
+ case 51: { /* winspool_AsyncAddMonitor */
+ struct winspool_AsyncAddMonitor *r2 = (struct winspool_AsyncAddMonitor *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddMonitor, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAddMonitor(p, r2);
+ break;
+ }
+ case 52: { /* winspool_AsyncDeleteMonitor */
+ struct winspool_AsyncDeleteMonitor *r2 = (struct winspool_AsyncDeleteMonitor *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeleteMonitor, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeleteMonitor(p, r2);
+ break;
+ }
+ case 53: { /* winspool_AsyncDeletePrintProcessor */
+ struct winspool_AsyncDeletePrintProcessor *r2 = (struct winspool_AsyncDeletePrintProcessor *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrintProcessor, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrintProcessor(p, r2);
+ break;
+ }
+ case 54: { /* winspool_AsyncEnumPrintProcessorDatatypes */
+ struct winspool_AsyncEnumPrintProcessorDatatypes *r2 = (struct winspool_AsyncEnumPrintProcessorDatatypes *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrintProcessorDatatypes, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pDatatypes = r2->in.pDatatypes;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPrintProcessorDatatypes(p, r2);
+ break;
+ }
+ case 55: { /* winspool_AsyncAddPerMachineConnection */
+ struct winspool_AsyncAddPerMachineConnection *r2 = (struct winspool_AsyncAddPerMachineConnection *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPerMachineConnection, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncAddPerMachineConnection(p, r2);
+ break;
+ }
+ case 56: { /* winspool_AsyncDeletePerMachineConnection */
+ struct winspool_AsyncDeletePerMachineConnection *r2 = (struct winspool_AsyncDeletePerMachineConnection *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePerMachineConnection, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePerMachineConnection(p, r2);
+ break;
+ }
+ case 57: { /* winspool_AsyncEnumPerMachineConnections */
+ struct winspool_AsyncEnumPerMachineConnections *r2 = (struct winspool_AsyncEnumPerMachineConnections *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPerMachineConnections, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pPrinterEnum = r2->in.pPrinterEnum;
+ r2->out.pcbNeeded = talloc_zero(r2, uint32_t);
+ if (r2->out.pcbNeeded == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcReturned = talloc_zero(r2, uint32_t);
+ if (r2->out.pcReturned == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumPerMachineConnections(p, r2);
+ break;
+ }
+ case 58: { /* winspool_SyncRegisterForRemoteNotifications */
+ struct winspool_SyncRegisterForRemoteNotifications *r2 = (struct winspool_SyncRegisterForRemoteNotifications *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_SyncRegisterForRemoteNotifications, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.phRpcHandle = talloc_zero(r2, struct policy_handle);
+ if (r2->out.phRpcHandle == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_SyncRegisterForRemoteNotifications(p, r2);
+ break;
+ }
+ case 59: { /* winspool_SyncUnRegisterForRemoteNotifications */
+ struct winspool_SyncUnRegisterForRemoteNotifications *r2 = (struct winspool_SyncUnRegisterForRemoteNotifications *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_SyncUnRegisterForRemoteNotifications, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.phRpcHandle = r2->in.phRpcHandle;
+ r2->out.result = _winspool_SyncUnRegisterForRemoteNotifications(p, r2);
+ break;
+ }
+ case 60: { /* winspool_SyncRefreshRemoteNotifications */
+ struct winspool_SyncRefreshRemoteNotifications *r2 = (struct winspool_SyncRefreshRemoteNotifications *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_SyncRefreshRemoteNotifications, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.ppNotifyData = talloc_zero(r2, struct winspool_PrintPropertiesCollection *);
+ if (r2->out.ppNotifyData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_SyncRefreshRemoteNotifications(p, r2);
+ break;
+ }
+ case 61: { /* winspool_AsyncGetRemoteNotifications */
+ struct winspool_AsyncGetRemoteNotifications *r2 = (struct winspool_AsyncGetRemoteNotifications *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetRemoteNotifications, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.ppNotifyData = talloc_zero(r2, struct winspool_PrintPropertiesCollection *);
+ if (r2->out.ppNotifyData == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetRemoteNotifications(p, r2);
+ break;
+ }
+ case 62: { /* winspool_AsyncInstallPrinterDriverFromPackage */
+ struct winspool_AsyncInstallPrinterDriverFromPackage *r2 = (struct winspool_AsyncInstallPrinterDriverFromPackage *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncInstallPrinterDriverFromPackage, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncInstallPrinterDriverFromPackage(p, r2);
+ break;
+ }
+ case 63: { /* winspool_AsyncUploadPrinterDriverPackage */
+ struct winspool_AsyncUploadPrinterDriverPackage *r2 = (struct winspool_AsyncUploadPrinterDriverPackage *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncUploadPrinterDriverPackage, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pszDestInfPath = r2->in.pszDestInfPath;
+ r2->out.pcchDestInfPath = r2->in.pcchDestInfPath;
+ r2->out.result = _winspool_AsyncUploadPrinterDriverPackage(p, r2);
+ break;
+ }
+ case 64: { /* winspool_AsyncGetCorePrinterDrivers */
+ struct winspool_AsyncGetCorePrinterDrivers *r2 = (struct winspool_AsyncGetCorePrinterDrivers *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetCorePrinterDrivers, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pCorePrinterDrivers = talloc_zero_array(r2, struct spoolss_CorePrinterDriver, r2->in.cCorePrinterDrivers);
+ if (r2->out.pCorePrinterDrivers == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetCorePrinterDrivers(p, r2);
+ break;
+ }
+ case 65: { /* winspool_AsyncCorePrinterDriverInstalled */
+ struct winspool_AsyncCorePrinterDriverInstalled *r2 = (struct winspool_AsyncCorePrinterDriverInstalled *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncCorePrinterDriverInstalled, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pbDriverInstalled = talloc_zero(r2, int32_t);
+ if (r2->out.pbDriverInstalled == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncCorePrinterDriverInstalled(p, r2);
+ break;
+ }
+ case 66: { /* winspool_AsyncGetPrinterDriverPackagePath */
+ struct winspool_AsyncGetPrinterDriverPackagePath *r2 = (struct winspool_AsyncGetPrinterDriverPackagePath *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDriverPackagePath, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pszDriverPackageCab = r2->in.pszDriverPackageCab;
+ r2->out.pcchRequiredSize = talloc_zero(r2, uint32_t);
+ if (r2->out.pcchRequiredSize == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetPrinterDriverPackagePath(p, r2);
+ break;
+ }
+ case 67: { /* winspool_AsyncDeletePrinterDriverPackage */
+ struct winspool_AsyncDeletePrinterDriverPackage *r2 = (struct winspool_AsyncDeletePrinterDriverPackage *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDriverPackage, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeletePrinterDriverPackage(p, r2);
+ break;
+ }
+ case 68: { /* winspool_AsyncReadPrinter */
+ struct winspool_AsyncReadPrinter *r2 = (struct winspool_AsyncReadPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncReadPrinter, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pBuf = talloc_zero_array(r2, uint8_t, r2->in.cbBuf);
+ if (r2->out.pBuf == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.pcNoBytesRead = talloc_zero(r2, uint32_t);
+ if (r2->out.pcNoBytesRead == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncReadPrinter(p, r2);
+ break;
+ }
+ case 69: { /* winspool_AsyncResetPrinter */
+ struct winspool_AsyncResetPrinter *r2 = (struct winspool_AsyncResetPrinter *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncResetPrinter, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncResetPrinter(p, r2);
+ break;
+ }
+ case 70: { /* winspool_AsyncGetJobNamedPropertyValue */
+ struct winspool_AsyncGetJobNamedPropertyValue *r2 = (struct winspool_AsyncGetJobNamedPropertyValue *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetJobNamedPropertyValue, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pValue = talloc_zero(r2, struct spoolss_PrintPropertyValue);
+ if (r2->out.pValue == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncGetJobNamedPropertyValue(p, r2);
+ break;
+ }
+ case 71: { /* winspool_AsyncSetJobNamedProperty */
+ struct winspool_AsyncSetJobNamedProperty *r2 = (struct winspool_AsyncSetJobNamedProperty *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetJobNamedProperty, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncSetJobNamedProperty(p, r2);
+ break;
+ }
+ case 72: { /* winspool_AsyncDeleteJobNamedProperty */
+ struct winspool_AsyncDeleteJobNamedProperty *r2 = (struct winspool_AsyncDeleteJobNamedProperty *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeleteJobNamedProperty, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncDeleteJobNamedProperty(p, r2);
+ break;
+ }
+ case 73: { /* winspool_AsyncEnumJobNamedProperties */
+ struct winspool_AsyncEnumJobNamedProperties *r2 = (struct winspool_AsyncEnumJobNamedProperties *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumJobNamedProperties, NDR_IN, r2);
+ }
+ NDR_ZERO_STRUCT(r2->out);
+ r2->out.pcProperties = talloc_zero(r2, uint32_t);
+ if (r2->out.pcProperties == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.ppProperties = talloc_zero(r2, struct spoolss_PrintNamedProperty *);
+ if (r2->out.ppProperties == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ p->fault_state = DCERPC_FAULT_CANT_PERFORM;
+ goto fail;
+ }
+
+ r2->out.result = _winspool_AsyncEnumJobNamedProperties(p, r2);
+ break;
+ }
+ case 74: { /* winspool_AsyncLogJobInfoForBranchOffice */
+ struct winspool_AsyncLogJobInfoForBranchOffice *r2 = (struct winspool_AsyncLogJobInfoForBranchOffice *)r;
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncLogJobInfoForBranchOffice, NDR_IN, r2);
+ }
+ r2->out.result = _winspool_AsyncLogJobInfoForBranchOffice(p, r2);
+ break;
+ }
+ default:
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ break;
+ }
+
+fail:
+ /* Unimpersonate */
+ if (impersonated) {
+ unbecome_authenticated_pipe_user();
+ }
+
+ p->dce_call = NULL;
+ p->mem_ctx = NULL;
+ /* Check pipes struct fault state */
+ if (p->fault_state != 0) {
+ dce_call->fault_code = p->fault_state;
+ }
+ if (dce_call->fault_code != 0) {
+ status = NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return status;
+}
+
+NTSTATUS iremotewinspool__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ return iremotewinspool__op_dispatch_internal(dce_call, mem_ctx, r, false);
+}
+
+NTSTATUS iremotewinspool__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ if (forward_opnum_to_spoolss(opnum)) {
+ return spoolss__op_reply(dce_call, mem_ctx, r);
+ }
+
+ switch (opnum) {
+ case 0: { /* winspool_AsyncOpenPrinter */
+ struct winspool_AsyncOpenPrinter *r2 = (struct winspool_AsyncOpenPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncOpenPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncOpenPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncOpenPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 1: { /* winspool_AsyncAddPrinter */
+ struct winspool_AsyncAddPrinter *r2 = (struct winspool_AsyncAddPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 2: { /* winspool_AsyncSetJob */
+ struct winspool_AsyncSetJob *r2 = (struct winspool_AsyncSetJob *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetJob replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetJob, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetJob\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 3: { /* winspool_AsyncGetJob */
+ struct winspool_AsyncGetJob *r2 = (struct winspool_AsyncGetJob *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetJob replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetJob, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetJob\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 4: { /* winspool_AsyncEnumJobs */
+ struct winspool_AsyncEnumJobs *r2 = (struct winspool_AsyncEnumJobs *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumJobs replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumJobs, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumJobs\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 5: { /* winspool_AsyncAddJob */
+ struct winspool_AsyncAddJob *r2 = (struct winspool_AsyncAddJob *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddJob replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddJob, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddJob\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 6: { /* winspool_AsyncScheduleJob */
+ struct winspool_AsyncScheduleJob *r2 = (struct winspool_AsyncScheduleJob *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncScheduleJob replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncScheduleJob, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncScheduleJob\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 7: { /* winspool_AsyncDeletePrinter */
+ struct winspool_AsyncDeletePrinter *r2 = (struct winspool_AsyncDeletePrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 8: { /* winspool_AsyncSetPrinter */
+ struct winspool_AsyncSetPrinter *r2 = (struct winspool_AsyncSetPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 9: { /* winspool_AsyncGetPrinter */
+ struct winspool_AsyncGetPrinter *r2 = (struct winspool_AsyncGetPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 10: { /* winspool_AsyncStartDocPrinter */
+ struct winspool_AsyncStartDocPrinter *r2 = (struct winspool_AsyncStartDocPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncStartDocPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncStartDocPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncStartDocPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 11: { /* winspool_AsyncStartPagePrinter */
+ struct winspool_AsyncStartPagePrinter *r2 = (struct winspool_AsyncStartPagePrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncStartPagePrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncStartPagePrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncStartPagePrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 12: { /* winspool_AsyncWritePrinter */
+ struct winspool_AsyncWritePrinter *r2 = (struct winspool_AsyncWritePrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncWritePrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncWritePrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncWritePrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 13: { /* winspool_AsyncEndPagePrinter */
+ struct winspool_AsyncEndPagePrinter *r2 = (struct winspool_AsyncEndPagePrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEndPagePrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEndPagePrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEndPagePrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 14: { /* winspool_AsyncEndDocPrinter */
+ struct winspool_AsyncEndDocPrinter *r2 = (struct winspool_AsyncEndDocPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEndDocPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEndDocPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEndDocPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 15: { /* winspool_AsyncAbortPrinter */
+ struct winspool_AsyncAbortPrinter *r2 = (struct winspool_AsyncAbortPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAbortPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAbortPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAbortPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 16: { /* winspool_AsyncGetPrinterData */
+ struct winspool_AsyncGetPrinterData *r2 = (struct winspool_AsyncGetPrinterData *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrinterData replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterData, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrinterData\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 17: { /* winspool_AsyncGetPrinterDataEx */
+ struct winspool_AsyncGetPrinterDataEx *r2 = (struct winspool_AsyncGetPrinterDataEx *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrinterDataEx replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDataEx, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrinterDataEx\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 18: { /* winspool_AsyncSetPrinterData */
+ struct winspool_AsyncSetPrinterData *r2 = (struct winspool_AsyncSetPrinterData *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetPrinterData replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPrinterData, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetPrinterData\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 19: { /* winspool_AsyncSetPrinterDataEx */
+ struct winspool_AsyncSetPrinterDataEx *r2 = (struct winspool_AsyncSetPrinterDataEx *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetPrinterDataEx replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPrinterDataEx, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetPrinterDataEx\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 20: { /* winspool_AsyncClosePrinter */
+ struct winspool_AsyncClosePrinter *r2 = (struct winspool_AsyncClosePrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncClosePrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncClosePrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncClosePrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 21: { /* winspool_AsyncAddForm */
+ struct winspool_AsyncAddForm *r2 = (struct winspool_AsyncAddForm *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddForm replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddForm, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddForm\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 22: { /* winspool_AsyncDeleteForm */
+ struct winspool_AsyncDeleteForm *r2 = (struct winspool_AsyncDeleteForm *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeleteForm replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeleteForm, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeleteForm\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 23: { /* winspool_AsyncGetForm */
+ struct winspool_AsyncGetForm *r2 = (struct winspool_AsyncGetForm *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetForm replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetForm, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetForm\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 24: { /* winspool_AsyncSetForm */
+ struct winspool_AsyncSetForm *r2 = (struct winspool_AsyncSetForm *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetForm replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetForm, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetForm\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 25: { /* winspool_AsyncEnumForms */
+ struct winspool_AsyncEnumForms *r2 = (struct winspool_AsyncEnumForms *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumForms replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumForms, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumForms\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 26: { /* winspool_AsyncGetPrinterDriver */
+ struct winspool_AsyncGetPrinterDriver *r2 = (struct winspool_AsyncGetPrinterDriver *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrinterDriver replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDriver, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrinterDriver\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 27: { /* winspool_AsyncEnumPrinterData */
+ struct winspool_AsyncEnumPrinterData *r2 = (struct winspool_AsyncEnumPrinterData *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrinterData replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterData, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrinterData\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 28: { /* winspool_AsyncEnumPrinterDataEx */
+ struct winspool_AsyncEnumPrinterDataEx *r2 = (struct winspool_AsyncEnumPrinterDataEx *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrinterDataEx replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterDataEx, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrinterDataEx\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 29: { /* winspool_AsyncEnumPrinterKey */
+ struct winspool_AsyncEnumPrinterKey *r2 = (struct winspool_AsyncEnumPrinterKey *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrinterKey replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterKey, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrinterKey\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 30: { /* winspool_AsyncDeletePrinterData */
+ struct winspool_AsyncDeletePrinterData *r2 = (struct winspool_AsyncDeletePrinterData *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterData replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterData, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterData\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 31: { /* winspool_AsyncDeletePrinterDataEx */
+ struct winspool_AsyncDeletePrinterDataEx *r2 = (struct winspool_AsyncDeletePrinterDataEx *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterDataEx replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDataEx, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterDataEx\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 32: { /* winspool_AsyncDeletePrinterKey */
+ struct winspool_AsyncDeletePrinterKey *r2 = (struct winspool_AsyncDeletePrinterKey *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterKey replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterKey, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterKey\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 33: { /* winspool_AsyncXcvData */
+ struct winspool_AsyncXcvData *r2 = (struct winspool_AsyncXcvData *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncXcvData replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncXcvData, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncXcvData\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 34: { /* winspool_AsyncSendRecvBidiData */
+ struct winspool_AsyncSendRecvBidiData *r2 = (struct winspool_AsyncSendRecvBidiData *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSendRecvBidiData replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSendRecvBidiData, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSendRecvBidiData\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 35: { /* winspool_AsyncCreatePrinterIC */
+ struct winspool_AsyncCreatePrinterIC *r2 = (struct winspool_AsyncCreatePrinterIC *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncCreatePrinterIC replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncCreatePrinterIC, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncCreatePrinterIC\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 36: { /* winspool_AsyncPlayGdiScriptOnPrinterIC */
+ struct winspool_AsyncPlayGdiScriptOnPrinterIC *r2 = (struct winspool_AsyncPlayGdiScriptOnPrinterIC *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncPlayGdiScriptOnPrinterIC replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncPlayGdiScriptOnPrinterIC, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncPlayGdiScriptOnPrinterIC\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 37: { /* winspool_AsyncDeletePrinterIC */
+ struct winspool_AsyncDeletePrinterIC *r2 = (struct winspool_AsyncDeletePrinterIC *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterIC replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterIC, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterIC\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 38: { /* winspool_AsyncEnumPrinters */
+ struct winspool_AsyncEnumPrinters *r2 = (struct winspool_AsyncEnumPrinters *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrinters replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinters, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrinters\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 39: { /* winspool_AsyncAddPrinterDriver */
+ struct winspool_AsyncAddPrinterDriver *r2 = (struct winspool_AsyncAddPrinterDriver *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddPrinterDriver replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPrinterDriver, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddPrinterDriver\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 40: { /* winspool_AsyncEnumPrinterDrivers */
+ struct winspool_AsyncEnumPrinterDrivers *r2 = (struct winspool_AsyncEnumPrinterDrivers *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrinterDrivers replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrinterDrivers, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrinterDrivers\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 41: { /* winspool_AsyncGetPrinterDriverDirectory */
+ struct winspool_AsyncGetPrinterDriverDirectory *r2 = (struct winspool_AsyncGetPrinterDriverDirectory *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrinterDriverDirectory replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDriverDirectory, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrinterDriverDirectory\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 42: { /* winspool_AsyncDeletePrinterDriver */
+ struct winspool_AsyncDeletePrinterDriver *r2 = (struct winspool_AsyncDeletePrinterDriver *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterDriver replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDriver, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterDriver\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 43: { /* winspool_AsyncDeletePrinterDriverEx */
+ struct winspool_AsyncDeletePrinterDriverEx *r2 = (struct winspool_AsyncDeletePrinterDriverEx *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterDriverEx replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDriverEx, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterDriverEx\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 44: { /* winspool_AsyncAddPrintProcessor */
+ struct winspool_AsyncAddPrintProcessor *r2 = (struct winspool_AsyncAddPrintProcessor *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddPrintProcessor replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPrintProcessor, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddPrintProcessor\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 45: { /* winspool_AsyncEnumPrintProcessors */
+ struct winspool_AsyncEnumPrintProcessors *r2 = (struct winspool_AsyncEnumPrintProcessors *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrintProcessors replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrintProcessors, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrintProcessors\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 46: { /* winspool_AsyncGetPrintProcessorDirectory */
+ struct winspool_AsyncGetPrintProcessorDirectory *r2 = (struct winspool_AsyncGetPrintProcessorDirectory *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrintProcessorDirectory replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrintProcessorDirectory, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrintProcessorDirectory\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 47: { /* winspool_AsyncEnumPorts */
+ struct winspool_AsyncEnumPorts *r2 = (struct winspool_AsyncEnumPorts *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPorts replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPorts, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPorts\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 48: { /* winspool_AsyncEnumMonitors */
+ struct winspool_AsyncEnumMonitors *r2 = (struct winspool_AsyncEnumMonitors *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumMonitors replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumMonitors, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumMonitors\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 49: { /* winspool_AsyncAddPort */
+ struct winspool_AsyncAddPort *r2 = (struct winspool_AsyncAddPort *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddPort replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPort, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddPort\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 50: { /* winspool_AsyncSetPort */
+ struct winspool_AsyncSetPort *r2 = (struct winspool_AsyncSetPort *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetPort replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetPort, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetPort\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 51: { /* winspool_AsyncAddMonitor */
+ struct winspool_AsyncAddMonitor *r2 = (struct winspool_AsyncAddMonitor *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddMonitor replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddMonitor, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddMonitor\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 52: { /* winspool_AsyncDeleteMonitor */
+ struct winspool_AsyncDeleteMonitor *r2 = (struct winspool_AsyncDeleteMonitor *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeleteMonitor replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeleteMonitor, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeleteMonitor\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 53: { /* winspool_AsyncDeletePrintProcessor */
+ struct winspool_AsyncDeletePrintProcessor *r2 = (struct winspool_AsyncDeletePrintProcessor *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrintProcessor replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrintProcessor, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrintProcessor\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 54: { /* winspool_AsyncEnumPrintProcessorDatatypes */
+ struct winspool_AsyncEnumPrintProcessorDatatypes *r2 = (struct winspool_AsyncEnumPrintProcessorDatatypes *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPrintProcessorDatatypes replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPrintProcessorDatatypes, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPrintProcessorDatatypes\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 55: { /* winspool_AsyncAddPerMachineConnection */
+ struct winspool_AsyncAddPerMachineConnection *r2 = (struct winspool_AsyncAddPerMachineConnection *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncAddPerMachineConnection replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncAddPerMachineConnection, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncAddPerMachineConnection\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 56: { /* winspool_AsyncDeletePerMachineConnection */
+ struct winspool_AsyncDeletePerMachineConnection *r2 = (struct winspool_AsyncDeletePerMachineConnection *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePerMachineConnection replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePerMachineConnection, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePerMachineConnection\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 57: { /* winspool_AsyncEnumPerMachineConnections */
+ struct winspool_AsyncEnumPerMachineConnections *r2 = (struct winspool_AsyncEnumPerMachineConnections *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumPerMachineConnections replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumPerMachineConnections, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumPerMachineConnections\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 58: { /* winspool_SyncRegisterForRemoteNotifications */
+ struct winspool_SyncRegisterForRemoteNotifications *r2 = (struct winspool_SyncRegisterForRemoteNotifications *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_SyncRegisterForRemoteNotifications replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_SyncRegisterForRemoteNotifications, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_SyncRegisterForRemoteNotifications\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 59: { /* winspool_SyncUnRegisterForRemoteNotifications */
+ struct winspool_SyncUnRegisterForRemoteNotifications *r2 = (struct winspool_SyncUnRegisterForRemoteNotifications *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_SyncUnRegisterForRemoteNotifications replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_SyncUnRegisterForRemoteNotifications, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_SyncUnRegisterForRemoteNotifications\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 60: { /* winspool_SyncRefreshRemoteNotifications */
+ struct winspool_SyncRefreshRemoteNotifications *r2 = (struct winspool_SyncRefreshRemoteNotifications *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_SyncRefreshRemoteNotifications replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_SyncRefreshRemoteNotifications, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_SyncRefreshRemoteNotifications\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 61: { /* winspool_AsyncGetRemoteNotifications */
+ struct winspool_AsyncGetRemoteNotifications *r2 = (struct winspool_AsyncGetRemoteNotifications *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetRemoteNotifications replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetRemoteNotifications, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetRemoteNotifications\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 62: { /* winspool_AsyncInstallPrinterDriverFromPackage */
+ struct winspool_AsyncInstallPrinterDriverFromPackage *r2 = (struct winspool_AsyncInstallPrinterDriverFromPackage *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncInstallPrinterDriverFromPackage replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncInstallPrinterDriverFromPackage, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncInstallPrinterDriverFromPackage\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 63: { /* winspool_AsyncUploadPrinterDriverPackage */
+ struct winspool_AsyncUploadPrinterDriverPackage *r2 = (struct winspool_AsyncUploadPrinterDriverPackage *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncUploadPrinterDriverPackage replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncUploadPrinterDriverPackage, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncUploadPrinterDriverPackage\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 64: { /* winspool_AsyncGetCorePrinterDrivers */
+ struct winspool_AsyncGetCorePrinterDrivers *r2 = (struct winspool_AsyncGetCorePrinterDrivers *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetCorePrinterDrivers replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetCorePrinterDrivers, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetCorePrinterDrivers\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 65: { /* winspool_AsyncCorePrinterDriverInstalled */
+ struct winspool_AsyncCorePrinterDriverInstalled *r2 = (struct winspool_AsyncCorePrinterDriverInstalled *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncCorePrinterDriverInstalled replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncCorePrinterDriverInstalled, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncCorePrinterDriverInstalled\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 66: { /* winspool_AsyncGetPrinterDriverPackagePath */
+ struct winspool_AsyncGetPrinterDriverPackagePath *r2 = (struct winspool_AsyncGetPrinterDriverPackagePath *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetPrinterDriverPackagePath replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetPrinterDriverPackagePath, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetPrinterDriverPackagePath\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 67: { /* winspool_AsyncDeletePrinterDriverPackage */
+ struct winspool_AsyncDeletePrinterDriverPackage *r2 = (struct winspool_AsyncDeletePrinterDriverPackage *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeletePrinterDriverPackage replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeletePrinterDriverPackage, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeletePrinterDriverPackage\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 68: { /* winspool_AsyncReadPrinter */
+ struct winspool_AsyncReadPrinter *r2 = (struct winspool_AsyncReadPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncReadPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncReadPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncReadPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 69: { /* winspool_AsyncResetPrinter */
+ struct winspool_AsyncResetPrinter *r2 = (struct winspool_AsyncResetPrinter *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncResetPrinter replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncResetPrinter, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncResetPrinter\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 70: { /* winspool_AsyncGetJobNamedPropertyValue */
+ struct winspool_AsyncGetJobNamedPropertyValue *r2 = (struct winspool_AsyncGetJobNamedPropertyValue *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncGetJobNamedPropertyValue replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncGetJobNamedPropertyValue, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncGetJobNamedPropertyValue\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 71: { /* winspool_AsyncSetJobNamedProperty */
+ struct winspool_AsyncSetJobNamedProperty *r2 = (struct winspool_AsyncSetJobNamedProperty *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncSetJobNamedProperty replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncSetJobNamedProperty, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncSetJobNamedProperty\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 72: { /* winspool_AsyncDeleteJobNamedProperty */
+ struct winspool_AsyncDeleteJobNamedProperty *r2 = (struct winspool_AsyncDeleteJobNamedProperty *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncDeleteJobNamedProperty replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncDeleteJobNamedProperty, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncDeleteJobNamedProperty\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 73: { /* winspool_AsyncEnumJobNamedProperties */
+ struct winspool_AsyncEnumJobNamedProperties *r2 = (struct winspool_AsyncEnumJobNamedProperties *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncEnumJobNamedProperties replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncEnumJobNamedProperties, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncEnumJobNamedProperties\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ case 74: { /* winspool_AsyncLogJobInfoForBranchOffice */
+ struct winspool_AsyncLogJobInfoForBranchOffice *r2 = (struct winspool_AsyncLogJobInfoForBranchOffice *)r;
+ if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ DEBUG(5,("function winspool_AsyncLogJobInfoForBranchOffice replied async\n"));
+ }
+ if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {
+ NDR_PRINT_FUNCTION_DEBUG(winspool_AsyncLogJobInfoForBranchOffice, NDR_OUT | NDR_SET_VALUES, r2);
+ }
+ if (dce_call->fault_code != 0) {
+ DBG_WARNING("dcerpc_fault %s in winspool_AsyncLogJobInfoForBranchOffice\n", dcerpc_errstr(mem_ctx, dce_call->fault_code));
+ }
+ break;
+ }
+ default:
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ break;
+ }
+
+ if (dce_call->fault_code != 0) {
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS iremotewinspool__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;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ if (forward_opnum_to_spoolss(opnum)) {
+ return spoolss__op_ndr_push(dce_call, mem_ctx, push, r);
+ }
+
+ ndr_err = ndr_table_iremotewinspool.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;
+}
+
+NTSTATUS iremotewinspool__op_local(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ return iremotewinspool__op_dispatch_internal(dce_call, mem_ctx, r, true);
+}
+
+static const struct dcesrv_interface dcesrv_iremotewinspool_interface = {
+ .name = "iremotewinspool",
+ .syntax_id = {{0x76f03f96,0xcdfd,0x44fc,{0xa2,0x2c},{0x64,0x95,0x0a,0x00,0x12,0x09}},1.0},
+ .bind = iremotewinspool__op_bind,
+ .unbind = iremotewinspool__op_unbind,
+ .ndr_pull = iremotewinspool__op_ndr_pull,
+ .dispatch = iremotewinspool__op_dispatch,
+ .reply = iremotewinspool__op_reply,
+ .ndr_push = iremotewinspool__op_ndr_push,
+ .local = iremotewinspool__op_local,
+#ifdef DCESRV_INTERFACE_IREMOTEWINSPOOL_FLAGS
+ .flags = DCESRV_INTERFACE_IREMOTEWINSPOOL_FLAGS
+#else
+ .flags = 0
+#endif
+};
+
+static NTSTATUS iremotewinspool__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
+{
+ int i;
+ NTSTATUS ret;
+
+#ifdef DCESRV_INTERFACE_IREMOTEWINSPOOL_NCACN_NP_SECONDARY_ENDPOINT
+ const char *ncacn_np_secondary_endpoint = DCESRV_INTERFACE_IREMOTEWINSPOOL_NCACN_NP_SECONDARY_ENDPOINT;
+#else
+ const char *ncacn_np_secondary_endpoint = NULL;
+#endif
+
+ for (i=0;i<ndr_table_iremotewinspool.endpoints->count;i++) {
+ const char *name = ndr_table_iremotewinspool.endpoints->names[i];
+
+ ret = dcesrv_interface_register(dce_ctx, name, ncacn_np_secondary_endpoint, &dcesrv_iremotewinspool_interface, NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DBG_ERR("Failed to register endpoint '%s'\n",name);
+ return ret;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS iremotewinspool__op_shutdown_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
+{
+ return NT_STATUS_OK;
+}
+
+static bool iremotewinspool__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version)
+{
+ if (dcesrv_iremotewinspool_interface.syntax_id.if_version == if_version && GUID_equal(&dcesrv_iremotewinspool_interface.syntax_id.uuid, uuid)) {
+ memcpy(iface,&dcesrv_iremotewinspool_interface, sizeof(*iface));
+ return true;
+ }
+
+ return false;
+}
+
+static bool iremotewinspool__op_interface_by_name(struct dcesrv_interface *iface, const char *name)
+{
+ if (strcmp(dcesrv_iremotewinspool_interface.name, name)==0) {
+ memcpy(iface, &dcesrv_iremotewinspool_interface, sizeof(*iface));
+ return true;
+ }
+
+ return false;
+}
+
+static const struct dcesrv_endpoint_server iremotewinspool_ep_server = {
+ /* fill in our name */
+ .name = "iremotewinspool",
+
+ /* Initialization flag */
+ .initialized = false,
+
+ /* fill in all the operations */
+#ifdef DCESRV_INTERFACE_IREMOTEWINSPOOL_INIT_SERVER
+ .init_server = DCESRV_INTERFACE_IREMOTEWINSPOOL_INIT_SERVER,
+#else
+ .init_server = iremotewinspool__op_init_server,
+#endif
+#ifdef DCESRV_INTERFACE_IREMOTEWINSPOOL_SHUTDOWN_SERVER
+ .shutdown_server = DCESRV_INTERFACE_IREMOTEWINSPOOL_SHUTDOWN_SERVER,
+#else
+ .shutdown_server = iremotewinspool__op_shutdown_server,
+#endif
+ .interface_by_uuid = iremotewinspool__op_interface_by_uuid,
+ .interface_by_name = iremotewinspool__op_interface_by_name
+};
+
+const struct dcesrv_endpoint_server *iremotewinspool_get_ep_server(void)
+{
+ return &iremotewinspool_ep_server;
+}
diff --git a/source3/rpc_server/spoolss/srv_iremotewinspool_nt.c b/source3/rpc_server/spoolss/srv_iremotewinspool_nt.c
new file mode 100644
index 0000000..c437192
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_iremotewinspool_nt.c
@@ -0,0 +1,924 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the iremotewinspool pipe
+
+ Copyright (C) YOUR NAME HERE YEAR
+
+ 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 "ntdomain.h"
+#include "librpc/gen_ndr/ndr_winspool.h"
+#include "librpc/gen_ndr/ndr_winspool_scompat.h"
+
+/****************************************************************
+ _winspool_AsyncOpenPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncOpenPrinter(struct pipes_struct *p,
+ struct winspool_AsyncOpenPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncAddPrinter(struct pipes_struct *p,
+ struct winspool_AsyncAddPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetJob
+****************************************************************/
+
+WERROR _winspool_AsyncSetJob(struct pipes_struct *p,
+ struct winspool_AsyncSetJob *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetJob
+****************************************************************/
+
+WERROR _winspool_AsyncGetJob(struct pipes_struct *p,
+ struct winspool_AsyncGetJob *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumJobs
+****************************************************************/
+
+WERROR _winspool_AsyncEnumJobs(struct pipes_struct *p,
+ struct winspool_AsyncEnumJobs *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddJob
+****************************************************************/
+
+WERROR _winspool_AsyncAddJob(struct pipes_struct *p,
+ struct winspool_AsyncAddJob *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncScheduleJob
+****************************************************************/
+
+WERROR _winspool_AsyncScheduleJob(struct pipes_struct *p,
+ struct winspool_AsyncScheduleJob *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinter
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinter(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncSetPrinter(struct pipes_struct *p,
+ struct winspool_AsyncSetPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncGetPrinter(struct pipes_struct *p,
+ struct winspool_AsyncGetPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncStartDocPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncStartDocPrinter(struct pipes_struct *p,
+ struct winspool_AsyncStartDocPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncStartPagePrinter
+****************************************************************/
+
+WERROR _winspool_AsyncStartPagePrinter(struct pipes_struct *p,
+ struct winspool_AsyncStartPagePrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncWritePrinter
+****************************************************************/
+
+WERROR _winspool_AsyncWritePrinter(struct pipes_struct *p,
+ struct winspool_AsyncWritePrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEndPagePrinter
+****************************************************************/
+
+WERROR _winspool_AsyncEndPagePrinter(struct pipes_struct *p,
+ struct winspool_AsyncEndPagePrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEndDocPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncEndDocPrinter(struct pipes_struct *p,
+ struct winspool_AsyncEndDocPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAbortPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncAbortPrinter(struct pipes_struct *p,
+ struct winspool_AsyncAbortPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrinterData
+****************************************************************/
+
+WERROR _winspool_AsyncGetPrinterData(struct pipes_struct *p,
+ struct winspool_AsyncGetPrinterData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrinterDataEx
+****************************************************************/
+
+WERROR _winspool_AsyncGetPrinterDataEx(struct pipes_struct *p,
+ struct winspool_AsyncGetPrinterDataEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetPrinterData
+****************************************************************/
+
+WERROR _winspool_AsyncSetPrinterData(struct pipes_struct *p,
+ struct winspool_AsyncSetPrinterData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetPrinterDataEx
+****************************************************************/
+
+WERROR _winspool_AsyncSetPrinterDataEx(struct pipes_struct *p,
+ struct winspool_AsyncSetPrinterDataEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncClosePrinter
+****************************************************************/
+
+WERROR _winspool_AsyncClosePrinter(struct pipes_struct *p,
+ struct winspool_AsyncClosePrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddForm
+****************************************************************/
+
+WERROR _winspool_AsyncAddForm(struct pipes_struct *p,
+ struct winspool_AsyncAddForm *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeleteForm
+****************************************************************/
+
+WERROR _winspool_AsyncDeleteForm(struct pipes_struct *p,
+ struct winspool_AsyncDeleteForm *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetForm
+****************************************************************/
+
+WERROR _winspool_AsyncGetForm(struct pipes_struct *p,
+ struct winspool_AsyncGetForm *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetForm
+****************************************************************/
+
+WERROR _winspool_AsyncSetForm(struct pipes_struct *p,
+ struct winspool_AsyncSetForm *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumForms
+****************************************************************/
+
+WERROR _winspool_AsyncEnumForms(struct pipes_struct *p,
+ struct winspool_AsyncEnumForms *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrinterDriver
+****************************************************************/
+
+WERROR _winspool_AsyncGetPrinterDriver(struct pipes_struct *p,
+ struct winspool_AsyncGetPrinterDriver *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrinterData
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrinterData(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrinterData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrinterDataEx
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrinterDataEx(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrinterDataEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrinterKey
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrinterKey(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrinterKey *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterData
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinterData(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterDataEx
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinterDataEx(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterDataEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterKey
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinterKey(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterKey *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncXcvData
+****************************************************************/
+
+WERROR _winspool_AsyncXcvData(struct pipes_struct *p,
+ struct winspool_AsyncXcvData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSendRecvBidiData
+****************************************************************/
+
+WERROR _winspool_AsyncSendRecvBidiData(struct pipes_struct *p,
+ struct winspool_AsyncSendRecvBidiData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncCreatePrinterIC
+****************************************************************/
+
+WERROR _winspool_AsyncCreatePrinterIC(struct pipes_struct *p,
+ struct winspool_AsyncCreatePrinterIC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncPlayGdiScriptOnPrinterIC
+****************************************************************/
+
+WERROR _winspool_AsyncPlayGdiScriptOnPrinterIC(struct pipes_struct *p,
+ struct winspool_AsyncPlayGdiScriptOnPrinterIC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterIC
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinterIC(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterIC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrinters
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrinters(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrinters *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddPrinterDriver
+****************************************************************/
+
+WERROR _winspool_AsyncAddPrinterDriver(struct pipes_struct *p,
+ struct winspool_AsyncAddPrinterDriver *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrinterDrivers
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrinterDrivers(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrinterDrivers *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrinterDriverDirectory
+****************************************************************/
+
+WERROR _winspool_AsyncGetPrinterDriverDirectory(struct pipes_struct *p,
+ struct winspool_AsyncGetPrinterDriverDirectory *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterDriver
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinterDriver(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterDriver *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterDriverEx
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrinterDriverEx(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterDriverEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddPrintProcessor
+****************************************************************/
+
+WERROR _winspool_AsyncAddPrintProcessor(struct pipes_struct *p,
+ struct winspool_AsyncAddPrintProcessor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrintProcessors
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrintProcessors(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrintProcessors *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrintProcessorDirectory
+****************************************************************/
+
+WERROR _winspool_AsyncGetPrintProcessorDirectory(struct pipes_struct *p,
+ struct winspool_AsyncGetPrintProcessorDirectory *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPorts
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPorts(struct pipes_struct *p,
+ struct winspool_AsyncEnumPorts *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumMonitors
+****************************************************************/
+
+WERROR _winspool_AsyncEnumMonitors(struct pipes_struct *p,
+ struct winspool_AsyncEnumMonitors *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddPort
+****************************************************************/
+
+WERROR _winspool_AsyncAddPort(struct pipes_struct *p,
+ struct winspool_AsyncAddPort *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetPort
+****************************************************************/
+
+WERROR _winspool_AsyncSetPort(struct pipes_struct *p,
+ struct winspool_AsyncSetPort *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddMonitor
+****************************************************************/
+
+WERROR _winspool_AsyncAddMonitor(struct pipes_struct *p,
+ struct winspool_AsyncAddMonitor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeleteMonitor
+****************************************************************/
+
+WERROR _winspool_AsyncDeleteMonitor(struct pipes_struct *p,
+ struct winspool_AsyncDeleteMonitor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrintProcessor
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePrintProcessor(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrintProcessor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPrintProcessorDatatypes
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPrintProcessorDatatypes(struct pipes_struct *p,
+ struct winspool_AsyncEnumPrintProcessorDatatypes *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncAddPerMachineConnection
+****************************************************************/
+
+WERROR _winspool_AsyncAddPerMachineConnection(struct pipes_struct *p,
+ struct winspool_AsyncAddPerMachineConnection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePerMachineConnection
+****************************************************************/
+
+WERROR _winspool_AsyncDeletePerMachineConnection(struct pipes_struct *p,
+ struct winspool_AsyncDeletePerMachineConnection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumPerMachineConnections
+****************************************************************/
+
+WERROR _winspool_AsyncEnumPerMachineConnections(struct pipes_struct *p,
+ struct winspool_AsyncEnumPerMachineConnections *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_SyncRegisterForRemoteNotifications
+****************************************************************/
+
+HRESULT _winspool_SyncRegisterForRemoteNotifications(struct pipes_struct *p,
+ struct winspool_SyncRegisterForRemoteNotifications *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_SyncUnRegisterForRemoteNotifications
+****************************************************************/
+
+HRESULT _winspool_SyncUnRegisterForRemoteNotifications(struct pipes_struct *p,
+ struct winspool_SyncUnRegisterForRemoteNotifications *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_SyncRefreshRemoteNotifications
+****************************************************************/
+
+HRESULT _winspool_SyncRefreshRemoteNotifications(struct pipes_struct *p,
+ struct winspool_SyncRefreshRemoteNotifications *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetRemoteNotifications
+****************************************************************/
+
+HRESULT _winspool_AsyncGetRemoteNotifications(struct pipes_struct *p,
+ struct winspool_AsyncGetRemoteNotifications *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncInstallPrinterDriverFromPackage
+****************************************************************/
+
+HRESULT _winspool_AsyncInstallPrinterDriverFromPackage(struct pipes_struct *p,
+ struct winspool_AsyncInstallPrinterDriverFromPackage *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncUploadPrinterDriverPackage
+****************************************************************/
+
+HRESULT _winspool_AsyncUploadPrinterDriverPackage(struct pipes_struct *p,
+ struct winspool_AsyncUploadPrinterDriverPackage *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetCorePrinterDrivers
+****************************************************************/
+
+HRESULT _winspool_AsyncGetCorePrinterDrivers(struct pipes_struct *p,
+ struct winspool_AsyncGetCorePrinterDrivers *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncCorePrinterDriverInstalled
+****************************************************************/
+
+HRESULT _winspool_AsyncCorePrinterDriverInstalled(struct pipes_struct *p,
+ struct winspool_AsyncCorePrinterDriverInstalled *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetPrinterDriverPackagePath
+****************************************************************/
+
+HRESULT _winspool_AsyncGetPrinterDriverPackagePath(struct pipes_struct *p,
+ struct winspool_AsyncGetPrinterDriverPackagePath *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeletePrinterDriverPackage
+****************************************************************/
+
+HRESULT _winspool_AsyncDeletePrinterDriverPackage(struct pipes_struct *p,
+ struct winspool_AsyncDeletePrinterDriverPackage *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncReadPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncReadPrinter(struct pipes_struct *p,
+ struct winspool_AsyncReadPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncResetPrinter
+****************************************************************/
+
+WERROR _winspool_AsyncResetPrinter(struct pipes_struct *p,
+ struct winspool_AsyncResetPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncGetJobNamedPropertyValue
+****************************************************************/
+
+WERROR _winspool_AsyncGetJobNamedPropertyValue(struct pipes_struct *p,
+ struct winspool_AsyncGetJobNamedPropertyValue *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncSetJobNamedProperty
+****************************************************************/
+
+WERROR _winspool_AsyncSetJobNamedProperty(struct pipes_struct *p,
+ struct winspool_AsyncSetJobNamedProperty *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncDeleteJobNamedProperty
+****************************************************************/
+
+WERROR _winspool_AsyncDeleteJobNamedProperty(struct pipes_struct *p,
+ struct winspool_AsyncDeleteJobNamedProperty *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncEnumJobNamedProperties
+****************************************************************/
+
+WERROR _winspool_AsyncEnumJobNamedProperties(struct pipes_struct *p,
+ struct winspool_AsyncEnumJobNamedProperties *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _winspool_AsyncLogJobInfoForBranchOffice
+****************************************************************/
+
+WERROR _winspool_AsyncLogJobInfoForBranchOffice(struct pipes_struct *p,
+ struct winspool_AsyncLogJobInfoForBranchOffice *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
diff --git a/source3/rpc_server/spoolss/srv_spoolss_handle.h b/source3/rpc_server/spoolss/srv_spoolss_handle.h
new file mode 100644
index 0000000..e84037c
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_spoolss_handle.h
@@ -0,0 +1,77 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Gerald Carter 2000-2004,
+ * Copyright (C) Tim Potter 2001-2002.
+ * Copyright (C) Guenther Deschner 2009-2010.
+ * Copyright (C) Andreas Schneider 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Since the SPOOLSS rpc routines are basically DOS 16-bit calls wrapped
+ up, all the errors returned are DOS errors, not NT status codes. */
+
+#include "includes.h"
+#include "../librpc/gen_ndr/spoolss.h"
+
+struct notify_back_channel;
+
+#define SPLHND_PRINTER 1
+#define SPLHND_SERVER 2
+#define SPLHND_PORTMON_TCP 3
+#define SPLHND_PORTMON_LOCAL 4
+
+/* structure to store the printer handles */
+/* and a reference to what it's pointing to */
+/* and the notify info asked about */
+/* that's the central struct */
+struct printer_handle {
+ struct printer_handle *prev, *next;
+ bool document_started;
+ bool page_started;
+ uint32_t jobid; /* jobid in printing backend */
+ int printer_type;
+ const char *servername;
+ fstring sharename;
+ uint32_t access_granted;
+ struct {
+ uint32_t flags;
+ uint32_t options;
+ fstring localmachine;
+ uint32_t printerlocal;
+ struct spoolss_NotifyOption *option;
+ struct policy_handle cli_hnd;
+ struct notify_back_channel *cli_chan;
+ uint32_t change;
+ /* are we in a FindNextPrinterChangeNotify() call? */
+ bool fnpcn;
+ struct messaging_context *msg_ctx;
+ } notify;
+ struct {
+ fstring machine;
+ fstring user;
+ } client;
+
+ /* devmode sent in the OpenPrinter() call */
+ struct spoolss_DeviceMode *devmode;
+
+ /* TODO cache the printer info2 structure */
+ struct spoolss_PrinterInfo2 *info2;
+
+};
diff --git a/source3/rpc_server/spoolss/srv_spoolss_nt.c b/source3/rpc_server/spoolss/srv_spoolss_nt.c
new file mode 100644
index 0000000..16b8b41
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_spoolss_nt.c
@@ -0,0 +1,11622 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Gerald Carter 2000-2004,
+ * Copyright (C) Tim Potter 2001-2002.
+ * Copyright (C) Guenther Deschner 2009-2010.
+ * Copyright (C) Andreas Schneider 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Since the SPOOLSS rpc routines are basically DOS 16-bit calls wrapped
+ up, all the errors returned are DOS errors, not NT status codes. */
+
+#include "includes.h"
+#include "libsmb/namequery.h"
+#include "ntdomain.h"
+#include "nt_printing.h"
+#include "srv_spoolss_util.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_scompat.h"
+#include "../librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_client/init_spoolss.h"
+#include "rpc_client/cli_pipe.h"
+#include "../libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "registry.h"
+#include "include/printing.h"
+#include "secrets.h"
+#include "../librpc/gen_ndr/netlogon.h"
+#include "rpc_misc.h"
+#include "printing/notify.h"
+#include "serverid.h"
+#include "../libcli/registry/util_reg.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "auth.h"
+#include "messages.h"
+#include "rpc_server/spoolss/srv_spoolss_nt.h"
+#include "util_tdb.h"
+#include "libsmb/libsmb.h"
+#include "printing/printer_list.h"
+#include "../lib/tsocket/tsocket.h"
+#include "rpc_client/cli_winreg_spoolss.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "rpc_server/spoolss/srv_spoolss_handle.h"
+#include "lib/gencache.h"
+#include "rpc_server/rpc_server.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "printing/nt_printing_migrate_internal.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+
+/* macros stolen from s4 spoolss server */
+#define SPOOLSS_BUFFER_UNION(fn,info,level) \
+ ((info)?ndr_size_##fn(info, level, 0):0)
+
+#define SPOOLSS_BUFFER_UNION_ARRAY(mem_ctx,fn,info,level,count) \
+ ((info)?ndr_size_##fn##_info(mem_ctx, level, count, info):0)
+
+#define SPOOLSS_BUFFER_ARRAY(mem_ctx,fn,info,count) \
+ ((info)?ndr_size_##fn##_info(mem_ctx, count, info):0)
+
+#define SPOOLSS_BUFFER_OK(val_true,val_false) ((r->in.offered >= *r->out.needed)?val_true:val_false)
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#ifndef MAX_OPEN_PRINTER_EXS
+#define MAX_OPEN_PRINTER_EXS 50
+#endif
+
+#define GLOBAL_SPOOLSS_OS_MAJOR_DEFAULT 5
+#define GLOBAL_SPOOLSS_OS_MINOR_DEFAULT 2
+#define GLOBAL_SPOOLSS_OS_BUILD_DEFAULT 3790
+#define GLOBAL_SPOOLSS_ARCHITECTURE SPOOLSS_ARCHITECTURE_x64
+
+static struct printer_handle *printers_list;
+
+struct printer_session_counter {
+ struct printer_session_counter *next;
+ struct printer_session_counter *prev;
+
+ int snum;
+ uint32_t counter;
+};
+
+static struct printer_session_counter *counter_list;
+
+struct notify_back_channel {
+ struct notify_back_channel *prev, *next;
+
+ /* associated client */
+ struct sockaddr_storage client_address;
+
+ /* print notify back-channel pipe handle*/
+ struct rpc_pipe_client *cli_pipe;
+ struct cli_state *cli;
+ uint32_t active_connections;
+};
+
+static struct notify_back_channel *back_channels;
+
+/* Map generic permissions to printer object specific permissions */
+
+const struct standard_mapping printer_std_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+/* Map generic permissions to print server object specific permissions */
+
+const struct standard_mapping printserver_std_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+/* API table for Xcv Monitor functions */
+
+struct xcv_api_table {
+ const char *name;
+ WERROR(*fn) (TALLOC_CTX *mem_ctx, struct security_token *token, DATA_BLOB *in, DATA_BLOB *out, uint32_t *needed);
+};
+
+static void prune_printername_cache(void);
+
+/********************************************************************
+ * Canonicalize servername.
+ ********************************************************************/
+
+static const char *canon_servername(const char *servername)
+{
+ const char *pservername = servername;
+
+ if (servername == NULL) {
+ return "";
+ }
+
+ while (*pservername == '\\') {
+ pservername++;
+ }
+ return pservername;
+}
+
+/* translate between internal status numbers and NT status numbers */
+static int nt_printj_status(int v)
+{
+ switch (v) {
+ case LPQ_QUEUED:
+ return 0;
+ case LPQ_PAUSED:
+ return JOB_STATUS_PAUSED;
+ case LPQ_SPOOLING:
+ return JOB_STATUS_SPOOLING;
+ case LPQ_PRINTING:
+ return JOB_STATUS_PRINTING;
+ case LPQ_ERROR:
+ return JOB_STATUS_ERROR;
+ case LPQ_DELETING:
+ return JOB_STATUS_DELETING;
+ case LPQ_OFFLINE:
+ return JOB_STATUS_OFFLINE;
+ case LPQ_PAPEROUT:
+ return JOB_STATUS_PAPEROUT;
+ case LPQ_PRINTED:
+ return JOB_STATUS_PRINTED;
+ case LPQ_DELETED:
+ return JOB_STATUS_DELETED;
+ case LPQ_BLOCKED:
+ return JOB_STATUS_BLOCKED_DEVQ;
+ case LPQ_USER_INTERVENTION:
+ return JOB_STATUS_USER_INTERVENTION;
+ }
+ return 0;
+}
+
+static int nt_printq_status(int v)
+{
+ switch (v) {
+ case LPQ_PAUSED:
+ return PRINTER_STATUS_PAUSED;
+ case LPQ_QUEUED:
+ case LPQ_SPOOLING:
+ case LPQ_PRINTING:
+ return 0;
+ }
+ return 0;
+}
+
+/***************************************************************************
+ Disconnect from the client
+****************************************************************************/
+
+static void srv_spoolss_replycloseprinter(int snum,
+ struct printer_handle *prn_hnd)
+{
+ WERROR result;
+ NTSTATUS status;
+
+ /*
+ * Tell the specific printing tdb we no longer want messages for this printer
+ * by deregistering our PID.
+ */
+
+ if (!print_notify_deregister_pid(snum)) {
+ DEBUG(0, ("Failed to register our pid for printer %s\n",
+ lp_const_servicename(snum)));
+ }
+
+ /* weird if the test succeeds !!! */
+ if (prn_hnd->notify.cli_chan == NULL ||
+ prn_hnd->notify.cli_chan->cli_pipe == NULL ||
+ prn_hnd->notify.cli_chan->cli_pipe->binding_handle == NULL ||
+ prn_hnd->notify.cli_chan->active_connections == 0) {
+ DEBUG(0, ("Trying to close unexisting backchannel!\n"));
+ DLIST_REMOVE(back_channels, prn_hnd->notify.cli_chan);
+ TALLOC_FREE(prn_hnd->notify.cli_chan);
+ return;
+ }
+
+ status = dcerpc_spoolss_ReplyClosePrinter(
+ prn_hnd->notify.cli_chan->cli_pipe->binding_handle,
+ talloc_tos(),
+ &prn_hnd->notify.cli_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_spoolss_ReplyClosePrinter failed [%s].\n",
+ nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ } else if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("reply_close_printer failed [%s].\n",
+ win_errstr(result)));
+ }
+
+ /* if it's the last connection, deconnect the IPC$ share */
+ if (prn_hnd->notify.cli_chan->active_connections == 1) {
+
+ cli_shutdown(prn_hnd->notify.cli_chan->cli);
+ DLIST_REMOVE(back_channels, prn_hnd->notify.cli_chan);
+ TALLOC_FREE(prn_hnd->notify.cli_chan);
+
+ if (prn_hnd->notify.msg_ctx != NULL) {
+ messaging_deregister(prn_hnd->notify.msg_ctx,
+ MSG_PRINTER_NOTIFY2, NULL);
+ }
+ }
+
+ if (prn_hnd->notify.cli_chan) {
+ prn_hnd->notify.cli_chan->active_connections--;
+ prn_hnd->notify.cli_chan = NULL;
+ }
+}
+
+/****************************************************************************
+ Functions to free a printer entry datastruct.
+****************************************************************************/
+
+static int printer_entry_destructor(struct printer_handle *Printer)
+{
+ if (Printer->notify.cli_chan != NULL &&
+ Printer->notify.cli_chan->active_connections > 0) {
+ int snum = -1;
+
+ switch(Printer->printer_type) {
+ case SPLHND_SERVER:
+ srv_spoolss_replycloseprinter(snum, Printer);
+ break;
+
+ case SPLHND_PRINTER:
+ snum = print_queue_snum(Printer->sharename);
+ if (snum != -1) {
+ srv_spoolss_replycloseprinter(snum, Printer);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ TALLOC_FREE(Printer->notify.option);
+ TALLOC_FREE(Printer->devmode);
+
+ /* Remove from the internal list. */
+ DLIST_REMOVE(printers_list, Printer);
+ return 0;
+}
+
+/****************************************************************************
+ find printer index by handle
+****************************************************************************/
+
+static struct printer_handle *find_printer_index_by_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd)
+{
+ struct printer_handle *find_printer = NULL;
+ NTSTATUS status;
+
+ find_printer = find_policy_by_hnd(p,
+ hnd,
+ DCESRV_HANDLE_ANY,
+ struct printer_handle,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("find_printer_index_by_hnd: Printer handle not found: %s\n",
+ nt_errstr(status)));
+ return NULL;
+ }
+
+ return find_printer;
+}
+
+/****************************************************************************
+ Close printer index by handle.
+****************************************************************************/
+
+static bool close_printer_handle(struct pipes_struct *p, struct policy_handle *hnd)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("close_printer_handle: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(hnd)));
+ return false;
+ }
+
+ close_policy_hnd(p, hnd);
+
+ return true;
+}
+
+/****************************************************************************
+ Delete a printer given a handle.
+****************************************************************************/
+
+static WERROR delete_printer_hook(TALLOC_CTX *ctx, struct security_token *token,
+ const char *sharename,
+ struct messaging_context *msg_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *cmd = lp_deleteprinter_command(talloc_tos(), lp_sub);
+ char *command = NULL;
+ int ret;
+ bool is_print_op = false;
+
+ /* can't fail if we don't try */
+
+ if ( !*cmd )
+ return WERR_OK;
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\"",
+ cmd, sharename);
+ if (!command) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if ( token )
+ is_print_op = security_token_has_privilege(token, SEC_PRIV_PRINT_OPERATOR);
+
+ DEBUG(10,("Running [%s]\n", command));
+
+ /********** BEGIN SePrintOperatorPrivlege BLOCK **********/
+
+ if ( is_print_op )
+ become_root();
+
+ ret = smbrun(command, NULL, NULL);
+ if (ret == 0) {
+ /* Tell everyone we updated smb.conf. */
+ messaging_send_all(msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0);
+ }
+
+ if ( is_print_op )
+ unbecome_root();
+
+ /********** END SePrintOperatorPrivlege BLOCK **********/
+
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ TALLOC_FREE(command);
+
+ if (ret != 0)
+ return WERR_INVALID_HANDLE; /* What to return here? */
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Delete a printer given a handle.
+****************************************************************************/
+
+static WERROR delete_printer_handle(struct pipes_struct *p, struct policy_handle *hnd)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, hnd);
+ WERROR result;
+
+ if (!Printer) {
+ DEBUG(2,("delete_printer_handle: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(hnd)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /*
+ * It turns out that Windows allows delete printer on a handle
+ * opened by an admin user, then used on a pipe handle created
+ * by an anonymous user..... but they're working on security.... riiight !
+ * JRA.
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("delete_printer_handle: denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* this does not need a become root since the access check has been
+ done on the handle already */
+
+ result = winreg_delete_printer_key_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ Printer->sharename,
+ "");
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3,("Error deleting printer %s\n", Printer->sharename));
+ return WERR_INVALID_HANDLE;
+ }
+
+ result = delete_printer_hook(p->mem_ctx, session_info->security_token,
+ Printer->sharename, p->msg_ctx);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ prune_printername_cache();
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Return the snum of a printer corresponding to an handle.
+****************************************************************************/
+
+static bool get_printer_snum(struct pipes_struct *p, struct policy_handle *hnd,
+ int *number, struct share_params **params)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("get_printer_snum: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(hnd)));
+ return false;
+ }
+
+ switch (Printer->printer_type) {
+ case SPLHND_PRINTER:
+ DEBUG(4,("short name:%s\n", Printer->sharename));
+ *number = print_queue_snum(Printer->sharename);
+ return (*number != -1);
+ case SPLHND_SERVER:
+ return false;
+ default:
+ return false;
+ }
+}
+
+/****************************************************************************
+ Set printer handle type.
+ Check if it's \\server or \\server\printer
+****************************************************************************/
+
+static bool set_printer_hnd_printertype(struct printer_handle *Printer, const char *handlename)
+{
+ DEBUG(3,("Setting printer type=%s\n", handlename));
+
+ /* it's a print server */
+ if (handlename && *handlename=='\\' && *(handlename+1)=='\\' && !strchr_m(handlename+2, '\\')) {
+ DEBUGADD(4,("Printer is a print server\n"));
+ Printer->printer_type = SPLHND_SERVER;
+ }
+ /* it's a printer (set_printer_hnd_name() will handle port monitors */
+ else {
+ DEBUGADD(4,("Printer is a printer\n"));
+ Printer->printer_type = SPLHND_PRINTER;
+ }
+
+ return true;
+}
+
+static void prune_printername_cache_fn(const char *key, const char *value,
+ time_t timeout, void *private_data)
+{
+ gencache_del(key);
+}
+
+static void prune_printername_cache(void)
+{
+ gencache_iterate(prune_printername_cache_fn, NULL, "PRINTERNAME/*");
+}
+
+/****************************************************************************
+ Set printer handle name.. Accept names like \\server, \\server\printer,
+ \\server\SHARE, & "\\server\,XcvMonitor Standard TCP/IP Port" See
+ the MSDN docs regarding OpenPrinter() for details on the XcvData() and
+ XcvDataPort() interface.
+****************************************************************************/
+
+static WERROR set_printer_hnd_name(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct printer_handle *Printer,
+ const char *handlename)
+{
+ int snum;
+ int n_services=lp_numservices();
+ char *aprinter;
+ const char *printername;
+ const char *servername = NULL;
+ fstring sname;
+ bool found = false;
+ struct spoolss_PrinterInfo2 *info2 = NULL;
+ WERROR result;
+ char *p;
+
+ /*
+ * Hopefully nobody names his printers like this. Maybe \ or ,
+ * are illegal in printer names even?
+ */
+ const char printer_not_found[] = "Printer \\, !@#$%^&*( not found";
+ char *cache_key;
+ char *tmp;
+
+ DEBUG(4,("Setting printer name=%s (len=%lu)\n", handlename,
+ (unsigned long)strlen(handlename)));
+
+ aprinter = discard_const_p(char, handlename);
+ if ( *handlename == '\\' ) {
+ servername = canon_servername(handlename);
+ if ( (aprinter = strchr_m( servername, '\\' )) != NULL ) {
+ *aprinter = '\0';
+ aprinter++;
+ }
+ if (!is_myname_or_ipaddr(servername)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+ Printer->servername = talloc_asprintf(Printer, "\\\\%s", servername);
+ if (Printer->servername == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if (Printer->printer_type == SPLHND_SERVER) {
+ return WERR_OK;
+ }
+
+ if (Printer->printer_type != SPLHND_PRINTER) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ DEBUGADD(5, ("searching for [%s]\n", aprinter));
+
+ p = strchr(aprinter, ',');
+ if (p != NULL) {
+ char *p2 = p;
+ p++;
+ if (*p == ' ') {
+ p++;
+ }
+ if (strncmp(p, "DrvConvert", strlen("DrvConvert")) == 0) {
+ *p2 = '\0';
+ } else if (strncmp(p, "LocalOnly", strlen("LocalOnly")) == 0) {
+ *p2 = '\0';
+ }
+ }
+
+ if (p) {
+ DEBUGADD(5, ("stripped handlename: [%s]\n", aprinter));
+ }
+
+ /* check for the Port Monitor Interface */
+ if ( strequal( aprinter, SPL_XCV_MONITOR_TCPMON ) ) {
+ Printer->printer_type = SPLHND_PORTMON_TCP;
+ fstrcpy(sname, SPL_XCV_MONITOR_TCPMON);
+ found = true;
+ }
+ else if ( strequal( aprinter, SPL_XCV_MONITOR_LOCALMON ) ) {
+ Printer->printer_type = SPLHND_PORTMON_LOCAL;
+ fstrcpy(sname, SPL_XCV_MONITOR_LOCALMON);
+ found = true;
+ }
+
+ cache_key = talloc_asprintf(talloc_tos(), "PRINTERNAME/%s", aprinter);
+ if (cache_key == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * With hundreds of printers, the "for" loop iterating all
+ * shares can be quite expensive, as it is done on every
+ * OpenPrinter. The loop maps "aprinter" to "sname", the
+ * result of which we cache in gencache.
+ */
+ if (gencache_get(cache_key, talloc_tos(), &tmp, NULL)) {
+ found = (strcmp(tmp, printer_not_found) != 0);
+ if (!found) {
+ DEBUG(4, ("Printer %s not found\n", aprinter));
+ TALLOC_FREE(tmp);
+ return WERR_INVALID_PRINTER_NAME;
+ }
+ fstrcpy(sname, tmp);
+ TALLOC_FREE(tmp);
+ }
+
+ /* Search all sharenames first as this is easier than pulling
+ the printer_info_2 off of disk. Don't use find_service() since
+ that calls out to map_username() */
+
+ /* do another loop to look for printernames */
+ for (snum = 0; !found && snum < n_services; snum++) {
+ const char *printer = lp_const_servicename(snum);
+
+ /* no point going on if this is not a printer */
+ if (!(lp_snum_ok(snum) && lp_printable(snum))) {
+ continue;
+ }
+
+ /* ignore [printers] share */
+ if (strequal(printer, "printers")) {
+ continue;
+ }
+
+ fstrcpy(sname, printer);
+ if (strequal(aprinter, printer)) {
+ found = true;
+ break;
+ }
+
+ /* no point looking up the printer object if
+ we aren't allowing printername != sharename */
+ if (lp_force_printername(snum)) {
+ continue;
+ }
+
+ result = winreg_get_printer_internal(mem_ctx,
+ session_info,
+ msg_ctx,
+ sname,
+ &info2);
+ if ( !W_ERROR_IS_OK(result) ) {
+ DEBUG(2,("set_printer_hnd_name: failed to lookup printer [%s] -- result [%s]\n",
+ sname, win_errstr(result)));
+ continue;
+ }
+
+ printername = strrchr(info2->printername, '\\');
+ if (printername == NULL) {
+ printername = info2->printername;
+ } else {
+ printername++;
+ }
+
+ if (strequal(printername, aprinter)) {
+ found = true;
+ break;
+ }
+
+ DEBUGADD(10, ("printername: %s\n", printername));
+
+ TALLOC_FREE(info2);
+ }
+
+ if (!found) {
+ gencache_set(cache_key, printer_not_found,
+ time(NULL) + 300);
+ TALLOC_FREE(cache_key);
+ DEBUGADD(4,("Printer not found\n"));
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ gencache_set(cache_key, sname, time(NULL) + 300);
+ TALLOC_FREE(cache_key);
+
+ DEBUGADD(4,("set_printer_hnd_name: Printer found: %s -> %s\n", aprinter, sname));
+
+ strlcpy(Printer->sharename, sname, sizeof(Printer->sharename));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Find first available printer slot. creates a printer handle for you.
+ ****************************************************************************/
+
+static WERROR open_printer_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ const char *name,
+ uint32_t access_granted)
+{
+ struct printer_handle *new_printer;
+ WERROR result;
+
+ DEBUG(10,("open_printer_hnd: name [%s]\n", name));
+
+ new_printer = talloc_zero(p->mem_ctx, struct printer_handle);
+ if (new_printer == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ talloc_set_destructor(new_printer, printer_entry_destructor);
+
+ /* This also steals the printer_handle on the policy_handle */
+ if (!create_policy_hnd(p, hnd, 0, new_printer)) {
+ TALLOC_FREE(new_printer);
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* Add to the internal list. */
+ DLIST_ADD(printers_list, new_printer);
+
+ new_printer->notify.option=NULL;
+
+ if (!set_printer_hnd_printertype(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return WERR_INVALID_HANDLE;
+ }
+
+ result = set_printer_hnd_name(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ new_printer, name);
+ if (!W_ERROR_IS_OK(result)) {
+ close_printer_handle(p, hnd);
+ return result;
+ }
+
+ new_printer->access_granted = access_granted;
+
+ DBG_INFO("%d printer handles active\n", (int)num_pipe_handles());
+
+ return WERR_OK;
+}
+
+/***************************************************************************
+ check to see if the client notify handle is monitoring the notification
+ given by (notify_type, notify_field).
+ **************************************************************************/
+
+static bool is_monitoring_event_flags(uint32_t flags, uint16_t notify_type,
+ uint16_t notify_field)
+{
+ return true;
+}
+
+static bool is_monitoring_event(struct printer_handle *p, uint16_t notify_type,
+ uint16_t notify_field)
+{
+ struct spoolss_NotifyOption *option = p->notify.option;
+ uint32_t i, j;
+
+ /*
+ * Flags should always be zero when the change notify
+ * is registered by the client's spooler. A user Win32 app
+ * might use the flags though instead of the NOTIFY_OPTION_INFO
+ * --jerry
+ */
+
+ if (!option) {
+ return false;
+ }
+
+ if (p->notify.flags)
+ return is_monitoring_event_flags(
+ p->notify.flags, notify_type, notify_field);
+
+ for (i = 0; i < option->count; i++) {
+
+ /* Check match for notify_type */
+
+ if (option->types[i].type != notify_type)
+ continue;
+
+ /* Check match for field */
+
+ for (j = 0; j < option->types[i].count; j++) {
+ if (option->types[i].fields[j].field == notify_field) {
+ return true;
+ }
+ }
+ }
+
+ DEBUG(10, ("Open handle for \\\\%s\\%s is not monitoring 0x%02x/0x%02x\n",
+ p->servername, p->sharename, notify_type, notify_field));
+
+ return false;
+}
+
+#define SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(_data, _integer) \
+ _data->data.integer[0] = _integer; \
+ _data->data.integer[1] = 0;
+
+
+#define SETUP_SPOOLSS_NOTIFY_DATA_STRING(_data, _p) \
+ _data->data.string.string = talloc_strdup(mem_ctx, _p); \
+ if (!_data->data.string.string) {\
+ _data->data.string.size = 0; \
+ } \
+ _data->data.string.size = strlen_m_term(_p) * 2;
+
+#define SETUP_SPOOLSS_NOTIFY_DATA_DEVMODE(_data, _devmode) \
+ _data->data.devmode.devmode = _devmode;
+
+static void init_systemtime_buffer(TALLOC_CTX *mem_ctx,
+ struct tm *t,
+ const char **pp,
+ uint32_t *plen)
+{
+ struct spoolss_Time st;
+ uint32_t len = 16;
+ char *p;
+
+ if (!init_systemtime(&st, t)) {
+ return;
+ }
+
+ p = talloc_array(mem_ctx, char, len);
+ if (!p) {
+ return;
+ }
+
+ /*
+ * Systemtime must be linearized as a set of UINT16's.
+ * Fix from Benjamin (Bj) Kuit bj@it.uts.edu.au
+ */
+
+ SSVAL(p, 0, st.year);
+ SSVAL(p, 2, st.month);
+ SSVAL(p, 4, st.day_of_week);
+ SSVAL(p, 6, st.day);
+ SSVAL(p, 8, st.hour);
+ SSVAL(p, 10, st.minute);
+ SSVAL(p, 12, st.second);
+ SSVAL(p, 14, st.millisecond);
+
+ *pp = p;
+ *plen = len;
+}
+
+/* Convert a notification message to a struct spoolss_Notify */
+
+static void notify_one_value(struct spoolss_notify_msg *msg,
+ struct spoolss_Notify *data,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, msg->notify.value[0]);
+}
+
+static void notify_string(struct spoolss_notify_msg *msg,
+ struct spoolss_Notify *data,
+ TALLOC_CTX *mem_ctx)
+{
+ /* The length of the message includes the trailing \0 */
+
+ data->data.string.size = msg->len * 2;
+ data->data.string.string = talloc_strdup(mem_ctx, msg->notify.data);
+ if (!data->data.string.string) {
+ data->data.string.size = 0;
+ return;
+ }
+}
+
+static void notify_system_time(struct spoolss_notify_msg *msg,
+ struct spoolss_Notify *data,
+ TALLOC_CTX *mem_ctx)
+{
+ data->data.string.string = NULL;
+ data->data.string.size = 0;
+
+ if (msg->len != sizeof(time_t)) {
+ DEBUG(5, ("notify_system_time: received wrong sized message (%d)\n",
+ msg->len));
+ return;
+ }
+
+ init_systemtime_buffer(mem_ctx, gmtime((time_t *)msg->notify.data),
+ &data->data.string.string,
+ &data->data.string.size);
+}
+
+struct notify2_message_table {
+ const char *name;
+ void (*fn)(struct spoolss_notify_msg *msg,
+ struct spoolss_Notify *data, TALLOC_CTX *mem_ctx);
+};
+
+static struct notify2_message_table printer_notify_table[] = {
+ /* 0x00 */ { "PRINTER_NOTIFY_FIELD_SERVER_NAME", notify_string },
+ /* 0x01 */ { "PRINTER_NOTIFY_FIELD_PRINTER_NAME", notify_string },
+ /* 0x02 */ { "PRINTER_NOTIFY_FIELD_SHARE_NAME", notify_string },
+ /* 0x03 */ { "PRINTER_NOTIFY_FIELD_PORT_NAME", notify_string },
+ /* 0x04 */ { "PRINTER_NOTIFY_FIELD_DRIVER_NAME", notify_string },
+ /* 0x05 */ { "PRINTER_NOTIFY_FIELD_COMMENT", notify_string },
+ /* 0x06 */ { "PRINTER_NOTIFY_FIELD_LOCATION", notify_string },
+ /* 0x07 */ { "PRINTER_NOTIFY_FIELD_DEVMODE", NULL },
+ /* 0x08 */ { "PRINTER_NOTIFY_FIELD_SEPFILE", notify_string },
+ /* 0x09 */ { "PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR", notify_string },
+ /* 0x0a */ { "PRINTER_NOTIFY_FIELD_PARAMETERS", NULL },
+ /* 0x0b */ { "PRINTER_NOTIFY_FIELD_DATATYPE", notify_string },
+ /* 0x0c */ { "PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "PRINTER_NOTIFY_FIELD_ATTRIBUTES", notify_one_value },
+ /* 0x0e */ { "PRINTER_NOTIFY_FIELD_PRIORITY", notify_one_value },
+ /* 0x0f */ { "PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY", NULL },
+ /* 0x10 */ { "PRINTER_NOTIFY_FIELD_START_TIME", NULL },
+ /* 0x11 */ { "PRINTER_NOTIFY_FIELD_UNTIL_TIME", NULL },
+ /* 0x12 */ { "PRINTER_NOTIFY_FIELD_STATUS", notify_one_value },
+};
+
+static struct notify2_message_table job_notify_table[] = {
+ /* 0x00 */ { "JOB_NOTIFY_FIELD_PRINTER_NAME", NULL },
+ /* 0x01 */ { "JOB_NOTIFY_FIELD_MACHINE_NAME", NULL },
+ /* 0x02 */ { "JOB_NOTIFY_FIELD_PORT_NAME", NULL },
+ /* 0x03 */ { "JOB_NOTIFY_FIELD_USER_NAME", notify_string },
+ /* 0x04 */ { "JOB_NOTIFY_FIELD_NOTIFY_NAME", NULL },
+ /* 0x05 */ { "JOB_NOTIFY_FIELD_DATATYPE", NULL },
+ /* 0x06 */ { "JOB_NOTIFY_FIELD_PRINT_PROCESSOR", NULL },
+ /* 0x07 */ { "JOB_NOTIFY_FIELD_PARAMETERS", NULL },
+ /* 0x08 */ { "JOB_NOTIFY_FIELD_DRIVER_NAME", NULL },
+ /* 0x09 */ { "JOB_NOTIFY_FIELD_DEVMODE", NULL },
+ /* 0x0a */ { "JOB_NOTIFY_FIELD_STATUS", notify_one_value },
+ /* 0x0b */ { "JOB_NOTIFY_FIELD_STATUS_STRING", NULL },
+ /* 0x0c */ { "JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "JOB_NOTIFY_FIELD_DOCUMENT", notify_string },
+ /* 0x0e */ { "JOB_NOTIFY_FIELD_PRIORITY", NULL },
+ /* 0x0f */ { "JOB_NOTIFY_FIELD_POSITION", NULL },
+ /* 0x10 */ { "JOB_NOTIFY_FIELD_SUBMITTED", notify_system_time },
+ /* 0x11 */ { "JOB_NOTIFY_FIELD_START_TIME", NULL },
+ /* 0x12 */ { "JOB_NOTIFY_FIELD_UNTIL_TIME", NULL },
+ /* 0x13 */ { "JOB_NOTIFY_FIELD_TIME", NULL },
+ /* 0x14 */ { "JOB_NOTIFY_FIELD_TOTAL_PAGES", notify_one_value },
+ /* 0x15 */ { "JOB_NOTIFY_FIELD_PAGES_PRINTED", NULL },
+ /* 0x16 */ { "JOB_NOTIFY_FIELD_TOTAL_BYTES", notify_one_value },
+ /* 0x17 */ { "JOB_NOTIFY_FIELD_BYTES_PRINTED", NULL },
+};
+
+
+/***********************************************************************
+ Allocate talloc context for container object
+ **********************************************************************/
+
+static void notify_msg_ctr_init( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return;
+
+ ctr->ctx = talloc_init("notify_msg_ctr_init %p", ctr);
+
+ return;
+}
+
+/***********************************************************************
+ release all allocated memory and zero out structure
+ **********************************************************************/
+
+static void notify_msg_ctr_destroy( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return;
+
+ if ( ctr->ctx )
+ talloc_destroy(ctr->ctx);
+
+ ZERO_STRUCTP(ctr);
+
+ return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static TALLOC_CTX* notify_ctr_getctx( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return NULL;
+
+ return ctr->ctx;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static SPOOLSS_NOTIFY_MSG_GROUP* notify_ctr_getgroup( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32_t idx )
+{
+ if ( !ctr || !ctr->msg_groups )
+ return NULL;
+
+ if ( idx >= ctr->num_groups )
+ return NULL;
+
+ return &ctr->msg_groups[idx];
+
+}
+
+/***********************************************************************
+ How many groups of change messages do we have ?
+ **********************************************************************/
+
+static uint32_t notify_msg_ctr_numgroups( SPOOLSS_NOTIFY_MSG_CTR *ctr )
+{
+ if ( !ctr )
+ return 0;
+
+ return ctr->num_groups;
+}
+
+/***********************************************************************
+ Add a SPOOLSS_NOTIFY_MSG_CTR to the correct group
+ **********************************************************************/
+
+static int notify_msg_ctr_addmsg( SPOOLSS_NOTIFY_MSG_CTR *ctr, SPOOLSS_NOTIFY_MSG *msg )
+{
+ SPOOLSS_NOTIFY_MSG_GROUP *groups = NULL;
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_grp = NULL;
+ SPOOLSS_NOTIFY_MSG *msg_list = NULL;
+ uint32_t i, new_slot;
+
+ if ( !ctr || !msg )
+ return 0;
+
+ /* loop over all groups looking for a matching printer name */
+
+ for ( i=0; i<ctr->num_groups; i++ ) {
+ if ( strcmp(ctr->msg_groups[i].printername, msg->printer) == 0 )
+ break;
+ }
+
+ /* add a new group? */
+
+ if ( i == ctr->num_groups ) {
+ ctr->num_groups++;
+
+ if ( !(groups = talloc_realloc( ctr->ctx, ctr->msg_groups, SPOOLSS_NOTIFY_MSG_GROUP, ctr->num_groups)) ) {
+ DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed!\n"));
+ return 0;
+ }
+ ctr->msg_groups = groups;
+
+ /* clear the new entry and set the printer name */
+
+ ZERO_STRUCT( ctr->msg_groups[ctr->num_groups-1] );
+ fstrcpy( ctr->msg_groups[ctr->num_groups-1].printername, msg->printer );
+ }
+
+ /* add the change messages; 'i' is the correct index now regardless */
+
+ msg_grp = &ctr->msg_groups[i];
+
+ msg_grp->num_msgs++;
+
+ if ( !(msg_list = talloc_realloc( ctr->ctx, msg_grp->msgs, SPOOLSS_NOTIFY_MSG, msg_grp->num_msgs )) ) {
+ DEBUG(0,("notify_msg_ctr_addmsg: talloc_realloc() failed for new message [%d]!\n", msg_grp->num_msgs));
+ return 0;
+ }
+ msg_grp->msgs = msg_list;
+
+ new_slot = msg_grp->num_msgs-1;
+ memcpy( &msg_grp->msgs[new_slot], msg, sizeof(SPOOLSS_NOTIFY_MSG) );
+
+ /* need to allocate own copy of data */
+
+ if ( msg->len != 0 )
+ msg_grp->msgs[new_slot].notify.data = (char *)
+ talloc_memdup( ctr->ctx, msg->notify.data, msg->len );
+
+ return ctr->num_groups;
+}
+
+static void construct_info_data(struct spoolss_Notify *info_data,
+ enum spoolss_NotifyType type,
+ uint16_t field, int id);
+
+/***********************************************************************
+ Send a change notification message on all handles which have a call
+ back registered
+ **********************************************************************/
+
+static int build_notify2_messages(TALLOC_CTX *mem_ctx,
+ struct printer_handle *prn_hnd,
+ SPOOLSS_NOTIFY_MSG *messages,
+ uint32_t num_msgs,
+ struct spoolss_Notify **_notifies,
+ size_t *_count)
+{
+ struct spoolss_Notify *notifies;
+ SPOOLSS_NOTIFY_MSG *msg;
+ size_t count = 0;
+ uint32_t id;
+ uint32_t i;
+
+ notifies = talloc_zero_array(mem_ctx,
+ struct spoolss_Notify, num_msgs);
+ if (!notifies) {
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num_msgs; i++) {
+
+ msg = &messages[i];
+
+ /* Are we monitoring this event? */
+
+ if (!is_monitoring_event(prn_hnd, msg->type, msg->field)) {
+ continue;
+ }
+
+ DEBUG(10, ("Sending message type [0x%x] field [0x%2x] "
+ "for printer [%s]\n",
+ msg->type, msg->field, prn_hnd->sharename));
+
+ /*
+ * if the is a printer notification handle and not a job
+ * notification type, then set the id to 0.
+ * Otherwise just use what was specified in the message.
+ *
+ * When registering change notification on a print server
+ * handle we always need to send back the id (snum) matching
+ * the printer for which the change took place.
+ * For change notify registered on a printer handle,
+ * this does not matter and the id should be 0.
+ *
+ * --jerry
+ */
+
+ if ((msg->type == PRINTER_NOTIFY_TYPE) &&
+ (prn_hnd->printer_type == SPLHND_PRINTER)) {
+ id = 0;
+ } else {
+ id = msg->id;
+ }
+
+ /* Convert unix jobid to smb jobid */
+
+ if (msg->flags & SPOOLSS_NOTIFY_MSG_UNIX_JOBID) {
+ id = sysjob_to_jobid(msg->id);
+
+ if (id == -1) {
+ DEBUG(3, ("no such unix jobid %d\n",
+ msg->id));
+ continue;
+ }
+ }
+
+ construct_info_data(&notifies[count],
+ msg->type, msg->field, id);
+
+ switch(msg->type) {
+ case PRINTER_NOTIFY_TYPE:
+ if (printer_notify_table[msg->field].fn) {
+ printer_notify_table[msg->field].fn(msg,
+ &notifies[count], mem_ctx);
+ }
+ break;
+
+ case JOB_NOTIFY_TYPE:
+ if (job_notify_table[msg->field].fn) {
+ job_notify_table[msg->field].fn(msg,
+ &notifies[count], mem_ctx);
+ }
+ break;
+
+ default:
+ DEBUG(5, ("Unknown notification type %d\n",
+ msg->type));
+ continue;
+ }
+
+ count++;
+ }
+
+ *_notifies = notifies;
+ *_count = count;
+
+ return 0;
+}
+
+static int send_notify2_printer(TALLOC_CTX *mem_ctx,
+ struct printer_handle *prn_hnd,
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_group)
+{
+ struct spoolss_Notify *notifies;
+ size_t count = 0;
+ union spoolss_ReplyPrinterInfo info;
+ struct spoolss_NotifyInfo info0;
+ uint32_t reply_result;
+ NTSTATUS status;
+ WERROR werr;
+ int ret;
+
+ /* Is there notification on this handle? */
+ if (prn_hnd->notify.cli_chan == NULL ||
+ prn_hnd->notify.cli_chan->cli_pipe == NULL ||
+ prn_hnd->notify.cli_chan->cli_pipe->binding_handle == NULL ||
+ prn_hnd->notify.cli_chan->active_connections == 0) {
+ return 0;
+ }
+
+ DEBUG(10, ("Client connected! [\\\\%s\\%s]\n",
+ prn_hnd->servername, prn_hnd->sharename));
+
+ /* For this printer? Print servers always receive notifications. */
+ if ((prn_hnd->printer_type == SPLHND_PRINTER) &&
+ (!strequal(msg_group->printername, prn_hnd->sharename))) {
+ return 0;
+ }
+
+ DEBUG(10,("Our printer\n"));
+
+ /* build the array of change notifications */
+ ret = build_notify2_messages(mem_ctx, prn_hnd,
+ msg_group->msgs,
+ msg_group->num_msgs,
+ &notifies, &count);
+ if (ret) {
+ return ret;
+ }
+
+ info0.version = 0x2;
+ info0.flags = count ? 0x00020000 /* ??? */ : PRINTER_NOTIFY_INFO_DISCARDED;
+ info0.count = count;
+ info0.notifies = notifies;
+
+ info.info0 = &info0;
+
+ status = dcerpc_spoolss_RouterReplyPrinterEx(
+ prn_hnd->notify.cli_chan->cli_pipe->binding_handle,
+ mem_ctx,
+ &prn_hnd->notify.cli_hnd,
+ prn_hnd->notify.change, /* color */
+ prn_hnd->notify.flags,
+ &reply_result,
+ 0, /* reply_type, must be 0 */
+ info, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("dcerpc_spoolss_RouterReplyPrinterEx to client: %s "
+ "failed: %s\n",
+ prn_hnd->notify.cli_chan->cli_pipe->srv_name_slash,
+ nt_errstr(status)));
+ werr = ntstatus_to_werror(status);
+ } else if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, ("RouterReplyPrinterEx to client: %s "
+ "failed: %s\n",
+ prn_hnd->notify.cli_chan->cli_pipe->srv_name_slash,
+ win_errstr(werr)));
+ }
+ switch (reply_result) {
+ case 0:
+ break;
+ case PRINTER_NOTIFY_INFO_DISCARDED:
+ case PRINTER_NOTIFY_INFO_DISCARDNOTED:
+ case PRINTER_NOTIFY_INFO_COLOR_MISMATCH:
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void send_notify2_changes( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32_t idx )
+{
+ struct printer_handle *p;
+ TALLOC_CTX *mem_ctx = notify_ctr_getctx( ctr );
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_group = notify_ctr_getgroup( ctr, idx );
+ int ret;
+
+ if ( !msg_group ) {
+ DEBUG(5,("send_notify2_changes() called with no msg group!\n"));
+ return;
+ }
+
+ if (!msg_group->msgs) {
+ DEBUG(5, ("send_notify2_changes() called with no messages!\n"));
+ return;
+ }
+
+ DEBUG(8,("send_notify2_changes: Enter...[%s]\n", msg_group->printername));
+
+ /* loop over all printers */
+
+ for (p = printers_list; p; p = p->next) {
+ ret = send_notify2_printer(mem_ctx, p, msg_group);
+ if (ret) {
+ goto done;
+ }
+ }
+
+done:
+ DEBUG(8,("send_notify2_changes: Exit...\n"));
+ return;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static bool notify2_unpack_msg( SPOOLSS_NOTIFY_MSG *msg, struct timeval *tv, void *buf, size_t len )
+{
+
+ uint32_t tv_sec, tv_usec;
+ size_t offset = 0;
+
+ /* Unpack message */
+
+ offset += tdb_unpack((uint8_t *)buf + offset, len - offset, "f",
+ msg->printer);
+
+ offset += tdb_unpack((uint8_t *)buf + offset, len - offset, "ddddddd",
+ &tv_sec, &tv_usec,
+ &msg->type, &msg->field, &msg->id, &msg->len, &msg->flags);
+
+ if (msg->len == 0)
+ tdb_unpack((uint8_t *)buf + offset, len - offset, "dd",
+ &msg->notify.value[0], &msg->notify.value[1]);
+ else
+ tdb_unpack((uint8_t *)buf + offset, len - offset, "B",
+ &msg->len, &msg->notify.data);
+
+ DEBUG(3, ("notify2_unpack_msg: got NOTIFY2 message for printer %s, jobid %u type %d, field 0x%02x, flags 0x%04x\n",
+ msg->printer, (unsigned int)msg->id, msg->type, msg->field, msg->flags));
+
+ tv->tv_sec = tv_sec;
+ tv->tv_usec = tv_usec;
+
+ if (msg->len == 0)
+ DEBUG(3, ("notify2_unpack_msg: value1 = %d, value2 = %d\n", msg->notify.value[0],
+ msg->notify.value[1]));
+ else
+ dump_data(3, (uint8_t *)msg->notify.data, msg->len);
+
+ return true;
+}
+
+/********************************************************************
+ Receive a notify2 message list
+ ********************************************************************/
+
+static void receive_notify2_message_list(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ size_t msg_count, i, num_groups;
+ char *buf = (char *)data->data;
+ char *msg_ptr;
+ size_t msg_len;
+ SPOOLSS_NOTIFY_MSG notify;
+ SPOOLSS_NOTIFY_MSG_CTR messages;
+
+ if (data->length < 4) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (len < 4)!\n"));
+ return;
+ }
+
+ msg_count = IVAL(buf, 0);
+ msg_ptr = buf + 4;
+
+ DEBUG(5, ("receive_notify2_message_list: got %lu messages in list\n", (unsigned long)msg_count));
+
+ if (msg_count == 0) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (msg_count == 0) !\n"));
+ return;
+ }
+
+ /* initialize the container */
+
+ ZERO_STRUCT( messages );
+ notify_msg_ctr_init( &messages );
+
+ /*
+ * build message groups for each printer identified
+ * in a change_notify msg. Remember that a PCN message
+ * includes the handle returned for the srv_spoolss_replyopenprinter()
+ * call. Therefore messages are grouped according to printer handle.
+ */
+
+ for ( i=0; i<msg_count; i++ ) {
+ struct timeval msg_tv;
+
+ if (msg_ptr + 4 - buf > data->length) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (len > buf_size) !\n"));
+ return;
+ }
+
+ msg_len = IVAL(msg_ptr,0);
+ msg_ptr += 4;
+
+ if (msg_ptr + msg_len - buf > data->length) {
+ DEBUG(0,("receive_notify2_message_list: bad message format (bad len) !\n"));
+ return;
+ }
+
+ /* unpack messages */
+
+ ZERO_STRUCT( notify );
+ notify2_unpack_msg( &notify, &msg_tv, msg_ptr, msg_len );
+ msg_ptr += msg_len;
+
+ /* add to correct list in container */
+
+ notify_msg_ctr_addmsg( &messages, &notify );
+
+ /* free memory that might have been allocated by notify2_unpack_msg() */
+
+ if ( notify.len != 0 )
+ SAFE_FREE( notify.notify.data );
+ }
+
+ /* process each group of messages */
+
+ num_groups = notify_msg_ctr_numgroups( &messages );
+ for ( i=0; i<num_groups; i++ )
+ send_notify2_changes( &messages, i );
+
+
+ /* cleanup */
+
+ DEBUG(10,("receive_notify2_message_list: processed %u messages\n",
+ (uint32_t)msg_count ));
+
+ notify_msg_ctr_destroy( &messages );
+
+ return;
+}
+
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+
+static bool srv_spoolss_drv_upgrade_printer(const char *drivername,
+ struct messaging_context *msg_ctx)
+{
+ int len = strlen(drivername);
+
+ if (!len)
+ return false;
+
+ DEBUG(10,("srv_spoolss_drv_upgrade_printer: Sending message about driver upgrade [%s]\n",
+ drivername));
+
+ messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
+ MSG_PRINTER_DRVUPGRADE,
+ (const uint8_t *)drivername, len+1);
+
+ return true;
+}
+
+void srv_spoolss_cleanup(void)
+{
+ struct printer_session_counter *session_counter;
+
+ for (session_counter = counter_list;
+ session_counter != NULL;
+ session_counter = counter_list) {
+ DLIST_REMOVE(counter_list, session_counter);
+ TALLOC_FREE(session_counter);
+ }
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTER_DRVUPGRADE message and iterate
+ over all printers, upgrading ones as necessary
+ This is now *ONLY* called inside the background lpq updater. JRA.
+ **********************************************************************/
+
+void do_drv_upgrade_printer(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ TALLOC_CTX *tmp_ctx;
+ const struct auth_session_info *session_info = get_session_info_system();
+ struct spoolss_PrinterInfo2 *pinfo2;
+ WERROR result;
+ const char *drivername;
+ int snum;
+ int n_services = lp_numservices();
+ struct dcerpc_binding_handle *b = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) return;
+
+ drivername = talloc_strndup(tmp_ctx, (const char *)data->data, data->length);
+ if (!drivername) {
+ DEBUG(0, ("do_drv_upgrade_printer: Out of memory ?!\n"));
+ goto done;
+ }
+
+ DEBUG(10, ("do_drv_upgrade_printer: "
+ "Got message for new driver [%s]\n", drivername));
+
+ /* Iterate the printer list */
+
+ for (snum = 0; snum < n_services; snum++) {
+ if (!lp_snum_ok(snum) || !lp_printable(snum)) {
+ continue;
+ }
+
+ /* ignore [printers] share */
+ if (strequal(lp_const_servicename(snum), "printers")) {
+ continue;
+ }
+
+ if (b == NULL) {
+ result = winreg_printer_binding_handle(tmp_ctx,
+ session_info,
+ msg,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ break;
+ }
+ }
+
+ result = winreg_get_printer(tmp_ctx, b,
+ lp_const_servicename(snum),
+ &pinfo2);
+
+ if (!W_ERROR_IS_OK(result)) {
+ continue;
+ }
+
+ if (!pinfo2->drivername) {
+ continue;
+ }
+
+ if (strcmp(drivername, pinfo2->drivername) != 0) {
+ continue;
+ }
+
+ DEBUG(6,("Updating printer [%s]\n", pinfo2->printername));
+
+ /* all we care about currently is the change_id */
+ result = winreg_printer_update_changeid(tmp_ctx, b,
+ pinfo2->printername);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("do_drv_upgrade_printer: "
+ "Failed to update changeid [%s]\n",
+ win_errstr(result)));
+ }
+ }
+
+ /* all done */
+done:
+ talloc_free(tmp_ctx);
+}
+
+/********************************************************************
+ Update the cache for all printq's with a registered client
+ connection
+ ********************************************************************/
+
+void update_monitored_printq_cache(struct messaging_context *msg_ctx)
+{
+ struct printer_handle *printer = printers_list;
+ int snum;
+
+ /* loop through all printers and update the cache where
+ a client is connected */
+ while (printer) {
+ if ((printer->printer_type == SPLHND_PRINTER) &&
+ ((printer->notify.cli_chan != NULL) &&
+ (printer->notify.cli_chan->active_connections > 0))) {
+ snum = print_queue_snum(printer->sharename);
+ print_queue_status(msg_ctx, snum, NULL, NULL);
+ }
+
+ printer = printer->next;
+ }
+
+ return;
+}
+
+/****************************************************************
+ _spoolss_OpenPrinter
+****************************************************************/
+
+WERROR _spoolss_OpenPrinter(struct pipes_struct *p,
+ struct spoolss_OpenPrinter *r)
+{
+ struct spoolss_OpenPrinterEx e;
+ struct spoolss_UserLevel1 level1;
+ WERROR werr;
+
+ ZERO_STRUCT(level1);
+
+ e.in.printername = r->in.printername;
+ e.in.datatype = r->in.datatype;
+ e.in.devmode_ctr = r->in.devmode_ctr;
+ e.in.access_mask = r->in.access_mask;
+ e.in.userlevel_ctr.level = 1;
+ e.in.userlevel_ctr.user_info.level1 = &level1;
+
+ e.out.handle = r->out.handle;
+
+ werr = _spoolss_OpenPrinterEx(p, &e);
+
+ if (W_ERROR_EQUAL(werr, WERR_INVALID_PARAMETER)) {
+ /* OpenPrinterEx returns this for a bad
+ * printer name. We must return WERR_INVALID_PRINTER_NAME
+ * instead.
+ */
+ werr = WERR_INVALID_PRINTER_NAME;
+ }
+
+ return werr;
+}
+
+static WERROR copy_devicemode(TALLOC_CTX *mem_ctx,
+ struct spoolss_DeviceMode *orig,
+ struct spoolss_DeviceMode **dest)
+{
+ struct spoolss_DeviceMode *dm;
+
+ dm = talloc(mem_ctx, struct spoolss_DeviceMode);
+ if (!dm) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* copy all values, then duplicate strings and structs */
+ *dm = *orig;
+
+ dm->devicename = talloc_strdup(dm, orig->devicename);
+ if (!dm->devicename) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ dm->formname = talloc_strdup(dm, orig->formname);
+ if (!dm->formname) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (orig->driverextra_data.data) {
+ dm->driverextra_data.data =
+ (uint8_t *) talloc_memdup(dm, orig->driverextra_data.data,
+ orig->driverextra_data.length);
+ if (!dm->driverextra_data.data) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ *dest = dm;
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_OpenPrinterEx
+****************************************************************/
+
+WERROR _spoolss_OpenPrinterEx(struct pipes_struct *p,
+ struct spoolss_OpenPrinterEx *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ int snum;
+ char *raddr;
+ char *rhost;
+ struct printer_handle *Printer=NULL;
+ WERROR result;
+ int rc;
+
+ if (!r->in.printername) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!*r->in.printername) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (r->in.userlevel_ctr.level > 3) {
+ return WERR_INVALID_PARAMETER;
+ }
+ if ((r->in.userlevel_ctr.level == 1 && !r->in.userlevel_ctr.user_info.level1) ||
+ (r->in.userlevel_ctr.level == 2 && !r->in.userlevel_ctr.user_info.level2) ||
+ (r->in.userlevel_ctr.level == 3 && !r->in.userlevel_ctr.user_info.level3)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * The printcap printer share inventory is updated on client
+ * enumeration. For clients that do not perform enumeration prior to
+ * access, such as cupssmbadd, we reinitialise the printer share
+ * inventory on open as well.
+ */
+ become_root();
+ delete_and_reload_printers();
+ unbecome_root();
+
+ /* some sanity check because you can open a printer or a print server */
+ /* aka: \\server\printer or \\server */
+
+ DEBUGADD(3,("checking name: %s\n", r->in.printername));
+
+ result = open_printer_hnd(p, r->out.handle, r->in.printername, 0);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3,("_spoolss_OpenPrinterEx: Cannot open a printer handle "
+ "for printer %s\n", r->in.printername));
+ ZERO_STRUCTP(r->out.handle);
+ return result;
+ }
+
+ Printer = find_printer_index_by_hnd(p, r->out.handle);
+ if ( !Printer ) {
+ DEBUG(0,("_spoolss_OpenPrinterEx: logic error. Can't find printer "
+ "handle we created for printer %s\n", r->in.printername));
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * First case: the user is opening the print server:
+ *
+ * Disallow MS AddPrinterWizard if parameter disables it. A Win2k
+ * client 1st tries an OpenPrinterEx with access==0, MUST be allowed.
+ *
+ * Then both Win2k and WinNT clients try an OpenPrinterEx with
+ * SERVER_ALL_ACCESS, which we allow only if the user is root (uid=0)
+ * or if the user is listed in the smb.conf printer admin parameter.
+ *
+ * Then they try OpenPrinterEx with SERVER_READ which we allow. This lets the
+ * client view printer folder, but does not show the MSAPW.
+ *
+ * Note: this test needs code to check access rights here too. Jeremy
+ * could you look at this?
+ *
+ * Second case: the user is opening a printer:
+ * NT doesn't let us connect to a printer if the connecting user
+ * doesn't have print permission.
+ *
+ * Third case: user is opening a Port Monitor
+ * access checks same as opening a handle to the print server.
+ */
+
+ switch (Printer->printer_type )
+ {
+ case SPLHND_SERVER:
+ case SPLHND_PORTMON_TCP:
+ case SPLHND_PORTMON_LOCAL:
+ /* Printserver handles use global struct... */
+
+ snum = -1;
+
+ if (r->in.access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ r->in.access_mask |= SERVER_ACCESS_ADMINISTER;
+ r->in.access_mask |= SERVER_ACCESS_ENUMERATE;
+ }
+
+ /* Map standard access rights to object specific access rights */
+
+ se_map_standard(&r->in.access_mask,
+ &printserver_std_mapping);
+
+ /* Deny any object specific bits that don't apply to print
+ servers (i.e printer and job specific bits) */
+
+ r->in.access_mask &= SEC_MASK_SPECIFIC;
+
+ if (r->in.access_mask &
+ ~(SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)) {
+ DEBUG(3, ("access DENIED for non-printserver bits\n"));
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Allow admin access */
+
+ if ( r->in.access_mask & SERVER_ACCESS_ADMINISTER )
+ {
+ if (!lp_show_add_printer_wizard()) {
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ !security_token_has_privilege(
+ session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR) &&
+ !nt_token_check_sid(&global_sid_Builtin_Print_Operators,
+ session_info->security_token)) {
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ DEBUG(3,("access DENIED as user is not root, "
+ "has no printoperator privilege and is "
+ "not a member of the printoperator builtin group\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ r->in.access_mask = SERVER_ACCESS_ADMINISTER;
+ }
+ else
+ {
+ r->in.access_mask = SERVER_ACCESS_ENUMERATE;
+ }
+
+ DEBUG(4,("Setting print server access = %s\n", (r->in.access_mask == SERVER_ACCESS_ADMINISTER)
+ ? "SERVER_ACCESS_ADMINISTER" : "SERVER_ACCESS_ENUMERATE" ));
+
+ break;
+
+ case SPLHND_PRINTER:
+ /* NT doesn't let us connect to a printer if the connecting user
+ doesn't have print permission. */
+
+ if (!get_printer_snum(p, r->out.handle, &snum, NULL)) {
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (r->in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
+ r->in.access_mask = PRINTER_ACCESS_ADMINISTER;
+ }
+
+ se_map_standard(&r->in.access_mask, &printer_std_mapping);
+
+ /* map an empty access mask to the minimum access mask */
+ if (r->in.access_mask == 0x0)
+ r->in.access_mask = PRINTER_ACCESS_USE;
+
+ /*
+ * If we are not serving the printer driver for this printer,
+ * map PRINTER_ACCESS_ADMINISTER to PRINTER_ACCESS_USE. This
+ * will keep NT clients happy --jerry
+ */
+
+ if (lp_use_client_driver(snum)
+ && (r->in.access_mask & PRINTER_ACCESS_ADMINISTER))
+ {
+ r->in.access_mask = PRINTER_ACCESS_USE;
+ }
+
+ /* check smb.conf parameters and the the sec_desc */
+ raddr = tsocket_address_inet_addr_string(remote_address,
+ p->mem_ctx);
+ if (raddr == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ rc = get_remote_hostname(remote_address,
+ &rhost,
+ p->mem_ctx);
+ if (rc < 0) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (strequal(rhost, "UNKNOWN")) {
+ rhost = raddr;
+ }
+
+ if (!allow_access(lp_hosts_deny(snum), lp_hosts_allow(snum),
+ rhost, raddr)) {
+ DEBUG(3, ("access DENIED (hosts allow/deny) for printer open\n"));
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!user_ok_token(session_info->unix_info->unix_name,
+ session_info->info->domain_name,
+ session_info->security_token, snum) ||
+ !W_ERROR_IS_OK(print_access_check(session_info,
+ p->msg_ctx,
+ snum,
+ r->in.access_mask))) {
+ DEBUG(3, ("access DENIED for printer open\n"));
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if ((r->in.access_mask & SEC_MASK_SPECIFIC)& ~(PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("access DENIED for printer open - unknown bits\n"));
+ close_printer_handle(p, r->out.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.access_mask & PRINTER_ACCESS_ADMINISTER)
+ r->in.access_mask = PRINTER_ACCESS_ADMINISTER;
+ else
+ r->in.access_mask = PRINTER_ACCESS_USE;
+
+ DEBUG(4,("Setting printer access = %s\n", (r->in.access_mask == PRINTER_ACCESS_ADMINISTER)
+ ? "PRINTER_ACCESS_ADMINISTER" : "PRINTER_ACCESS_USE" ));
+
+ winreg_create_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum));
+
+ break;
+
+ default:
+ /* sanity check to prevent programmer error */
+ ZERO_STRUCTP(r->out.handle);
+ return WERR_INVALID_HANDLE;
+ }
+
+ Printer->access_granted = r->in.access_mask;
+
+ /*
+ * If the client sent a devmode in the OpenPrinter() call, then
+ * save it here in case we get a job submission on this handle
+ */
+
+ if ((Printer->printer_type != SPLHND_SERVER)
+ && (r->in.devmode_ctr.devmode != NULL)) {
+ copy_devicemode(NULL, r->in.devmode_ctr.devmode,
+ &Printer->devmode);
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_ClosePrinter
+****************************************************************/
+
+WERROR _spoolss_ClosePrinter(struct pipes_struct *p,
+ struct spoolss_ClosePrinter *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (Printer && Printer->document_started) {
+ struct spoolss_EndDocPrinter e;
+
+ e.in.handle = r->in.handle;
+
+ _spoolss_EndDocPrinter(p, &e);
+ }
+
+ if (!close_printer_handle(p, r->in.handle))
+ return WERR_INVALID_HANDLE;
+
+ /* clear the returned printer handle. Observed behavior
+ from Win2k server. Don't think this really matters.
+ Previous code just copied the value of the closed
+ handle. --jerry */
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinter
+****************************************************************/
+
+WERROR _spoolss_DeletePrinter(struct pipes_struct *p,
+ struct spoolss_DeletePrinter *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ WERROR result;
+ int snum;
+
+ if (Printer && Printer->document_started) {
+ struct spoolss_EndDocPrinter e;
+
+ e.in.handle = r->in.handle;
+
+ _spoolss_EndDocPrinter(p, &e);
+ }
+
+ if (get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ winreg_delete_printer_key_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum),
+ "");
+ }
+
+ result = delete_printer_handle(p, r->in.handle);
+
+ return result;
+}
+
+/*******************************************************************
+ * static function to lookup the version id corresponding to an
+ * long architecture string
+ ******************************************************************/
+
+static const int drv_cversion[] = {SPOOLSS_DRIVER_VERSION_9X,
+ SPOOLSS_DRIVER_VERSION_NT35,
+ SPOOLSS_DRIVER_VERSION_NT4,
+ SPOOLSS_DRIVER_VERSION_200X,
+ -1};
+
+static int get_version_id(const char *arch)
+{
+ int i;
+
+ for (i=0; archi_table[i].long_archi != NULL; i++)
+ {
+ if (strcmp(arch, archi_table[i].long_archi) == 0)
+ return (archi_table[i].version);
+ }
+
+ return -1;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterDriver
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterDriver(struct pipes_struct *p,
+ struct spoolss_DeletePrinterDriver *r)
+{
+
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct spoolss_DriverInfo8 *info = NULL;
+ int version;
+ WERROR status;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i;
+ bool found;
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ !security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.architecture == NULL || r->in.driver == NULL) {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ /* check that we have a valid driver name first */
+
+ if ((version = get_version_id(r->in.architecture)) == -1) {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ for (found = false, i = 0; drv_cversion[i] >= 0; i++) {
+ status = winreg_get_driver(tmp_ctx, b,
+ r->in.architecture, r->in.driver,
+ drv_cversion[i], &info);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(5, ("skipping del of driver with version %d\n",
+ drv_cversion[i]));
+ continue;
+ }
+ found = true;
+
+ if (printer_driver_in_use(tmp_ctx, b, info)) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ status = winreg_del_driver(tmp_ctx, b, info, drv_cversion[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0, ("failed del of driver with version %d\n",
+ drv_cversion[i]));
+ goto done;
+ }
+ }
+ if (found == false) {
+ DEBUG(0, ("driver %s not found for deletion\n", r->in.driver));
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ } else {
+ status = WERR_OK;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+static WERROR spoolss_dpd_version(TALLOC_CTX *mem_ctx,
+ struct pipes_struct *p,
+ struct spoolss_DeletePrinterDriverEx *r,
+ struct dcerpc_binding_handle *b,
+ struct spoolss_DriverInfo8 *info)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ WERROR status;
+ bool delete_files;
+
+ if (printer_driver_in_use(mem_ctx, b, info)) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ /*
+ * we have a couple of cases to consider.
+ * (1) Are any files in use? If so and DPD_DELETE_ALL_FILES is set,
+ * then the delete should fail if **any** files overlap with
+ * other drivers
+ * (2) If DPD_DELETE_UNUSED_FILES is set, then delete all
+ * non-overlapping files
+ * (3) If neither DPD_DELETE_ALL_FILES nor DPD_DELETE_UNUSED_FILES
+ * are set, then do not delete any files
+ * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+ */
+
+ delete_files = r->in.delete_flags
+ & (DPD_DELETE_ALL_FILES | DPD_DELETE_UNUSED_FILES);
+
+
+ if (delete_files) {
+ bool in_use = printer_driver_files_in_use(mem_ctx, b, info);
+ if (in_use && (r->in.delete_flags & DPD_DELETE_ALL_FILES)) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+ /*
+ * printer_driver_files_in_use() has trimmed overlapping files
+ * from info so they are not removed on DPD_DELETE_UNUSED_FILES
+ */
+ }
+
+
+ status = winreg_del_driver(mem_ctx, b, info, info->version);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ /*
+ * now delete any associated files if delete_files is
+ * true. Even if this part fails, we return success
+ * because the driver does not exist any more
+ */
+ if (delete_files) {
+ delete_driver_files(session_info, info);
+ }
+
+done:
+ return status;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterDriverEx
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterDriverEx(struct pipes_struct *p,
+ struct spoolss_DeletePrinterDriverEx *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct spoolss_DriverInfo8 *info = NULL;
+ WERROR status;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+ int i;
+ bool found;
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ !security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.architecture == NULL || r->in.driver == NULL) {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ /* check that we have a valid driver name first */
+ if (get_version_id(r->in.architecture) == -1) {
+ /* this is what NT returns */
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ for (found = false, i = 0; drv_cversion[i] >= 0; i++) {
+ if ((r->in.delete_flags & DPD_DELETE_SPECIFIC_VERSION)
+ && (drv_cversion[i] != r->in.version)) {
+ continue;
+ }
+
+ /* check if a driver with this version exists before delete */
+ status = winreg_get_driver(tmp_ctx, b,
+ r->in.architecture, r->in.driver,
+ drv_cversion[i], &info);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(5, ("skipping del of driver with version %d\n",
+ drv_cversion[i]));
+ continue;
+ }
+ found = true;
+
+ status = spoolss_dpd_version(tmp_ctx, p, r, b, info);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0, ("failed to delete driver with version %d\n",
+ drv_cversion[i]));
+ goto done;
+ }
+ }
+ if (found == false) {
+ DEBUG(0, ("driver %s not found for deletion\n", r->in.driver));
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ } else {
+ status = WERR_OK;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/********************************************************************
+ GetPrinterData on a printer server Handle.
+********************************************************************/
+
+static WERROR getprinterdata_printer_server(TALLOC_CTX *mem_ctx,
+ const char *value,
+ enum winreg_Type *type,
+ union spoolss_PrinterData *data)
+{
+ DEBUG(8,("getprinterdata_printer_server:%s\n", value));
+
+ if (!strcasecmp_m(value, "W3SvcInstalled")) {
+ *type = REG_DWORD;
+ SIVAL(&data->value, 0, 0x00);
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "BeepEnabled")) {
+ *type = REG_DWORD;
+ SIVAL(&data->value, 0, 0x00);
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "EventLog")) {
+ *type = REG_DWORD;
+ /* formally was 0x1b */
+ SIVAL(&data->value, 0, 0x00);
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "NetPopup")) {
+ *type = REG_DWORD;
+ SIVAL(&data->value, 0, 0x00);
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "MajorVersion")) {
+ *type = REG_DWORD;
+
+ /* Windows NT 4.0 seems to not allow uploading of drivers
+ to a server that reports 0x3 as the MajorVersion.
+ need to investigate more how Win2k gets around this .
+ -- jerry */
+
+ if (RA_WINNT == get_remote_arch()) {
+ SIVAL(&data->value, 0, 0x02);
+ } else {
+ SIVAL(&data->value, 0, 0x03);
+ }
+
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "MinorVersion")) {
+ *type = REG_DWORD;
+ SIVAL(&data->value, 0, 0x00);
+ return WERR_OK;
+ }
+
+ /* REG_BINARY
+ * uint32_t size = 0x114
+ * uint32_t major = 5
+ * uint32_t minor = [0|1]
+ * uint32_t build = [2195|2600]
+ * extra unicode string = e.g. "Service Pack 3"
+ */
+ if (!strcasecmp_m(value, "OSVersion")) {
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct spoolss_OSVersion os;
+
+ /*
+ * Set the default OSVersion to:
+ *
+ * Windows Server 2003R2 SP2 (5.2.3790)
+ *
+ * used to be Windows 2000 (5.0.2195)
+ */
+ os.major = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss", "os_major",
+ GLOBAL_SPOOLSS_OS_MAJOR_DEFAULT);
+ os.minor = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss", "os_minor",
+ GLOBAL_SPOOLSS_OS_MINOR_DEFAULT);
+ os.build = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss", "os_build",
+ GLOBAL_SPOOLSS_OS_BUILD_DEFAULT);
+ os.extra_string = ""; /* leave extra string empty */
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &os,
+ (ndr_push_flags_fn_t)ndr_push_spoolss_OSVersion);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(spoolss_OSVersion, &os);
+ }
+
+ *type = REG_BINARY;
+ data->binary = blob;
+
+ return WERR_OK;
+ }
+
+
+ if (!strcasecmp_m(value, "DefaultSpoolDirectory")) {
+ *type = REG_SZ;
+
+ data->string = talloc_strdup(mem_ctx, SPOOLSS_DEFAULT_SERVER_PATH);
+ W_ERROR_HAVE_NO_MEMORY(data->string);
+
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "Architecture")) {
+ *type = REG_SZ;
+ data->string = talloc_strdup(mem_ctx,
+ lp_parm_const_string(GLOBAL_SECTION_SNUM, "spoolss", "architecture", GLOBAL_SPOOLSS_ARCHITECTURE));
+ W_ERROR_HAVE_NO_MEMORY(data->string);
+
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "DsPresent")) {
+ *type = REG_DWORD;
+
+ /* only show the publish check box if we are a
+ member of a AD domain */
+
+ if (lp_security() == SEC_ADS) {
+ SIVAL(&data->value, 0, 0x01);
+ } else {
+ SIVAL(&data->value, 0, 0x00);
+ }
+ return WERR_OK;
+ }
+
+ if (!strcasecmp_m(value, "DNSMachineName")) {
+ const char *hostname = get_mydnsfullname();
+
+ if (!hostname) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ *type = REG_SZ;
+ data->string = talloc_strdup(mem_ctx, hostname);
+ W_ERROR_HAVE_NO_MEMORY(data->string);
+
+ return WERR_OK;
+ }
+
+ *type = REG_NONE;
+
+ return WERR_INVALID_PARAMETER;
+}
+
+/****************************************************************
+ _spoolss_GetPrinterData
+****************************************************************/
+
+WERROR _spoolss_GetPrinterData(struct pipes_struct *p,
+ struct spoolss_GetPrinterData *r)
+{
+ struct spoolss_GetPrinterDataEx r2;
+
+ r2.in.handle = r->in.handle;
+ r2.in.key_name = "PrinterDriverData";
+ r2.in.value_name = r->in.value_name;
+ r2.in.offered = r->in.offered;
+ r2.out.type = r->out.type;
+ r2.out.data = r->out.data;
+ r2.out.needed = r->out.needed;
+
+ return _spoolss_GetPrinterDataEx(p, &r2);
+}
+
+/*********************************************************
+ Connect to the client machine.
+**********************************************************/
+
+static bool spoolss_connect_to_client(struct rpc_pipe_client **pp_pipe, struct cli_state **pp_cli,
+ struct sockaddr_storage *client_ss, const char *remote_machine)
+{
+ NTSTATUS ret;
+ struct sockaddr_storage rm_addr;
+ char addr[INET6_ADDRSTRLEN];
+ struct cli_credentials *anon_creds = NULL;
+
+ if ( is_zero_addr(client_ss) ) {
+ DEBUG(2,("spoolss_connect_to_client: resolving %s\n",
+ remote_machine));
+ if ( !resolve_name( remote_machine, &rm_addr, 0x20, false) ) {
+ DEBUG(2,("spoolss_connect_to_client: Can't resolve address for %s\n", remote_machine));
+ return false;
+ }
+ print_sockaddr(addr, sizeof(addr), &rm_addr);
+ } else {
+ rm_addr = *client_ss;
+ print_sockaddr(addr, sizeof(addr), &rm_addr);
+ DEBUG(5,("spoolss_connect_to_client: Using address %s (no name resolution necessary)\n",
+ addr));
+ }
+
+ if (ismyaddr((struct sockaddr *)(void *)&rm_addr)) {
+ DEBUG(0,("spoolss_connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n",
+ addr));
+ return false;
+ }
+
+ anon_creds = cli_credentials_init_anon(NULL);
+ if (anon_creds == NULL) {
+ DBG_ERR("cli_credentials_init_anon() failed\n");
+ return false;
+ }
+
+ /* setup the connection */
+ ret = cli_full_connection_creds( pp_cli, lp_netbios_name(), remote_machine,
+ &rm_addr, 0, "IPC$", "IPC",
+ anon_creds,
+ CLI_FULL_CONNECTION_IPC);
+ TALLOC_FREE(anon_creds);
+ if ( !NT_STATUS_IS_OK( ret ) ) {
+ DEBUG(2,("spoolss_connect_to_client: connection to [%s] failed!\n",
+ remote_machine ));
+ return false;
+ }
+
+ if ( smbXcli_conn_protocol((*pp_cli)->conn) < PROTOCOL_NT1 ) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s didn't negotiate NT protocol.\n", remote_machine));
+ cli_shutdown(*pp_cli);
+ return false;
+ }
+
+ /*
+ * Ok - we have an anonymous connection to the IPC$ share.
+ * Now start the NT Domain stuff :-).
+ */
+
+ ret = cli_rpc_pipe_open_noauth(*pp_cli, &ndr_table_spoolss, pp_pipe);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(2,("spoolss_connect_to_client: unable to open the spoolss pipe on machine %s. Error was : %s.\n",
+ remote_machine, nt_errstr(ret)));
+ cli_shutdown(*pp_cli);
+ return false;
+ }
+
+ return true;
+}
+
+/***************************************************************************
+ Connect to the client.
+****************************************************************************/
+
+static bool srv_spoolss_replyopenprinter(int snum, const char *printer,
+ uint32_t localprinter,
+ enum winreg_Type type,
+ struct policy_handle *handle,
+ struct notify_back_channel **_chan,
+ struct sockaddr_storage *client_ss,
+ struct messaging_context *msg_ctx)
+{
+ WERROR result;
+ NTSTATUS status;
+ struct notify_back_channel *chan;
+
+ for (chan = back_channels; chan; chan = chan->next) {
+ if (memcmp(&chan->client_address, client_ss,
+ sizeof(struct sockaddr_storage)) == 0) {
+ break;
+ }
+ }
+
+ /*
+ * If it's the first connection, contact the client
+ * and connect to the IPC$ share anonymously
+ */
+ if (!chan) {
+ fstring unix_printer;
+
+ /* the +2 is to strip the leading 2 backslashes */
+ fstrcpy(unix_printer, printer + 2);
+
+ chan = talloc_zero(NULL, struct notify_back_channel);
+ if (!chan) {
+ return false;
+ }
+ chan->client_address = *client_ss;
+
+ if (!spoolss_connect_to_client(&chan->cli_pipe, &chan->cli, client_ss, unix_printer)) {
+ TALLOC_FREE(chan);
+ return false;
+ }
+
+ DLIST_ADD(back_channels, chan);
+
+ messaging_register(msg_ctx, NULL, MSG_PRINTER_NOTIFY2,
+ receive_notify2_message_list);
+ }
+
+ if (chan->cli_pipe == NULL ||
+ chan->cli_pipe->binding_handle == NULL) {
+ DEBUG(0, ("srv_spoolss_replyopenprinter: error - "
+ "NULL %s for printer %s\n",
+ chan->cli_pipe == NULL ?
+ "chan->cli_pipe" : "chan->cli_pipe->binding_handle",
+ printer));
+ return false;
+ }
+
+ /*
+ * Tell the specific printing tdb we want messages for this printer
+ * by registering our PID.
+ */
+
+ if (!print_notify_register_pid(snum)) {
+ DEBUG(0, ("Failed to register our pid for printer %s\n",
+ printer));
+ }
+
+ status = dcerpc_spoolss_ReplyOpenPrinter(chan->cli_pipe->binding_handle,
+ talloc_tos(),
+ printer,
+ localprinter,
+ type,
+ 0,
+ NULL,
+ handle,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("dcerpc_spoolss_ReplyOpenPrinter returned [%s]\n", nt_errstr(status)));
+ result = ntstatus_to_werror(status);
+ } else if (!W_ERROR_IS_OK(result)) {
+ DEBUG(5, ("ReplyOpenPrinter returned [%s]\n", win_errstr(result)));
+ }
+
+ chan->active_connections++;
+ *_chan = chan;
+
+ return (W_ERROR_IS_OK(result));
+}
+
+/****************************************************************
+ ****************************************************************/
+
+static struct spoolss_NotifyOption *dup_spoolss_NotifyOption(TALLOC_CTX *mem_ctx,
+ const struct spoolss_NotifyOption *r)
+{
+ struct spoolss_NotifyOption *option;
+ uint32_t i,k;
+
+ if (!r) {
+ return NULL;
+ }
+
+ option = talloc_zero(mem_ctx, struct spoolss_NotifyOption);
+ if (!option) {
+ return NULL;
+ }
+
+ *option = *r;
+
+ if (!option->count) {
+ return option;
+ }
+
+ option->types = talloc_zero_array(option,
+ struct spoolss_NotifyOptionType, option->count);
+ if (!option->types) {
+ talloc_free(option);
+ return NULL;
+ }
+
+ for (i=0; i < option->count; i++) {
+ option->types[i] = r->types[i];
+
+ if (option->types[i].count) {
+ option->types[i].fields = talloc_zero_array(option,
+ union spoolss_Field, option->types[i].count);
+ if (!option->types[i].fields) {
+ talloc_free(option);
+ return NULL;
+ }
+ for (k=0; k<option->types[i].count; k++) {
+ option->types[i].fields[k] =
+ r->types[i].fields[k];
+ }
+ }
+ }
+
+ return option;
+}
+
+/****************************************************************
+ * _spoolss_RemoteFindFirstPrinterChangeNotifyEx
+ *
+ * before replying OK: status=0 a rpc call is made to the workstation
+ * asking ReplyOpenPrinter
+ *
+ * in fact ReplyOpenPrinter is the changenotify equivalent on the spoolss pipe
+ * called from api_spoolss_rffpcnex
+****************************************************************/
+
+WERROR _spoolss_RemoteFindFirstPrinterChangeNotifyEx(struct pipes_struct *p,
+ struct spoolss_RemoteFindFirstPrinterChangeNotifyEx *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ int snum = -1;
+ struct spoolss_NotifyOption *option = r->in.notify_options;
+ struct sockaddr_storage client_ss;
+ ssize_t client_len;
+
+ /* store the notify value in the printer struct */
+
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_RemoteFindFirstPrinterChangeNotifyEx: "
+ "Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ Printer->notify.flags = r->in.flags;
+ Printer->notify.options = r->in.options;
+ Printer->notify.printerlocal = r->in.printer_local;
+ Printer->notify.msg_ctx = p->msg_ctx;
+
+ TALLOC_FREE(Printer->notify.option);
+ Printer->notify.option = dup_spoolss_NotifyOption(Printer, option);
+
+ fstrcpy(Printer->notify.localmachine, r->in.local_machine);
+
+ /* Connect to the client machine and send a ReplyOpenPrinter */
+
+ if ( Printer->printer_type == SPLHND_SERVER)
+ snum = -1;
+ else if ( (Printer->printer_type == SPLHND_PRINTER) &&
+ !get_printer_snum(p, r->in.handle, &snum, NULL) )
+ return WERR_INVALID_HANDLE;
+
+ DEBUG(10,("_spoolss_RemoteFindFirstPrinterChangeNotifyEx: "
+ "remote_address is %s\n",
+ tsocket_address_string(remote_address, p->mem_ctx)));
+
+ if (!lp_print_notify_backchannel(snum)) {
+ DEBUG(10, ("_spoolss_RemoteFindFirstPrinterChangeNotifyEx: "
+ "backchannel disabled\n"));
+ return WERR_RPC_S_SERVER_UNAVAILABLE;
+ }
+
+ client_len = tsocket_address_bsd_sockaddr(remote_address,
+ (struct sockaddr *) &client_ss,
+ sizeof(struct sockaddr_storage));
+ if (client_len < 0) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine,
+ Printer->notify.printerlocal, REG_SZ,
+ &Printer->notify.cli_hnd,
+ &Printer->notify.cli_chan,
+ &client_ss, p->msg_ctx)) {
+ return WERR_RPC_S_SERVER_UNAVAILABLE;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servername
+ ********************************************************************/
+
+static void spoolss_notify_server_name(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->servername);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername (not including the servername).
+ ********************************************************************/
+
+static void spoolss_notify_printer_name(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ /* the notify name should not contain the \\server\ part */
+ const char *p = strrchr(pinfo2->printername, '\\');
+
+ if (!p) {
+ p = pinfo2->printername;
+ } else {
+ p++;
+ }
+
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, p);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servicename
+ ********************************************************************/
+
+static void spoolss_notify_share_name(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, lp_servicename(talloc_tos(), lp_sub, snum));
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the port name
+ ********************************************************************/
+
+static void spoolss_notify_port_name(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->portname);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername
+ * but it doesn't exist, have to see what to do
+ ********************************************************************/
+
+static void spoolss_notify_driver_name(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->drivername);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ ********************************************************************/
+
+static void spoolss_notify_comment(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *p;
+
+ if (*pinfo2->comment == '\0') {
+ p = lp_comment(talloc_tos(), lp_sub, snum);
+ } else {
+ p = pinfo2->comment;
+ }
+
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, p);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ * location = "Room 1, floor 2, building 3"
+ ********************************************************************/
+
+static void spoolss_notify_location(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ const char *loc = pinfo2->location;
+ NTSTATUS status;
+
+ status = printer_list_get_printer(mem_ctx,
+ pinfo2->sharename,
+ NULL,
+ &loc,
+ NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ if (loc == NULL) {
+ loc = pinfo2->location;
+ }
+ }
+
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, loc);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the device mode
+ * jfm:xxxx don't to it for know but that's a real problem !!!
+ ********************************************************************/
+
+static void spoolss_notify_devmode(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ /* for a dummy implementation we have to zero the fields */
+ SETUP_SPOOLSS_NOTIFY_DATA_DEVMODE(data, NULL);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the separator file name
+ ********************************************************************/
+
+static void spoolss_notify_sepfile(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->sepfile);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor
+ * jfm:xxxx return always winprint to indicate we don't do anything to it
+ ********************************************************************/
+
+static void spoolss_notify_print_processor(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->printprocessor);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor options
+ * jfm:xxxx send an empty string
+ ********************************************************************/
+
+static void spoolss_notify_parameters(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->parameters);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the data type
+ * jfm:xxxx always send RAW as data type
+ ********************************************************************/
+
+static void spoolss_notify_datatype(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, pinfo2->datatype);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the security descriptor
+ * jfm:xxxx send an null pointer to say no security desc
+ * have to implement security before !
+ ********************************************************************/
+
+static void spoolss_notify_security_desc(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ if (pinfo2->secdesc == NULL) {
+ data->data.sd.sd = NULL;
+ } else {
+ data->data.sd.sd = security_descriptor_copy(mem_ctx,
+ pinfo2->secdesc);
+ }
+ data->data.sd.sd_size = ndr_size_security_descriptor(data->data.sd.sd,
+ 0);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the attributes
+ * jfm:xxxx a samba printer is always shared
+ ********************************************************************/
+
+static void spoolss_notify_attributes(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, pinfo2->attributes);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the priority
+ ********************************************************************/
+
+static void spoolss_notify_priority(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, pinfo2->priority);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the default priority
+ ********************************************************************/
+
+static void spoolss_notify_default_priority(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, pinfo2->defaultpriority);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the start time
+ ********************************************************************/
+
+static void spoolss_notify_start_time(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, pinfo2->starttime);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the until time
+ ********************************************************************/
+
+static void spoolss_notify_until_time(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, pinfo2->untiltime);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the status
+ ********************************************************************/
+
+static void spoolss_notify_status(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ print_status_struct status;
+
+ print_queue_length(msg_ctx, snum, &status);
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, status.status);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the number of jobs queued
+ ********************************************************************/
+
+static void spoolss_notify_cjobs(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(
+ data, print_queue_length(msg_ctx, snum, NULL));
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the average ppm
+ ********************************************************************/
+
+static void spoolss_notify_average_ppm(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ /* always respond 8 pages per minutes */
+ /* a little hard ! */
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, pinfo2->averageppm);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with username
+ ********************************************************************/
+
+static void spoolss_notify_username(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, queue->fs_user);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, nt_printj_status(queue->status));
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job name
+ ********************************************************************/
+
+static void spoolss_notify_job_name(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, queue->fs_file);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status_string(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ /*
+ * Now we're returning job status codes we just return a "" here. JRA.
+ */
+
+ const char *p = "";
+
+#if 0 /* NO LONGER NEEDED - JRA. 02/22/2001 */
+ p = "unknown";
+
+ switch (queue->status) {
+ case LPQ_QUEUED:
+ p = "Queued";
+ break;
+ case LPQ_PAUSED:
+ p = ""; /* NT provides the paused string */
+ break;
+ case LPQ_SPOOLING:
+ p = "Spooling";
+ break;
+ case LPQ_PRINTING:
+ p = "Printing";
+ break;
+ }
+#endif /* NO LONGER NEEDED. */
+
+ SETUP_SPOOLSS_NOTIFY_DATA_STRING(data, p);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job time
+ ********************************************************************/
+
+static void spoolss_notify_job_time(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, 0);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job size
+ ********************************************************************/
+
+static void spoolss_notify_job_size(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, queue->size);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with page info
+ ********************************************************************/
+static void spoolss_notify_total_pages(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, queue->page_count);
+}
+
+/*******************************************************************
+ * fill a notify_info_data with pages printed info.
+ ********************************************************************/
+static void spoolss_notify_pages_printed(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ /* Add code when back-end tracks this */
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, 0);
+}
+
+/*******************************************************************
+ Fill a notify_info_data with job position.
+ ********************************************************************/
+
+static void spoolss_notify_job_position(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ SETUP_SPOOLSS_NOTIFY_DATA_INTEGER(data, queue->sysjob);
+}
+
+/*******************************************************************
+ Fill a notify_info_data with submitted time.
+ ********************************************************************/
+
+static void spoolss_notify_submitted_time(struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx)
+{
+ data->data.string.string = NULL;
+ data->data.string.size = 0;
+
+ init_systemtime_buffer(mem_ctx, gmtime(&queue->time),
+ &data->data.string.string,
+ &data->data.string.size);
+
+}
+
+struct s_notify_info_data_table
+{
+ enum spoolss_NotifyType type;
+ uint16_t field;
+ const char *name;
+ enum spoolss_NotifyTable variable_type;
+ void (*fn) (struct messaging_context *msg_ctx,
+ int snum, struct spoolss_Notify *data,
+ print_queue_struct *queue,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ TALLOC_CTX *mem_ctx);
+};
+
+/* A table describing the various print notification constants and
+ whether the notification data is a pointer to a variable sized
+ buffer, a one value uint32_t or a two value uint32_t. */
+
+static const struct s_notify_info_data_table notify_info_data_table[] =
+{
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_SERVER_NAME, "PRINTER_NOTIFY_FIELD_SERVER_NAME", NOTIFY_TABLE_STRING, spoolss_notify_server_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_PRINTER_NAME, "PRINTER_NOTIFY_FIELD_PRINTER_NAME", NOTIFY_TABLE_STRING, spoolss_notify_printer_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_SHARE_NAME, "PRINTER_NOTIFY_FIELD_SHARE_NAME", NOTIFY_TABLE_STRING, spoolss_notify_share_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_PORT_NAME, "PRINTER_NOTIFY_FIELD_PORT_NAME", NOTIFY_TABLE_STRING, spoolss_notify_port_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_DRIVER_NAME, "PRINTER_NOTIFY_FIELD_DRIVER_NAME", NOTIFY_TABLE_STRING, spoolss_notify_driver_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_COMMENT, "PRINTER_NOTIFY_FIELD_COMMENT", NOTIFY_TABLE_STRING, spoolss_notify_comment },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_LOCATION, "PRINTER_NOTIFY_FIELD_LOCATION", NOTIFY_TABLE_STRING, spoolss_notify_location },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_DEVMODE, "PRINTER_NOTIFY_FIELD_DEVMODE", NOTIFY_TABLE_DEVMODE, spoolss_notify_devmode },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_SEPFILE, "PRINTER_NOTIFY_FIELD_SEPFILE", NOTIFY_TABLE_STRING, spoolss_notify_sepfile },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR, "PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR", NOTIFY_TABLE_STRING, spoolss_notify_print_processor },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_PARAMETERS, "PRINTER_NOTIFY_FIELD_PARAMETERS", NOTIFY_TABLE_STRING, spoolss_notify_parameters },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_DATATYPE, "PRINTER_NOTIFY_FIELD_DATATYPE", NOTIFY_TABLE_STRING, spoolss_notify_datatype },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR", NOTIFY_TABLE_SECURITYDESCRIPTOR, spoolss_notify_security_desc },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_ATTRIBUTES, "PRINTER_NOTIFY_FIELD_ATTRIBUTES", NOTIFY_TABLE_DWORD, spoolss_notify_attributes },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_PRIORITY, "PRINTER_NOTIFY_FIELD_PRIORITY", NOTIFY_TABLE_DWORD, spoolss_notify_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY, "PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY", NOTIFY_TABLE_DWORD, spoolss_notify_default_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_START_TIME, "PRINTER_NOTIFY_FIELD_START_TIME", NOTIFY_TABLE_DWORD, spoolss_notify_start_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_UNTIL_TIME, "PRINTER_NOTIFY_FIELD_UNTIL_TIME", NOTIFY_TABLE_DWORD, spoolss_notify_until_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_STATUS, "PRINTER_NOTIFY_FIELD_STATUS", NOTIFY_TABLE_DWORD, spoolss_notify_status },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_STATUS_STRING, "PRINTER_NOTIFY_FIELD_STATUS_STRING", NOTIFY_TABLE_STRING, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_CJOBS, "PRINTER_NOTIFY_FIELD_CJOBS", NOTIFY_TABLE_DWORD, spoolss_notify_cjobs },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_AVERAGE_PPM, "PRINTER_NOTIFY_FIELD_AVERAGE_PPM", NOTIFY_TABLE_DWORD, spoolss_notify_average_ppm },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_TOTAL_PAGES, "PRINTER_NOTIFY_FIELD_TOTAL_PAGES", NOTIFY_TABLE_DWORD, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_PAGES_PRINTED, "PRINTER_NOTIFY_FIELD_PAGES_PRINTED", NOTIFY_TABLE_DWORD, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_TOTAL_BYTES, "PRINTER_NOTIFY_FIELD_TOTAL_BYTES", NOTIFY_TABLE_DWORD, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_FIELD_BYTES_PRINTED, "PRINTER_NOTIFY_FIELD_BYTES_PRINTED", NOTIFY_TABLE_DWORD, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_PRINTER_NAME, "JOB_NOTIFY_FIELD_PRINTER_NAME", NOTIFY_TABLE_STRING, spoolss_notify_printer_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_MACHINE_NAME, "JOB_NOTIFY_FIELD_MACHINE_NAME", NOTIFY_TABLE_STRING, spoolss_notify_server_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_PORT_NAME, "JOB_NOTIFY_FIELD_PORT_NAME", NOTIFY_TABLE_STRING, spoolss_notify_port_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_USER_NAME, "JOB_NOTIFY_FIELD_USER_NAME", NOTIFY_TABLE_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_NOTIFY_NAME, "JOB_NOTIFY_FIELD_NOTIFY_NAME", NOTIFY_TABLE_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_DATATYPE, "JOB_NOTIFY_FIELD_DATATYPE", NOTIFY_TABLE_STRING, spoolss_notify_datatype },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_PRINT_PROCESSOR, "JOB_NOTIFY_FIELD_PRINT_PROCESSOR", NOTIFY_TABLE_STRING, spoolss_notify_print_processor },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_PARAMETERS, "JOB_NOTIFY_FIELD_PARAMETERS", NOTIFY_TABLE_STRING, spoolss_notify_parameters },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_DRIVER_NAME, "JOB_NOTIFY_FIELD_DRIVER_NAME", NOTIFY_TABLE_STRING, spoolss_notify_driver_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_DEVMODE, "JOB_NOTIFY_FIELD_DEVMODE", NOTIFY_TABLE_DEVMODE, spoolss_notify_devmode },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_STATUS, "JOB_NOTIFY_FIELD_STATUS", NOTIFY_TABLE_DWORD, spoolss_notify_job_status },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_STATUS_STRING, "JOB_NOTIFY_FIELD_STATUS_STRING", NOTIFY_TABLE_STRING, spoolss_notify_job_status_string },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR, "JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR", NOTIFY_TABLE_SECURITYDESCRIPTOR, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_DOCUMENT, "JOB_NOTIFY_FIELD_DOCUMENT", NOTIFY_TABLE_STRING, spoolss_notify_job_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_PRIORITY, "JOB_NOTIFY_FIELD_PRIORITY", NOTIFY_TABLE_DWORD, spoolss_notify_priority },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_POSITION, "JOB_NOTIFY_FIELD_POSITION", NOTIFY_TABLE_DWORD, spoolss_notify_job_position },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_SUBMITTED, "JOB_NOTIFY_FIELD_SUBMITTED", NOTIFY_TABLE_TIME, spoolss_notify_submitted_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_START_TIME, "JOB_NOTIFY_FIELD_START_TIME", NOTIFY_TABLE_DWORD, spoolss_notify_start_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_UNTIL_TIME, "JOB_NOTIFY_FIELD_UNTIL_TIME", NOTIFY_TABLE_DWORD, spoolss_notify_until_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_TIME, "JOB_NOTIFY_FIELD_TIME", NOTIFY_TABLE_DWORD, spoolss_notify_job_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_TOTAL_PAGES, "JOB_NOTIFY_FIELD_TOTAL_PAGES", NOTIFY_TABLE_DWORD, spoolss_notify_total_pages },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_PAGES_PRINTED, "JOB_NOTIFY_FIELD_PAGES_PRINTED", NOTIFY_TABLE_DWORD, spoolss_notify_pages_printed },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_FIELD_TOTAL_BYTES, "JOB_NOTIFY_FIELD_TOTAL_BYTES", NOTIFY_TABLE_DWORD, spoolss_notify_job_size },
+};
+
+/*******************************************************************
+ Return the variable_type of info_data structure.
+********************************************************************/
+
+static enum spoolss_NotifyTable variable_type_of_notify_info_data(enum spoolss_NotifyType type,
+ uint16_t field)
+{
+ int i=0;
+
+ for (i = 0; i < ARRAY_SIZE(notify_info_data_table); i++) {
+ if ( (notify_info_data_table[i].type == type) &&
+ (notify_info_data_table[i].field == field) ) {
+ return notify_info_data_table[i].variable_type;
+ }
+ }
+
+ DEBUG(5, ("invalid notify data type %d/%d\n", type, field));
+
+ return (enum spoolss_NotifyTable) 0;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool search_notify(enum spoolss_NotifyType type,
+ uint16_t field,
+ int *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(notify_info_data_table); i++) {
+ if (notify_info_data_table[i].type == type &&
+ notify_info_data_table[i].field == field &&
+ notify_info_data_table[i].fn != NULL) {
+ *value = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void construct_info_data(struct spoolss_Notify *info_data,
+ enum spoolss_NotifyType type,
+ uint16_t field, int id)
+{
+ info_data->type = type;
+ info_data->field.field = field;
+ info_data->variable_type = variable_type_of_notify_info_data(type, field);
+ info_data->job_id = id;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static bool construct_notify_printer_info(struct messaging_context *msg_ctx,
+ struct printer_handle *print_hnd,
+ struct spoolss_NotifyInfo *info,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ int snum,
+ const struct spoolss_NotifyOptionType *option_type,
+ uint32_t id,
+ TALLOC_CTX *mem_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int field_num,j;
+ enum spoolss_NotifyType type;
+ uint16_t field;
+
+ struct spoolss_Notify *current_data;
+
+ type = option_type->type;
+
+ DEBUG(4,("construct_notify_printer_info: Notify type: [%s], number of notify info: [%d] on printer: [%s]\n",
+ (type == PRINTER_NOTIFY_TYPE ? "PRINTER_NOTIFY_TYPE" : "JOB_NOTIFY_TYPE"),
+ option_type->count, lp_servicename(talloc_tos(), lp_sub, snum)));
+
+ for(field_num=0; field_num < option_type->count; field_num++) {
+ field = option_type->fields[field_num].field;
+
+ DEBUG(4,("construct_notify_printer_info: notify [%d]: type [%x], field [%x]\n", field_num, type, field));
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ info->notifies = talloc_realloc(info, info->notifies,
+ struct spoolss_Notify,
+ info->count + 1);
+ if (info->notifies == NULL) {
+ DEBUG(2,("construct_notify_printer_info: failed to enlarge buffer info->data!\n"));
+ return false;
+ }
+
+ current_data = &info->notifies[info->count];
+
+ construct_info_data(current_data, type, field, id);
+
+ DEBUG(10, ("construct_notify_printer_info: "
+ "calling [%s] snum=%d printername=[%s])\n",
+ notify_info_data_table[j].name, snum,
+ pinfo2->printername));
+
+ notify_info_data_table[j].fn(msg_ctx, snum, current_data,
+ NULL, pinfo2, mem_ctx);
+
+ info->count++;
+ }
+
+ return true;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static bool construct_notify_jobs_info(struct messaging_context *msg_ctx,
+ print_queue_struct *queue,
+ struct spoolss_NotifyInfo *info,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ int snum,
+ const struct spoolss_NotifyOptionType *option_type,
+ uint32_t id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ enum spoolss_NotifyType type;
+ uint16_t field;
+ struct spoolss_Notify *current_data;
+
+ DEBUG(4,("construct_notify_jobs_info\n"));
+
+ type = option_type->type;
+
+ DEBUGADD(4,("Notify type: [%s], number of notify info: [%d]\n",
+ (type == PRINTER_NOTIFY_TYPE ? "PRINTER_NOTIFY_TYPE" : "JOB_NOTIFY_TYPE"),
+ option_type->count));
+
+ for(field_num=0; field_num<option_type->count; field_num++) {
+ field = option_type->fields[field_num].field;
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ info->notifies = talloc_realloc(info, info->notifies,
+ struct spoolss_Notify,
+ info->count + 1);
+ if (info->notifies == NULL) {
+ DEBUG(2,("construct_notify_jobs_info: failed to enlarge buffer info->data!\n"));
+ return false;
+ }
+
+ current_data=&(info->notifies[info->count]);
+
+ construct_info_data(current_data, type, field, id);
+ notify_info_data_table[j].fn(msg_ctx, snum, current_data,
+ queue, pinfo2, mem_ctx);
+ info->count++;
+ }
+
+ return true;
+}
+
+/*
+ * JFM: The enumeration is not that simple, it's even non obvious.
+ *
+ * let's take an example: I want to monitor the PRINTER SERVER for
+ * the printer's name and the number of jobs currently queued.
+ * So in the NOTIFY_OPTION, I have one NOTIFY_OPTION_TYPE structure.
+ * Its type is PRINTER_NOTIFY_TYPE and it has 2 fields NAME and CJOBS.
+ *
+ * I have 3 printers on the back of my server.
+ *
+ * Now the response is a NOTIFY_INFO structure, with 6 NOTIFY_INFO_DATA
+ * structures.
+ * Number Data Id
+ * 1 printer 1 name 1
+ * 2 printer 1 cjob 1
+ * 3 printer 2 name 2
+ * 4 printer 2 cjob 2
+ * 5 printer 3 name 3
+ * 6 printer 3 name 3
+ *
+ * that's the print server case, the printer case is even worse.
+ */
+
+/*******************************************************************
+ *
+ * enumerate all printers on the printserver
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printserver_notify_info(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ struct spoolss_NotifyInfo *info,
+ TALLOC_CTX *mem_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int snum;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, hnd);
+ int n_services=lp_numservices();
+ int i;
+ struct spoolss_NotifyOption *option;
+ struct spoolss_NotifyOptionType option_type;
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ WERROR result;
+
+ DEBUG(4,("printserver_notify_info\n"));
+
+ if (!Printer)
+ return WERR_INVALID_HANDLE;
+
+ option = Printer->notify.option;
+
+ info->version = 2;
+ info->notifies = NULL;
+ info->count = 0;
+
+ /* a bug in xp sp2 rc2 causes it to send a fnpcn request without
+ sending a ffpcn() request first */
+
+ if ( !option )
+ return WERR_INVALID_HANDLE;
+
+ for (i=0; i<option->count; i++) {
+ option_type = option->types[i];
+
+ if (option_type.type != PRINTER_NOTIFY_TYPE)
+ continue;
+
+ for (snum = 0; snum < n_services; snum++) {
+ if (!lp_browseable(snum) ||
+ !lp_snum_ok(snum) ||
+ !lp_printable(snum)) {
+ continue; /* skip */
+ }
+
+ /* Maybe we should use the SYSTEM session_info here... */
+ result = winreg_get_printer_internal(mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_servicename(talloc_tos(), lp_sub, snum),
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(4, ("printserver_notify_info: "
+ "Failed to get printer [%s]\n",
+ lp_servicename(talloc_tos(), lp_sub, snum)));
+ continue;
+ }
+
+
+ construct_notify_printer_info(p->msg_ctx,
+ Printer, info,
+ pinfo2, snum,
+ &option_type, snum,
+ mem_ctx);
+
+ TALLOC_FREE(pinfo2);
+ }
+ }
+
+#if 0
+ /*
+ * Debugging information, don't delete.
+ */
+
+ DEBUG(1,("dumping the NOTIFY_INFO\n"));
+ DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+ DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+
+ for (i=0; i<info->count; i++) {
+ DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+ i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+ info->data[i].id, info->data[i].size, info->data[i].enc_type));
+ }
+#endif
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static WERROR printer_notify_info(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ struct spoolss_NotifyInfo *info,
+ TALLOC_CTX *mem_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int snum;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, hnd);
+ int i;
+ uint32_t id;
+ struct spoolss_NotifyOption *option;
+ struct spoolss_NotifyOptionType option_type;
+ int count,j;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ WERROR result;
+ struct tdb_print_db *pdb;
+
+ DEBUG(4,("printer_notify_info\n"));
+
+ if (!Printer)
+ return WERR_INVALID_HANDLE;
+
+ option = Printer->notify.option;
+ id = 0x0;
+
+ info->version = 2;
+ info->notifies = NULL;
+ info->count = 0;
+
+ /* a bug in xp sp2 rc2 causes it to send a fnpcn request without
+ sending a ffpcn() request first */
+
+ if ( !option )
+ return WERR_INVALID_HANDLE;
+
+ if (!get_printer_snum(p, hnd, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ pdb = get_print_db_byname(Printer->sharename);
+ if (pdb == NULL) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* Maybe we should use the SYSTEM session_info here... */
+ result = winreg_get_printer_internal(mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_servicename(talloc_tos(), lp_sub, snum), &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ result = WERR_INVALID_HANDLE;
+ goto err_pdb_drop;
+ }
+
+ /*
+ * When sending a PRINTER_NOTIFY_FIELD_SERVER_NAME we should send the
+ * correct servername.
+ */
+ pinfo2->servername = talloc_strdup(pinfo2, Printer->servername);
+ if (pinfo2->servername == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto err_pdb_drop;
+ }
+
+ for (i = 0; i < option->count; i++) {
+ option_type = option->types[i];
+
+ switch (option_type.type) {
+ case PRINTER_NOTIFY_TYPE:
+ if (construct_notify_printer_info(p->msg_ctx,
+ Printer, info,
+ pinfo2, snum,
+ &option_type, id,
+ mem_ctx)) {
+ id--;
+ }
+ break;
+
+ case JOB_NOTIFY_TYPE:
+
+ count = print_queue_status(p->msg_ctx, snum, &queue,
+ &status);
+
+ for (j = 0; j < count; j++) {
+ uint32_t jobid;
+ jobid = sysjob_to_jobid_pdb(pdb,
+ queue[j].sysjob);
+ if (jobid == (uint32_t)-1) {
+ DEBUG(2, ("ignoring untracked job %d\n",
+ queue[j].sysjob));
+ continue;
+ }
+ /* FIXME check return value */
+ construct_notify_jobs_info(p->msg_ctx,
+ &queue[j], info,
+ pinfo2, snum,
+ &option_type,
+ jobid,
+ mem_ctx);
+ }
+
+ SAFE_FREE(queue);
+ break;
+ }
+ }
+
+ /*
+ * Debugging information, don't delete.
+ */
+ /*
+ DEBUG(1,("dumping the NOTIFY_INFO\n"));
+ DEBUGADD(1,("info->version:[%d], info->flags:[%d], info->count:[%d]\n", info->version, info->flags, info->count));
+ DEBUGADD(1,("num\ttype\tfield\tres\tid\tsize\tenc_type\n"));
+
+ for (i=0; i<info->count; i++) {
+ DEBUGADD(1,("[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\t[%d]\n",
+ i, info->data[i].type, info->data[i].field, info->data[i].reserved,
+ info->data[i].id, info->data[i].size, info->data[i].enc_type));
+ }
+ */
+
+ talloc_free(pinfo2);
+ result = WERR_OK;
+err_pdb_drop:
+ release_print_db(pdb);
+ return result;
+}
+
+/****************************************************************
+ _spoolss_RouterRefreshPrinterChangeNotify
+****************************************************************/
+
+WERROR _spoolss_RouterRefreshPrinterChangeNotify(struct pipes_struct *p,
+ struct spoolss_RouterRefreshPrinterChangeNotify *r)
+{
+ struct spoolss_NotifyInfo *info;
+
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ WERROR result = WERR_INVALID_HANDLE;
+
+ /* we always have a spoolss_NotifyInfo struct */
+ info = talloc_zero(p->mem_ctx, struct spoolss_NotifyInfo);
+ if (!info) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ *r->out.info = info;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_RouterRefreshPrinterChangeNotify: "
+ "Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ goto done;
+ }
+
+ DEBUG(4,("Printer type %x\n",Printer->printer_type));
+
+ /*
+ * We are now using the change value, and
+ * I should check for PRINTER_NOTIFY_OPTIONS_REFRESH but as
+ * I don't have a global notification system, I'm sending back all the
+ * information even when _NOTHING_ has changed.
+ */
+
+ /* We need to keep track of the change value to send back in
+ RRPCN replies otherwise our updates are ignored. */
+
+ Printer->notify.fnpcn = true;
+
+ if (Printer->notify.cli_chan != NULL &&
+ Printer->notify.cli_chan->active_connections > 0) {
+ DEBUG(10,("_spoolss_RouterRefreshPrinterChangeNotify: "
+ "Saving change value in request [%x]\n",
+ r->in.change_low));
+ Printer->notify.change = r->in.change_low;
+ }
+
+ /* just ignore the spoolss_NotifyOption */
+
+ switch (Printer->printer_type) {
+ case SPLHND_SERVER:
+ result = printserver_notify_info(p, r->in.handle,
+ info, p->mem_ctx);
+ break;
+
+ case SPLHND_PRINTER:
+ result = printer_notify_info(p, r->in.handle,
+ info, p->mem_ctx);
+ break;
+ }
+
+ Printer->notify.fnpcn = false;
+
+done:
+ return result;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static WERROR create_printername(TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *printername,
+ const char **printername_p)
+{
+ /* FIXME: add lp_force_printername() */
+
+ if (servername == NULL) {
+ *printername_p = talloc_strdup(mem_ctx, printername);
+ W_ERROR_HAVE_NO_MEMORY(*printername_p);
+ return WERR_OK;
+ }
+
+ if (servername[0] == '\\' && servername[1] == '\\') {
+ servername += 2;
+ }
+
+ *printername_p = talloc_asprintf(mem_ctx, "\\\\%s\\%s", servername, printername);
+ W_ERROR_HAVE_NO_MEMORY(*printername_p);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static void compose_devicemode_devicename(struct spoolss_DeviceMode *dm,
+ const char *printername)
+{
+ if (dm == NULL) {
+ return;
+ }
+
+ dm->devicename = talloc_strndup(dm, printername,
+ MIN(strlen(printername), 31));
+}
+
+/********************************************************************
+ * construct_printer_info_0
+ * fill a printer_info_0 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info0(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_PrinterInfo0 *r,
+ int snum)
+{
+ int count;
+ struct printer_session_counter *session_counter;
+ struct timeval setuptime;
+ print_status_struct status;
+ WERROR result;
+ int os_major, os_minor, os_build;
+ const char *architecture;
+ uint32_t processor_architecture, processor_type;
+
+ result = create_printername(mem_ctx, servername, info2->printername, &r->printername);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ if (servername) {
+ r->servername = talloc_strdup(mem_ctx, servername);
+ W_ERROR_HAVE_NO_MEMORY(r->servername);
+ } else {
+ r->servername = NULL;
+ }
+
+ count = print_queue_length(msg_ctx, snum, &status);
+
+ /* check if we already have a counter for this printer */
+ for (session_counter = counter_list; session_counter; session_counter = session_counter->next) {
+ if (session_counter->snum == snum)
+ break;
+ }
+
+ /* it's the first time, add it to the list */
+ if (session_counter == NULL) {
+ session_counter = talloc_zero(counter_list, struct printer_session_counter);
+ W_ERROR_HAVE_NO_MEMORY(session_counter);
+ session_counter->snum = snum;
+ session_counter->counter = 0;
+ DLIST_ADD(counter_list, session_counter);
+ }
+
+ /* increment it */
+ session_counter->counter++;
+
+ r->cjobs = count;
+ r->total_jobs = 0;
+ r->total_bytes = 0;
+
+ get_startup_time(&setuptime);
+ init_systemtime(&r->time, gmtime(&setuptime.tv_sec));
+
+ /* JFM:
+ * the global_counter should be stored in a TDB as it's common to all the clients
+ * and should be zeroed on samba startup
+ */
+ r->global_counter = session_counter->counter;
+ r->total_pages = 0;
+
+ /* in 2.2 we reported ourselves as 0x0004 and 0x0565 */
+ os_major = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss", "os_major",
+ GLOBAL_SPOOLSS_OS_MAJOR_DEFAULT);
+ os_minor = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss", "os_minor",
+ GLOBAL_SPOOLSS_OS_MINOR_DEFAULT);
+ os_build = lp_parm_int(GLOBAL_SECTION_SNUM,
+ "spoolss", "os_build",
+ GLOBAL_SPOOLSS_OS_BUILD_DEFAULT);
+
+ SCVAL(&r->version, 0, os_major);
+ SCVAL(&r->version, 1, os_minor);
+ SSVAL(&r->version, 2, os_build);
+
+ architecture = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+ "spoolss",
+ "architecture",
+ GLOBAL_SPOOLSS_ARCHITECTURE);
+
+ if (strequal(architecture, SPOOLSS_ARCHITECTURE_x64)) {
+ processor_architecture = PROCESSOR_ARCHITECTURE_AMD64;
+ processor_type = PROCESSOR_AMD_X8664;
+ } else if (strequal(architecture, SPOOLSS_ARCHITECTURE_ARM64)) {
+ processor_architecture = PROCESSOR_ARCHITECTURE_ARM64;
+ processor_type = PROCESSOR_ARM820;
+ } else {
+ processor_architecture = PROCESSOR_ARCHITECTURE_INTEL;
+ processor_type = PROCESSOR_INTEL_PENTIUM;
+ }
+
+ r->free_build = SPOOLSS_RELEASE_BUILD;
+ r->spooling = 0;
+ r->max_spooling = 0;
+ r->session_counter = session_counter->counter;
+ r->num_error_out_of_paper = 0x0;
+ r->num_error_not_ready = 0x0; /* number of print failure */
+ r->job_error = 0x0;
+ r->number_of_processors = 0x1;
+ r->processor_type = processor_type;
+ r->high_part_total_bytes = 0x0;
+
+ /* ChangeID in milliseconds*/
+ winreg_printer_get_changeid_internal(mem_ctx, session_info, msg_ctx,
+ info2->sharename, &r->change_id);
+
+ r->last_error = WERR_OK;
+ r->status = nt_printq_status(status.status);
+ r->enumerate_network_printers = 0x0;
+ r->c_setprinter = 0x0;
+ r->processor_architecture = processor_architecture;
+ r->processor_level = 0x6; /* 6 ???*/
+ r->ref_ic = 0;
+ r->reserved2 = 0;
+ r->reserved3 = 0;
+
+ return WERR_OK;
+}
+
+
+/********************************************************************
+ * construct_printer_info1
+ * fill a spoolss_PrinterInfo1 struct
+********************************************************************/
+
+static WERROR construct_printer_info1(TALLOC_CTX *mem_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ uint32_t flags,
+ const char *servername,
+ struct spoolss_PrinterInfo1 *r,
+ int snum)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ WERROR result;
+
+ r->flags = flags;
+
+ if (info2->comment == NULL || info2->comment[0] == '\0') {
+ r->comment = lp_comment(mem_ctx, lp_sub, snum);
+ } else {
+ r->comment = talloc_strdup(mem_ctx, info2->comment); /* saved comment */
+ }
+ W_ERROR_HAVE_NO_MEMORY(r->comment);
+
+ result = create_printername(mem_ctx, servername, info2->printername, &r->name);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->description = talloc_asprintf(mem_ctx, "%s,%s,%s",
+ r->name,
+ info2->drivername,
+ r->comment);
+ W_ERROR_HAVE_NO_MEMORY(r->description);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info2
+ * fill a spoolss_PrinterInfo2 struct
+********************************************************************/
+
+static WERROR construct_printer_info2(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_PrinterInfo2 *r,
+ int snum)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int count;
+ print_status_struct status;
+ WERROR result;
+
+ count = print_queue_length(msg_ctx, snum, &status);
+
+ if (servername) {
+ r->servername = talloc_strdup(mem_ctx, servername);
+ W_ERROR_HAVE_NO_MEMORY(r->servername);
+ } else {
+ r->servername = NULL;
+ }
+
+ result = create_printername(mem_ctx, servername, info2->printername, &r->printername);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->sharename = lp_servicename(mem_ctx, lp_sub, snum);
+ W_ERROR_HAVE_NO_MEMORY(r->sharename);
+ r->portname = talloc_strdup(mem_ctx, info2->portname);
+ W_ERROR_HAVE_NO_MEMORY(r->portname);
+ r->drivername = talloc_strdup(mem_ctx, info2->drivername);
+ W_ERROR_HAVE_NO_MEMORY(r->drivername);
+
+ if (info2->comment[0] == '\0') {
+ r->comment = lp_comment(mem_ctx, lp_sub, snum);
+ } else {
+ r->comment = talloc_strdup(mem_ctx, info2->comment);
+ }
+ W_ERROR_HAVE_NO_MEMORY(r->comment);
+
+ r->location = talloc_strdup(mem_ctx, info2->location);
+ if (info2->location[0] == '\0') {
+ const char *loc = NULL;
+ NTSTATUS nt_status;
+
+ nt_status = printer_list_get_printer(mem_ctx,
+ info2->sharename,
+ NULL,
+ &loc,
+ NULL);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (loc != NULL) {
+ r->location = talloc_strdup(mem_ctx, loc);
+ }
+ }
+ }
+ W_ERROR_HAVE_NO_MEMORY(r->location);
+
+ r->sepfile = talloc_strdup(mem_ctx, info2->sepfile);
+ W_ERROR_HAVE_NO_MEMORY(r->sepfile);
+ r->printprocessor = talloc_strdup(mem_ctx, info2->printprocessor);
+ W_ERROR_HAVE_NO_MEMORY(r->printprocessor);
+ r->datatype = talloc_strdup(mem_ctx, info2->datatype);
+ W_ERROR_HAVE_NO_MEMORY(r->datatype);
+ r->parameters = talloc_strdup(mem_ctx, info2->parameters);
+ W_ERROR_HAVE_NO_MEMORY(r->parameters);
+
+ r->attributes = info2->attributes;
+
+ r->priority = info2->priority;
+ r->defaultpriority = info2->defaultpriority;
+ r->starttime = info2->starttime;
+ r->untiltime = info2->untiltime;
+ r->status = nt_printq_status(status.status);
+ r->cjobs = count;
+ r->averageppm = info2->averageppm;
+
+ if (info2->devmode != NULL) {
+ result = copy_devicemode(mem_ctx,
+ info2->devmode,
+ &r->devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ } else if (lp_default_devmode(snum)) {
+ result = spoolss_create_default_devmode(mem_ctx,
+ info2->printername,
+ &r->devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ } else {
+ r->devmode = NULL;
+ DEBUG(8,("Returning NULL Devicemode!\n"));
+ }
+
+ compose_devicemode_devicename(r->devmode, r->printername);
+
+ r->secdesc = NULL;
+
+ if (info2->secdesc != NULL) {
+ /* don't use talloc_steal() here unless you do a deep steal of all
+ the SEC_DESC members */
+
+ r->secdesc = security_descriptor_copy(mem_ctx, info2->secdesc);
+ if (r->secdesc == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info3
+ * fill a spoolss_PrinterInfo3 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info3(TALLOC_CTX *mem_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_PrinterInfo3 *r,
+ int snum)
+{
+ /* These are the components of the SD we are returning. */
+
+ if (info2->secdesc != NULL) {
+ /* don't use talloc_steal() here unless you do a deep steal of all
+ the SEC_DESC members */
+
+ r->secdesc = security_descriptor_copy(mem_ctx, info2->secdesc);
+ if (r->secdesc == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info4
+ * fill a spoolss_PrinterInfo4 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info4(TALLOC_CTX *mem_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_PrinterInfo4 *r,
+ int snum)
+{
+ WERROR result;
+
+ result = create_printername(mem_ctx, servername, info2->printername, &r->printername);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ if (servername) {
+ r->servername = talloc_strdup(mem_ctx, servername);
+ W_ERROR_HAVE_NO_MEMORY(r->servername);
+ } else {
+ r->servername = NULL;
+ }
+
+ r->attributes = info2->attributes;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info5
+ * fill a spoolss_PrinterInfo5 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info5(TALLOC_CTX *mem_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_PrinterInfo5 *r,
+ int snum)
+{
+ WERROR result;
+
+ result = create_printername(mem_ctx, servername, info2->printername, &r->printername);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->portname = talloc_strdup(mem_ctx, info2->portname);
+ W_ERROR_HAVE_NO_MEMORY(r->portname);
+
+ r->attributes = info2->attributes;
+
+ /*
+ * These two are not used by NT+ according to MSDN. However the values
+ * we saw on Windows Server 2012 and 2016 are always set to the 0xafc8.
+ */
+ r->device_not_selected_timeout = 0xafc8; /* 45 sec */
+ r->transmission_retry_timeout = 0xafc8; /* 45 sec */
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a spoolss_PrinterInfo6 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info6(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_PrinterInfo6 *r,
+ int snum)
+{
+ print_status_struct status;
+
+ print_queue_length(msg_ctx, snum, &status);
+
+ r->status = nt_printq_status(status.status);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info7
+ * fill a spoolss_PrinterInfo7 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info7(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ struct spoolss_PrinterInfo7 *r,
+ int snum)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const struct auth_session_info *session_info;
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ char *printer;
+ WERROR werr;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ session_info = get_session_info_system();
+ SMB_ASSERT(session_info != NULL);
+
+ printer = lp_servicename(tmp_ctx, lp_sub, snum);
+ if (printer == NULL) {
+ DEBUG(0, ("invalid printer snum %d\n", snum));
+ werr = WERR_INVALID_PARAMETER;
+ goto out_tmp_free;
+ }
+
+ if (is_printer_published(tmp_ctx, session_info, msg_ctx,
+ servername, printer, &pinfo2)) {
+ struct GUID guid;
+ char *guidstr;
+ werr = nt_printer_guid_get(tmp_ctx, session_info, msg_ctx,
+ printer, &guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ /*
+ * If we do not have a GUID entry in the registry, then
+ * try to retrieve it from AD and store it now.
+ */
+ werr = nt_printer_guid_retrieve(tmp_ctx, printer,
+ &guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ DBG_NOTICE("Failed to retrieve GUID for "
+ "printer [%s] from AD - %s\n",
+ printer,
+ win_errstr(werr));
+ if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ /*
+ * If we did not find it in AD, then it
+ * is unpublished and we should reflect
+ * this in the registry and return
+ * success.
+ */
+ DBG_WARNING("Unpublish printer [%s]\n",
+ pinfo2->sharename);
+ nt_printer_publish(tmp_ctx,
+ session_info,
+ msg_ctx,
+ pinfo2,
+ DSPRINT_UNPUBLISH);
+ r->guid = talloc_strdup(mem_ctx, "");
+ r->action = DSPRINT_UNPUBLISH;
+
+ if (r->guid == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ } else {
+ werr = WERR_OK;
+ }
+ }
+ goto out_tmp_free;
+ }
+
+ werr = nt_printer_guid_store(msg_ctx, printer, guid);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(3, ("failed to store printer %s guid\n",
+ printer));
+ }
+ }
+
+ /* [MS-RPRN] section 2.2: must use curly-braced GUIDs */
+ guidstr = GUID_string2(mem_ctx, &guid);
+ if (guidstr == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto out_tmp_free;
+ }
+ /* Convert GUID string to uppercase otherwise printers
+ * are pruned */
+ r->guid = talloc_strdup_upper(mem_ctx, guidstr);
+ r->action = DSPRINT_PUBLISH;
+
+ TALLOC_FREE(guidstr);
+ } else {
+ r->guid = talloc_strdup(mem_ctx, "");
+ r->action = DSPRINT_UNPUBLISH;
+ }
+ if (r->guid == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto out_tmp_free;
+ }
+
+ werr = WERR_OK;
+out_tmp_free:
+ talloc_free(tmp_ctx);
+ return werr;
+}
+
+/********************************************************************
+ * construct_printer_info8
+ * fill a spoolss_PrinterInfo8 struct
+ ********************************************************************/
+
+static WERROR construct_printer_info8(TALLOC_CTX *mem_ctx,
+ const struct spoolss_PrinterInfo2 *info2,
+ const char *servername,
+ struct spoolss_DeviceModeInfo *r,
+ int snum)
+{
+ WERROR result;
+ const char *printername;
+
+ result = create_printername(mem_ctx, servername, info2->printername, &printername);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ if (info2->devmode != NULL) {
+ result = copy_devicemode(mem_ctx,
+ info2->devmode,
+ &r->devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ } else if (lp_default_devmode(snum)) {
+ result = spoolss_create_default_devmode(mem_ctx,
+ info2->printername,
+ &r->devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ } else {
+ r->devmode = NULL;
+ DEBUG(8,("Returning NULL Devicemode!\n"));
+ }
+
+ compose_devicemode_devicename(r->devmode, printername);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ Spoolss_enumprinters.
+********************************************************************/
+
+static WERROR enum_all_printers_info_level(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ uint32_t level,
+ uint32_t flags,
+ union spoolss_PrinterInfo **info_p,
+ uint32_t *count_p)
+{
+ int snum;
+ int n_services;
+ union spoolss_PrinterInfo *info = NULL;
+ uint32_t count = 0;
+ WERROR result = WERR_OK;
+ struct dcerpc_binding_handle *b = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * printer shares are updated on client enumeration. The background
+ * printer process updates printer_list.tdb at regular intervals.
+ */
+ become_root();
+ delete_and_reload_printers();
+ unbecome_root();
+
+ n_services = lp_numservices();
+ *count_p = 0;
+ *info_p = NULL;
+
+ for (snum = 0; snum < n_services; snum++) {
+
+ const char *printer;
+ struct spoolss_PrinterInfo2 *info2;
+
+ if (!snum_is_shared_printer(snum)) {
+ continue;
+ }
+
+ printer = lp_const_servicename(snum);
+
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n",
+ printer, snum));
+
+ if (b == NULL) {
+ result = winreg_printer_binding_handle(tmp_ctx,
+ session_info,
+ msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+ }
+
+ result = winreg_create_printer(tmp_ctx, b,
+ printer);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ info = talloc_realloc(tmp_ctx, info,
+ union spoolss_PrinterInfo,
+ count + 1);
+ if (!info) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ result = winreg_get_printer(tmp_ctx, b,
+ printer, &info2);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ switch (level) {
+ case 0:
+ result = construct_printer_info0(info, session_info,
+ msg_ctx, info2,
+ servername,
+ &info[count].info0, snum);
+ break;
+ case 1:
+ result = construct_printer_info1(info, info2, flags,
+ servername,
+ &info[count].info1, snum);
+ break;
+ case 2:
+ result = construct_printer_info2(info, msg_ctx, info2,
+ servername,
+ &info[count].info2, snum);
+ break;
+ case 4:
+ result = construct_printer_info4(info, info2,
+ servername,
+ &info[count].info4, snum);
+ break;
+ case 5:
+ result = construct_printer_info5(info, info2,
+ servername,
+ &info[count].info5, snum);
+ break;
+
+ default:
+ result = WERR_INVALID_LEVEL;
+ goto out;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ count++;
+ }
+
+out:
+ if (W_ERROR_IS_OK(result)) {
+ *info_p = talloc_move(mem_ctx, &info);
+ *count_p = count;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 0
+ ********************************************************************/
+
+static WERROR enumprinters_level0(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t flags,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ DEBUG(4,("enum_all_printers_info_0\n"));
+
+ return enum_all_printers_info_level(mem_ctx, session_info, msg_ctx,
+ servername, 0, flags, info, count);
+}
+
+
+/********************************************************************
+********************************************************************/
+
+static WERROR enum_all_printers_info_1(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ uint32_t flags,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ DEBUG(4,("enum_all_printers_info_1\n"));
+
+ return enum_all_printers_info_level(mem_ctx, session_info, msg_ctx,
+ servername, 1, flags, info, count);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_local.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_local(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ DEBUG(4,("enum_all_printers_info_1_local\n"));
+
+ return enum_all_printers_info_1(mem_ctx, session_info, msg_ctx,
+ servername, PRINTER_ENUM_ICON8, info, count);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_name.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_name(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ const char *s = servername;
+
+ DEBUG(4,("enum_all_printers_info_1_name\n"));
+
+ if (servername != NULL &&
+ (servername[0] == '\\') && (servername[1] == '\\')) {
+ s = servername + 2;
+ }
+
+ if (!is_myname_or_ipaddr(s)) {
+ return WERR_INVALID_NAME;
+ }
+
+ return enum_all_printers_info_1(mem_ctx, session_info, msg_ctx,
+ servername, PRINTER_ENUM_ICON8, info, count);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_network.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_network(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ const char *s = servername;
+
+ DEBUG(4,("enum_all_printers_info_1_network\n"));
+
+ /* If we respond to a enum_printers level 1 on our name with flags
+ set to PRINTER_ENUM_REMOTE with a list of printers then these
+ printers incorrectly appear in the APW browse list.
+ Specifically the printers for the server appear at the workgroup
+ level where all the other servers in the domain are
+ listed. Windows responds to this call with a
+ WERR_CAN_NOT_COMPLETE so we should do the same. */
+
+ if (servername != NULL &&
+ (servername[0] == '\\') && (servername[1] == '\\')) {
+ s = servername + 2;
+ }
+
+ if (is_myname_or_ipaddr(s)) {
+ return WERR_CAN_NOT_COMPLETE;
+ }
+
+ return enum_all_printers_info_1(mem_ctx, session_info, msg_ctx,
+ servername, PRINTER_ENUM_NAME, info, count);
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+static WERROR enum_all_printers_info_2(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ DEBUG(4,("enum_all_printers_info_2\n"));
+
+ return enum_all_printers_info_level(mem_ctx, session_info, msg_ctx,
+ servername, 2, 0, info, count);
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 1
+ ********************************************************************/
+
+static WERROR enumprinters_level1(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t flags,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ /* Not all the flags are equals */
+
+ if (flags & PRINTER_ENUM_LOCAL) {
+ return enum_all_printers_info_1_local(mem_ctx, session_info,
+ msg_ctx, servername, info, count);
+ }
+
+ if (flags & PRINTER_ENUM_NAME) {
+ return enum_all_printers_info_1_name(mem_ctx, session_info,
+ msg_ctx, servername, info,
+ count);
+ }
+
+ if (flags & PRINTER_ENUM_NETWORK) {
+ return enum_all_printers_info_1_network(mem_ctx, session_info,
+ msg_ctx, servername, info,
+ count);
+ }
+
+ return WERR_OK; /* NT4sp5 does that */
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 2
+ ********************************************************************/
+
+static WERROR enumprinters_level2(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t flags,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ if (flags & PRINTER_ENUM_LOCAL) {
+
+ return enum_all_printers_info_2(mem_ctx, session_info, msg_ctx,
+ servername,
+ info, count);
+ }
+
+ if (flags & PRINTER_ENUM_NAME) {
+ if (servername && !is_myname_or_ipaddr(canon_servername(servername))) {
+ return WERR_INVALID_NAME;
+ }
+
+ return enum_all_printers_info_2(mem_ctx, session_info, msg_ctx,
+ servername,
+ info, count);
+ }
+
+ if (flags & PRINTER_ENUM_REMOTE) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 4
+ ********************************************************************/
+
+static WERROR enumprinters_level4(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t flags,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ DEBUG(4,("enum_all_printers_info_4\n"));
+
+ return enum_all_printers_info_level(mem_ctx, session_info, msg_ctx,
+ servername, 4, flags, info, count);
+}
+
+
+/********************************************************************
+ * handle enumeration of printers at level 5
+ ********************************************************************/
+
+static WERROR enumprinters_level5(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t flags,
+ const char *servername,
+ union spoolss_PrinterInfo **info,
+ uint32_t *count)
+{
+ DEBUG(4,("enum_all_printers_info_5\n"));
+
+ return enum_all_printers_info_level(mem_ctx, session_info, msg_ctx,
+ servername, 5, flags, info, count);
+}
+
+/****************************************************************
+ _spoolss_EnumPrinters
+****************************************************************/
+
+WERROR _spoolss_EnumPrinters(struct pipes_struct *p,
+ struct spoolss_EnumPrinters *r)
+{
+ const struct auth_session_info *session_info = get_session_info_system();
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("_spoolss_EnumPrinters\n"));
+
+ *r->out.needed = 0;
+ *r->out.count = 0;
+ *r->out.info = NULL;
+
+ /*
+ * Level 1:
+ * flags==PRINTER_ENUM_NAME
+ * if name=="" then enumerates all printers
+ * if name!="" then enumerate the printer
+ * flags==PRINTER_ENUM_REMOTE
+ * name is NULL, enumerate printers
+ * Level 2: name!="" enumerates printers, name can't be NULL
+ * Level 3: doesn't exist
+ * Level 4: does a local registry lookup
+ * Level 5: same as Level 2
+ */
+
+ if (r->in.server && r->in.server[0] == '\0') {
+ r->in.server = NULL;
+ }
+
+ switch (r->in.level) {
+ case 0:
+ result = enumprinters_level0(p->mem_ctx, session_info,
+ p->msg_ctx, r->in.flags,
+ r->in.server,
+ r->out.info, r->out.count);
+ break;
+ case 1:
+ result = enumprinters_level1(p->mem_ctx, session_info,
+ p->msg_ctx, r->in.flags,
+ r->in.server,
+ r->out.info, r->out.count);
+ break;
+ case 2:
+ result = enumprinters_level2(p->mem_ctx, session_info,
+ p->msg_ctx, r->in.flags,
+ r->in.server,
+ r->out.info, r->out.count);
+ break;
+ case 4:
+ result = enumprinters_level4(p->mem_ctx, session_info,
+ p->msg_ctx, r->in.flags,
+ r->in.server,
+ r->out.info, r->out.count);
+ break;
+ case 5:
+ result = enumprinters_level5(p->mem_ctx, session_info,
+ p->msg_ctx, r->in.flags,
+ r->in.server,
+ r->out.info, r->out.count);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ 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);
+}
+
+/****************************************************************
+ _spoolss_GetPrinter
+****************************************************************/
+
+WERROR _spoolss_GetPrinter(struct pipes_struct *p,
+ struct spoolss_GetPrinter *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ struct spoolss_PrinterInfo2 *info2 = NULL;
+ WERROR result = WERR_OK;
+ int snum;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_info_free;
+ }
+
+ *r->out.needed = 0;
+
+ if (Printer == NULL) {
+ result = WERR_INVALID_HANDLE;
+ goto err_info_free;
+ }
+
+ if (Printer->printer_type == SPLHND_SERVER) {
+
+ struct dcerpc_binding_handle *b;
+
+ if (r->in.level != 3) {
+ result = WERR_INVALID_LEVEL;
+ goto err_info_free;
+ }
+
+ result = winreg_printer_binding_handle(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_info_free;
+ }
+
+ result = winreg_get_printserver_secdesc(p->mem_ctx,
+ b,
+ &r->out.info->info3.secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_info_free;
+ }
+
+ goto done;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ result = WERR_INVALID_HANDLE;
+ goto err_info_free;
+ }
+
+ result = winreg_get_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum),
+ &info2);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_info_free;
+ }
+
+ switch (r->in.level) {
+ case 0:
+ result = construct_printer_info0(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ info2,
+ Printer->servername,
+ &r->out.info->info0,
+ snum);
+ break;
+ case 1:
+ result = construct_printer_info1(p->mem_ctx, info2,
+ PRINTER_ENUM_ICON8,
+ Printer->servername,
+ &r->out.info->info1, snum);
+ break;
+ case 2:
+ result = construct_printer_info2(p->mem_ctx, p->msg_ctx, info2,
+ Printer->servername,
+ &r->out.info->info2, snum);
+ break;
+ case 3:
+ result = construct_printer_info3(p->mem_ctx, info2,
+ Printer->servername,
+ &r->out.info->info3, snum);
+ break;
+ case 4:
+ result = construct_printer_info4(p->mem_ctx, info2,
+ Printer->servername,
+ &r->out.info->info4, snum);
+ break;
+ case 5:
+ result = construct_printer_info5(p->mem_ctx, info2,
+ Printer->servername,
+ &r->out.info->info5, snum);
+ break;
+ case 6:
+ result = construct_printer_info6(p->mem_ctx, p->msg_ctx, info2,
+ Printer->servername,
+ &r->out.info->info6, snum);
+ break;
+ case 7:
+ result = construct_printer_info7(p->mem_ctx, p->msg_ctx,
+ Printer->servername,
+ &r->out.info->info7, snum);
+ break;
+ case 8:
+ result = construct_printer_info8(p->mem_ctx, info2,
+ Printer->servername,
+ &r->out.info->info8, snum);
+ break;
+ default:
+ result = WERR_INVALID_LEVEL;
+ break;
+ }
+ TALLOC_FREE(info2);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("_spoolss_GetPrinter: failed to construct printer info level %d - %s\n",
+ r->in.level, win_errstr(result)));
+ goto err_info_free;
+ }
+ done:
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_PrinterInfo,
+ 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);
+
+err_info_free:
+ TALLOC_FREE(r->out.info);
+ return result;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+#define FILL_DRIVER_STRING(mem_ctx, in, out) \
+ do { \
+ if (in && strlen(in)) { \
+ out = talloc_strdup(mem_ctx, in); \
+ } else { \
+ out = talloc_strdup(mem_ctx, ""); \
+ } \
+ W_ERROR_HAVE_NO_MEMORY(out); \
+ } while (0);
+
+#define FILL_DRIVER_UNC_STRING(mem_ctx, server, arch, ver, in, out) \
+ do { \
+ if (in && strlen(in)) { \
+ out = talloc_asprintf(mem_ctx, "\\\\%s\\print$\\%s\\%d\\%s", server, get_short_archi(arch), ver, in); \
+ } else { \
+ out = talloc_strdup(mem_ctx, ""); \
+ } \
+ W_ERROR_HAVE_NO_MEMORY(out); \
+ } while (0);
+
+static WERROR string_array_from_driver_info(TALLOC_CTX *mem_ctx,
+ const char **string_array,
+ const char ***presult,
+ const char *cservername,
+ const char *arch,
+ int version)
+{
+ size_t i;
+ size_t num_strings = 0;
+ const char **array = NULL;
+
+ if (string_array == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ for (i=0; string_array[i] && string_array[i][0] != '\0'; i++) {
+ const char *str = NULL;
+
+ if (cservername == NULL || arch == NULL) {
+ FILL_DRIVER_STRING(mem_ctx, string_array[i], str);
+ } else {
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername, arch, version, string_array[i], str);
+ }
+
+ if (!add_string_to_array(mem_ctx, str, &array, &num_strings)) {
+ TALLOC_FREE(array);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if (i > 0) {
+ ADD_TO_ARRAY(mem_ctx, const char *, NULL,
+ &array, &num_strings);
+ }
+
+ if (presult != NULL) {
+ *presult = array;
+ } else {
+ talloc_free(array);
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo1 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info1(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo1 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo2 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info2(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo2 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+
+{
+ const char *cservername = canon_servername(servername);
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->driver_path,
+ r->driver_path);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->data_file,
+ r->data_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->config_file,
+ r->config_file);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo3 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info3(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo3 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ const char *cservername = canon_servername(servername);
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->driver_path,
+ r->driver_path);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->data_file,
+ r->data_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->config_file,
+ r->config_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->help_file,
+ r->help_file);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->monitor_name,
+ r->monitor_name);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->default_datatype,
+ r->default_datatype);
+
+ return string_array_from_driver_info(mem_ctx,
+ driver->dependent_files,
+ &r->dependent_files,
+ cservername,
+ driver->architecture,
+ driver->version);
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo4 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info4(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo4 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ const char *cservername = canon_servername(servername);
+ WERROR result;
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->driver_path,
+ r->driver_path);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->data_file,
+ r->data_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->config_file,
+ r->config_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->help_file,
+ r->help_file);
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->dependent_files,
+ &r->dependent_files,
+ cservername,
+ driver->architecture,
+ driver->version);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->monitor_name,
+ r->monitor_name);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->default_datatype,
+ r->default_datatype);
+
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->previous_names,
+ &r->previous_names,
+ NULL, NULL, 0);
+
+ return result;
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo5 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info5(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo5 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ const char *cservername = canon_servername(servername);
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->driver_path,
+ r->driver_path);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->data_file,
+ r->data_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->config_file,
+ r->config_file);
+
+ r->driver_attributes = 0;
+ r->config_version = 0;
+ r->driver_version = 0;
+
+ return WERR_OK;
+}
+/********************************************************************
+ * fill a spoolss_DriverInfo6 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info6(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo6 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ const char *cservername = canon_servername(servername);
+ WERROR result;
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->driver_path,
+ r->driver_path);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->data_file,
+ r->data_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->config_file,
+ r->config_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->help_file,
+ r->help_file);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->monitor_name,
+ r->monitor_name);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->default_datatype,
+ r->default_datatype);
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->dependent_files,
+ &r->dependent_files,
+ cservername,
+ driver->architecture,
+ driver->version);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->previous_names,
+ &r->previous_names,
+ NULL, NULL, 0);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->driver_date = driver->driver_date;
+ r->driver_version = driver->driver_version;
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->manufacturer_name,
+ r->manufacturer_name);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->manufacturer_url,
+ r->manufacturer_url);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->hardware_id,
+ r->hardware_id);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->provider,
+ r->provider);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo8 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info8(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo8 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ const char *cservername = canon_servername(servername);
+ WERROR result;
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->driver_path,
+ r->driver_path);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->data_file,
+ r->data_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->config_file,
+ r->config_file);
+
+ FILL_DRIVER_UNC_STRING(mem_ctx, cservername,
+ driver->architecture,
+ driver->version,
+ driver->help_file,
+ r->help_file);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->monitor_name,
+ r->monitor_name);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->default_datatype,
+ r->default_datatype);
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->dependent_files,
+ &r->dependent_files,
+ cservername,
+ driver->architecture,
+ driver->version);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->previous_names,
+ &r->previous_names,
+ NULL, NULL, 0);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->driver_date = driver->driver_date;
+ r->driver_version = driver->driver_version;
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->manufacturer_name,
+ r->manufacturer_name);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->manufacturer_url,
+ r->manufacturer_url);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->hardware_id,
+ r->hardware_id);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->provider,
+ r->provider);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->print_processor,
+ r->print_processor);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->vendor_setup,
+ r->vendor_setup);
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->color_profiles,
+ &r->color_profiles,
+ NULL, NULL, 0);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->inf_path,
+ r->inf_path);
+
+ r->printer_driver_attributes = driver->printer_driver_attributes;
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->core_driver_dependencies,
+ &r->core_driver_dependencies,
+ NULL, NULL, 0);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->min_inbox_driver_ver_date = driver->min_inbox_driver_ver_date;
+ r->min_inbox_driver_ver_version = driver->min_inbox_driver_ver_version;
+
+ return WERR_OK;
+}
+
+#if 0 /* disabled until marshalling issues are resolved - gd */
+/********************************************************************
+ ********************************************************************/
+
+static WERROR fill_spoolss_DriverFileInfo(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverFileInfo *r,
+ const char *cservername,
+ const char *file_name,
+ enum spoolss_DriverFileType file_type,
+ uint32_t file_version)
+{
+ r->file_name = talloc_asprintf(mem_ctx, "\\\\%s%s",
+ cservername, file_name);
+ W_ERROR_HAVE_NO_MEMORY(r->file_name);
+ r->file_type = file_type;
+ r->file_version = file_version;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static WERROR spoolss_DriverFileInfo_from_driver(TALLOC_CTX *mem_ctx,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *cservername,
+ struct spoolss_DriverFileInfo **info_p,
+ uint32_t *count_p)
+{
+ struct spoolss_DriverFileInfo *info = NULL;
+ uint32_t count = 0;
+ WERROR result;
+ uint32_t i;
+
+ *info_p = NULL;
+ *count_p = 0;
+
+ if (strlen(driver->driver_path)) {
+ info = talloc_realloc(mem_ctx, info,
+ struct spoolss_DriverFileInfo,
+ count + 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+ result = fill_spoolss_DriverFileInfo(info,
+ &info[count],
+ cservername,
+ driver->driver_path,
+ SPOOLSS_DRIVER_FILE_TYPE_RENDERING,
+ 0);
+ W_ERROR_NOT_OK_RETURN(result);
+ count++;
+ }
+
+ if (strlen(driver->config_file)) {
+ info = talloc_realloc(mem_ctx, info,
+ struct spoolss_DriverFileInfo,
+ count + 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+ result = fill_spoolss_DriverFileInfo(info,
+ &info[count],
+ cservername,
+ driver->config_file,
+ SPOOLSS_DRIVER_FILE_TYPE_CONFIGURATION,
+ 0);
+ W_ERROR_NOT_OK_RETURN(result);
+ count++;
+ }
+
+ if (strlen(driver->data_file)) {
+ info = talloc_realloc(mem_ctx, info,
+ struct spoolss_DriverFileInfo,
+ count + 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+ result = fill_spoolss_DriverFileInfo(info,
+ &info[count],
+ cservername,
+ driver->data_file,
+ SPOOLSS_DRIVER_FILE_TYPE_DATA,
+ 0);
+ W_ERROR_NOT_OK_RETURN(result);
+ count++;
+ }
+
+ if (strlen(driver->help_file)) {
+ info = talloc_realloc(mem_ctx, info,
+ struct spoolss_DriverFileInfo,
+ count + 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+ result = fill_spoolss_DriverFileInfo(info,
+ &info[count],
+ cservername,
+ driver->help_file,
+ SPOOLSS_DRIVER_FILE_TYPE_HELP,
+ 0);
+ W_ERROR_NOT_OK_RETURN(result);
+ count++;
+ }
+
+ for (i=0; driver->dependent_files[i] && driver->dependent_files[i][0] != '\0'; i++) {
+ info = talloc_realloc(mem_ctx, info,
+ struct spoolss_DriverFileInfo,
+ count + 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+ result = fill_spoolss_DriverFileInfo(info,
+ &info[count],
+ cservername,
+ driver->dependent_files[i],
+ SPOOLSS_DRIVER_FILE_TYPE_OTHER,
+ 0);
+ W_ERROR_NOT_OK_RETURN(result);
+ count++;
+ }
+
+ *info_p = info;
+ *count_p = count;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * fill a spoolss_DriverInfo101 struct
+ ********************************************************************/
+
+static WERROR fill_printer_driver_info101(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo101 *r,
+ const struct spoolss_DriverInfo8 *driver,
+ const char *servername)
+{
+ const char *cservername = canon_servername(servername);
+ WERROR result;
+
+ r->version = driver->version;
+
+ r->driver_name = talloc_strdup(mem_ctx, driver->driver_name);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+ r->architecture = talloc_strdup(mem_ctx, driver->architecture);
+ W_ERROR_HAVE_NO_MEMORY(r->architecture);
+
+ result = spoolss_DriverFileInfo_from_driver(mem_ctx, driver,
+ cservername,
+ &r->file_info,
+ &r->file_count);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->monitor_name,
+ r->monitor_name);
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->default_datatype,
+ r->default_datatype);
+
+ result = string_array_from_driver_info(mem_ctx,
+ driver->previous_names,
+ &r->previous_names,
+ NULL, NULL, 0);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ r->driver_date = driver->driver_date;
+ r->driver_version = driver->driver_version;
+
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->manufacturer_name,
+ r->manufacturer_name);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->manufacturer_url,
+ r->manufacturer_url);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->hardware_id,
+ r->hardware_id);
+ FILL_DRIVER_STRING(mem_ctx,
+ driver->provider,
+ r->provider);
+
+ return WERR_OK;
+}
+#endif
+/********************************************************************
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_level(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t level,
+ union spoolss_DriverInfo *r,
+ int snum,
+ const char *servername,
+ const char *architecture,
+ uint32_t version)
+{
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ struct spoolss_DriverInfo8 *driver;
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if (level == 101) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ session_info,
+ msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_get_printer(tmp_ctx, b,
+ lp_const_servicename(snum),
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ DBG_ERR("Failed to get printer info2 for [%s]: %s\n",
+ lp_const_servicename(snum), win_errstr(result));
+ result = WERR_INVALID_PRINTER_NAME;
+ goto done;
+ }
+
+ if (pinfo2->drivername == NULL || pinfo2->drivername[0] == '\0') {
+ result = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+
+ DBG_INFO("Construct printer driver [%s] for [%s]\n",
+ pinfo2->drivername,
+ pinfo2->sharename);
+
+ result = winreg_get_driver(tmp_ctx, b,
+ architecture,
+ pinfo2->drivername, version, &driver);
+
+ DBG_INFO("winreg_get_driver() status: %s\n",
+ win_errstr(result));
+
+ if (!W_ERROR_IS_OK(result)) {
+ /*
+ * Is this a W2k client ?
+ */
+
+ if (version < 3) {
+ result = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ result = winreg_get_driver(tmp_ctx, b,
+ architecture,
+ pinfo2->drivername,
+ version, &driver);
+ DEBUG(8,("construct_printer_driver_level: status: %s\n",
+ win_errstr(result)));
+ if (!W_ERROR_IS_OK(result)) {
+ result = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+
+ /* these are allocated on mem_ctx and not tmp_ctx because they are
+ * the 'return value' and need to outlive this call */
+ switch (level) {
+ case 1:
+ result = fill_printer_driver_info1(mem_ctx, &r->info1, driver, servername);
+ break;
+ case 2:
+ result = fill_printer_driver_info2(mem_ctx, &r->info2, driver, servername);
+ break;
+ case 3:
+ result = fill_printer_driver_info3(mem_ctx, &r->info3, driver, servername);
+ break;
+ case 4:
+ result = fill_printer_driver_info4(mem_ctx, &r->info4, driver, servername);
+ break;
+ case 5:
+ result = fill_printer_driver_info5(mem_ctx, &r->info5, driver, servername);
+ break;
+ case 6:
+ result = fill_printer_driver_info6(mem_ctx, &r->info6, driver, servername);
+ break;
+ case 8:
+ result = fill_printer_driver_info8(mem_ctx, &r->info8, driver, servername);
+ break;
+#if 0 /* disabled until marshalling issues are resolved - gd */
+ case 101:
+ result = fill_printer_driver_info101(mem_ctx, &r->info101, driver, servername);
+ break;
+#endif
+ default:
+ result = WERR_INVALID_LEVEL;
+ break;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/****************************************************************
+ _spoolss_GetPrinterDriver2
+****************************************************************/
+
+WERROR _spoolss_GetPrinterDriver2(struct pipes_struct *p,
+ struct spoolss_GetPrinterDriver2 *r)
+{
+ struct printer_handle *printer;
+ WERROR result;
+ uint32_t version = r->in.client_major_version;
+
+ int snum;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_info_free;
+ }
+
+ DEBUG(4,("_spoolss_GetPrinterDriver2\n"));
+
+ if (!(printer = find_printer_index_by_hnd(p, r->in.handle))) {
+ DEBUG(0,("_spoolss_GetPrinterDriver2: invalid printer handle!\n"));
+ result = WERR_INVALID_PRINTER_NAME;
+ goto err_info_free;
+ }
+
+ *r->out.needed = 0;
+ *r->out.server_major_version = 0;
+ *r->out.server_minor_version = 0;
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ result = WERR_INVALID_HANDLE;
+ goto err_info_free;
+ }
+
+ if (r->in.client_major_version == SPOOLSS_DRIVER_VERSION_2012) {
+ DEBUG(3,("_spoolss_GetPrinterDriver2: v4 driver requested, "
+ "downgrading to v3\n"));
+ version = SPOOLSS_DRIVER_VERSION_200X;
+ }
+
+ result = construct_printer_driver_info_level(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ r->in.level, r->out.info,
+ snum, printer->servername,
+ r->in.architecture,
+ version);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_info_free;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_DriverInfo,
+ 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);
+
+err_info_free:
+ TALLOC_FREE(r->out.info);
+ return result;
+}
+
+
+/****************************************************************
+ _spoolss_StartPagePrinter
+****************************************************************/
+
+WERROR _spoolss_StartPagePrinter(struct pipes_struct *p,
+ struct spoolss_StartPagePrinter *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (!Printer) {
+ DEBUG(3,("_spoolss_StartPagePrinter: "
+ "Error in startpageprinter printer handle\n"));
+ return WERR_INVALID_HANDLE;
+ }
+
+ Printer->page_started = true;
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_EndPagePrinter
+****************************************************************/
+
+WERROR _spoolss_EndPagePrinter(struct pipes_struct *p,
+ struct spoolss_EndPagePrinter *r)
+{
+ int snum;
+
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_EndPagePrinter: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL))
+ return WERR_INVALID_HANDLE;
+
+ Printer->page_started = false;
+ print_job_endpage(p->msg_ctx, snum, Printer->jobid);
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_StartDocPrinter
+****************************************************************/
+
+WERROR _spoolss_StartDocPrinter(struct pipes_struct *p,
+ struct spoolss_StartDocPrinter *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct spoolss_DocumentInfo1 *info_1;
+ int snum;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ WERROR werr;
+ char *rhost;
+ int rc;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_StartDocPrinter: "
+ "Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (Printer->jobid) {
+ DEBUG(2, ("_spoolss_StartDocPrinter: "
+ "StartDocPrinter called twice! "
+ "(existing jobid = %d)\n", Printer->jobid));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (r->in.info_ctr->level != 1) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ info_1 = r->in.info_ctr->info.info1;
+
+ /*
+ * a nice thing with NT is it doesn't listen to what you tell it.
+ * when asked to send _only_ RAW data, it tries to send data
+ * in EMF format.
+ *
+ * So I add checks like in NT Server ...
+ */
+
+ if (info_1->datatype) {
+ /*
+ * The v4 driver model used in Windows 8 declares print jobs
+ * intended to bypass the XPS processing layer by setting
+ * datatype to "XPS_PASS" instead of "RAW".
+ */
+ if ((strcmp(info_1->datatype, "RAW") != 0)
+ && (strcmp(info_1->datatype, "XPS_PASS") != 0)) {
+ *r->out.job_id = 0;
+ return WERR_INVALID_DATATYPE;
+ }
+ }
+
+ /* get the share number of the printer */
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ rc = get_remote_hostname(remote_address,
+ &rhost,
+ p->mem_ctx);
+ if (rc < 0) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (strequal(rhost,"UNKNOWN")) {
+ rhost = tsocket_address_inet_addr_string(remote_address,
+ p->mem_ctx);
+ if (rhost == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ werr = print_job_start(session_info,
+ p->msg_ctx,
+ rhost,
+ snum,
+ info_1->document_name,
+ info_1->output_file,
+ Printer->devmode,
+ &Printer->jobid);
+
+ /* An error occurred in print_job_start() so return an appropriate
+ NT error code. */
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ Printer->document_started = true;
+ *r->out.job_id = Printer->jobid;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_EndDocPrinter
+****************************************************************/
+
+WERROR _spoolss_EndDocPrinter(struct pipes_struct *p,
+ struct spoolss_EndDocPrinter *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ NTSTATUS status;
+ int snum;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_EndDocPrinter: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ Printer->document_started = false;
+ status = print_job_end(p->msg_ctx, snum, Printer->jobid, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("_spoolss_EndDocPrinter: "
+ "print_job_end failed [%s]\n",
+ nt_errstr(status)));
+ }
+
+ Printer->jobid = 0;
+ return ntstatus_to_werror(status);
+}
+
+/****************************************************************
+ _spoolss_WritePrinter
+****************************************************************/
+
+WERROR _spoolss_WritePrinter(struct pipes_struct *p,
+ struct spoolss_WritePrinter *r)
+{
+ ssize_t buffer_written;
+ int snum;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_WritePrinter: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(r->in.handle)));
+ *r->out.num_written = r->in._data_size;
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL))
+ return WERR_INVALID_HANDLE;
+
+ /* print_job_write takes care of checking for PJOB_SMBD_SPOOLING */
+ buffer_written = print_job_write(global_event_context(),p->msg_ctx,
+ snum, Printer->jobid,
+ (const char *)r->in.data.data,
+ (size_t)r->in._data_size);
+ if (buffer_written == (ssize_t)-1) {
+ *r->out.num_written = 0;
+ if (errno == ENOSPC)
+ return WERR_NO_SPOOL_SPACE;
+ else
+ return WERR_ACCESS_DENIED;
+ }
+
+ *r->out.num_written = r->in._data_size;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static WERROR control_printer(struct policy_handle *handle, uint32_t command,
+ struct pipes_struct *p)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ int snum;
+ WERROR errcode = WERR_INVALID_FUNCTION;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("control_printer: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_INVALID_HANDLE;
+
+ switch (command) {
+ case SPOOLSS_PRINTER_CONTROL_PAUSE:
+ errcode = print_queue_pause(session_info, p->msg_ctx, snum);
+ break;
+ case SPOOLSS_PRINTER_CONTROL_RESUME:
+ case SPOOLSS_PRINTER_CONTROL_UNPAUSE:
+ errcode = print_queue_resume(session_info, p->msg_ctx, snum);
+ break;
+ case SPOOLSS_PRINTER_CONTROL_PURGE:
+ errcode = print_queue_purge(session_info, p->msg_ctx, snum);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ return errcode;
+}
+
+
+/****************************************************************
+ _spoolss_AbortPrinter
+ * From MSDN: "Deletes printer's spool file if printer is configured
+ * for spooling"
+****************************************************************/
+
+WERROR _spoolss_AbortPrinter(struct pipes_struct *p,
+ struct spoolss_AbortPrinter *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ int snum;
+ WERROR errcode = WERR_OK;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_AbortPrinter: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL))
+ return WERR_INVALID_HANDLE;
+
+ if (!Printer->document_started) {
+ return WERR_SPL_NO_STARTDOC;
+ }
+
+ errcode = print_job_delete(session_info,
+ p->msg_ctx,
+ snum,
+ Printer->jobid);
+
+ return errcode;
+}
+
+/********************************************************************
+ * called by spoolss_api_setprinter
+ * when updating a printer description
+ ********************************************************************/
+
+static WERROR update_printer_sec(struct policy_handle *handle,
+ struct pipes_struct *p,
+ struct sec_desc_buf *secdesc_ctr)
+{
+ struct spoolss_security_descriptor *new_secdesc = NULL;
+ struct spoolss_security_descriptor *old_secdesc = NULL;
+ const char *printer = NULL;
+ WERROR result;
+ int snum = -1;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, handle);
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+ bool ok = false;
+
+ if (!Printer) {
+ DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(handle)));
+
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ if (secdesc_ctr == NULL) {
+ DEBUG(10,("update_printer_sec: secdesc_ctr is NULL !\n"));
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ switch (Printer->printer_type) {
+ case SPLHND_SERVER:
+ break;
+ case SPLHND_PRINTER:
+ if (!get_printer_snum(p, handle, &snum, NULL)) {
+ DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(handle)));
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+ printer = lp_const_servicename(snum);
+ break;
+ default:
+ break;
+ }
+
+ /* Check the user has permissions to change the security
+ descriptor. By experimentation with two NT machines, the user
+ requires Full Access to the printer to change security
+ information. */
+
+ switch (Printer->printer_type) {
+ case SPLHND_SERVER:
+ ok = Printer->access_granted == SERVER_ACCESS_ADMINISTER;
+ break;
+ case SPLHND_PRINTER:
+ ok = Printer->access_granted == PRINTER_ACCESS_ADMINISTER;
+ break;
+ default:
+ break;
+ }
+
+ if (!ok) {
+ DEBUG(4,("update_printer_sec: updated denied by printer permissions "
+ "(access_granted: 0x%08x)\n", Printer->access_granted));
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* NT seems to like setting the security descriptor even though
+ nothing may have actually changed. */
+
+ if (printer != NULL) {
+ result = winreg_get_printer_secdesc(tmp_ctx, b,
+ printer,
+ &old_secdesc);
+ } else {
+ result = winreg_get_printserver_secdesc(tmp_ctx, b,
+ &old_secdesc);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(2,("update_printer_sec: winreg_get_printer_secdesc_internal() failed\n"));
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ struct dom_sid_buf buf;
+ struct security_acl *the_acl;
+ int i;
+
+ the_acl = old_secdesc->dacl;
+ DEBUG(10, ("old_secdesc_ctr for %s has %d aces:\n",
+ printer, the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ DEBUG(10, ("%s 0x%08x\n",
+ dom_sid_str_buf(
+ &the_acl->aces[i].trustee,
+ &buf),
+ the_acl->aces[i].access_mask));
+ }
+
+ the_acl = secdesc_ctr->sd->dacl;
+
+ if (the_acl) {
+ DEBUG(10, ("secdesc_ctr for %s has %d aces:\n",
+ printer, the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ DEBUG(10, ("%s 0x%08x\n",
+ dom_sid_str_buf(
+ &the_acl->aces[i].trustee,
+ &buf),
+ the_acl->aces[i].access_mask));
+ }
+ } else {
+ DEBUG(10, ("dacl for secdesc_ctr is NULL\n"));
+ }
+ }
+
+ new_secdesc = sec_desc_merge(tmp_ctx, secdesc_ctr->sd, old_secdesc);
+ if (new_secdesc == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ if (security_descriptor_equal(new_secdesc, old_secdesc)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ if (printer != NULL) {
+ result = winreg_set_printer_secdesc(tmp_ctx, b,
+ printer,
+ new_secdesc);
+ } else {
+ result = winreg_set_printserver_secdesc(tmp_ctx, b,
+ new_secdesc);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/********************************************************************
+ Canonicalize printer info from a client
+ ********************************************************************/
+
+static bool check_printer_ok(TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterInfo2 *info2,
+ int snum)
+{
+ fstring printername;
+ const char *p;
+
+ DEBUG(5,("check_printer_ok: servername=%s printername=%s sharename=%s "
+ "portname=%s drivername=%s comment=%s location=%s\n",
+ info2->servername, info2->printername, info2->sharename,
+ info2->portname, info2->drivername, info2->comment,
+ info2->location));
+
+ /* we force some elements to "correct" values */
+ info2->servername = talloc_asprintf(mem_ctx, "\\\\%s", lp_netbios_name());
+ if (info2->servername == NULL) {
+ return false;
+ }
+ info2->sharename = talloc_strdup(mem_ctx, lp_const_servicename(snum));
+ if (info2->sharename == NULL) {
+ return false;
+ }
+
+ /* check to see if we allow printername != sharename */
+ if (lp_force_printername(snum)) {
+ info2->printername = talloc_asprintf(mem_ctx, "\\\\%s\\%s",
+ lp_netbios_name(), info2->sharename);
+ } else {
+ /* make sure printername is in \\server\printername format */
+ fstrcpy(printername, info2->printername);
+ p = printername;
+ if ( printername[0] == '\\' && printername[1] == '\\' ) {
+ if ( (p = strchr_m( &printername[2], '\\' )) != NULL )
+ p++;
+ }
+
+ info2->printername = talloc_asprintf(mem_ctx, "\\\\%s\\%s",
+ lp_netbios_name(), p);
+ }
+ if (info2->printername == NULL) {
+ return false;
+ }
+
+ info2->attributes |= PRINTER_ATTRIBUTE_SAMBA;
+ info2->attributes &= ~PRINTER_ATTRIBUTE_NOT_SAMBA;
+
+ return true;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR add_port_hook(TALLOC_CTX *ctx, struct security_token *token, const char *portname, const char *uri)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *cmd = lp_addport_command(talloc_tos(), lp_sub);
+ char *command = NULL;
+ int ret;
+ bool is_print_op = false;
+
+ if ( !*cmd ) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\" \"%s\"", cmd, portname, uri );
+ if (!command) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if ( token )
+ is_print_op = security_token_has_privilege(token, SEC_PRIV_PRINT_OPERATOR);
+
+ DEBUG(10,("Running [%s]\n", command));
+
+ /********* BEGIN SePrintOperatorPrivilege **********/
+
+ if ( is_print_op )
+ become_root();
+
+ ret = smbrun(command, NULL, NULL);
+
+ if ( is_print_op )
+ unbecome_root();
+
+ /********* END SePrintOperatorPrivilege **********/
+
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ TALLOC_FREE(command);
+
+ if ( ret != 0 ) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool spoolss_conn_snum_used(struct smbd_server_connection *sconn,
+ int snum)
+{
+ /*
+ * As we do not know if we are embedded in the file server process
+ * or not, we have to pretend that all shares are in use.
+ */
+ return true;
+}
+
+static bool add_printer_hook(TALLOC_CTX *ctx, struct security_token *token,
+ struct spoolss_SetPrinterInfo2 *info2,
+ const char *remote_machine,
+ struct messaging_context *msg_ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *cmd = lp_addprinter_command(talloc_tos(), lp_sub);
+ char **qlines;
+ char *command = NULL;
+ int numlines;
+ int ret;
+ int fd;
+ bool is_print_op = false;
+
+ if (!remote_machine) {
+ return false;
+ }
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
+ cmd, info2->printername, info2->sharename,
+ info2->portname, info2->drivername,
+ info2->location, info2->comment, remote_machine);
+ if (!command) {
+ return false;
+ }
+
+ if ( token )
+ is_print_op = security_token_has_privilege(token, SEC_PRIV_PRINT_OPERATOR);
+
+ DEBUG(10,("Running [%s]\n", command));
+
+ /********* BEGIN SePrintOperatorPrivilege **********/
+
+ if ( is_print_op )
+ become_root();
+
+ ret = smbrun(command, &fd, NULL);
+ if (ret == 0) {
+ /* Tell everyone we updated smb.conf. */
+ messaging_send_all(msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0);
+ }
+
+ if ( is_print_op )
+ unbecome_root();
+
+ /********* END SePrintOperatorPrivilege **********/
+
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ TALLOC_FREE(command);
+
+ if ( ret != 0 ) {
+ if (fd != -1)
+ close(fd);
+ return false;
+ }
+
+ /* reload our services immediately */
+ become_root();
+ reload_services(NULL, spoolss_conn_snum_used, false);
+ unbecome_root();
+
+ numlines = 0;
+ /* Get lines and convert them back to dos-codepage */
+ qlines = fd_lines_load(fd, &numlines, 0, NULL);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+
+ /* Set the portname to what the script says the portname should be. */
+ /* but don't require anything to be return from the script exit a good error code */
+
+ if (numlines) {
+ /* Set the portname to what the script says the portname should be. */
+ info2->portname = talloc_strdup(ctx, qlines[0]);
+ DEBUGADD(6,("Line[0] = [%s]\n", qlines[0]));
+ }
+
+ TALLOC_FREE(qlines);
+ return true;
+}
+
+static WERROR update_dsspooler(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ int snum,
+ struct spoolss_SetPrinterInfo2 *printer,
+ struct spoolss_PrinterInfo2 *old_printer)
+{
+ bool force_update = (old_printer == NULL);
+ const char *dnsdomname;
+ const char *longname;
+ const char *uncname;
+ const char *spooling;
+ DATA_BLOB buffer;
+ WERROR result = WERR_OK;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+ bool ok;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ session_info,
+ msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ if (printer->drivername != NULL &&
+ (force_update ||
+ !strequal(printer->drivername, old_printer->drivername))) {
+ ok = push_reg_sz(tmp_ctx, &buffer, printer->drivername);
+ if (!ok) {
+ DEBUG(0, ("%s data corrupted\n", SPOOL_REG_DRIVERNAME));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_DRIVERNAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_DRIVERNAME));
+ goto done;
+ }
+
+ if (!force_update) {
+ DEBUG(10,("update_printer: changing driver [%s]! Sending event!\n",
+ printer->drivername));
+
+ notify_printer_driver(global_event_context(), msg_ctx,
+ snum, printer->drivername ?
+ printer->drivername : "");
+ }
+ }
+
+ if (printer->comment != NULL &&
+ (force_update ||
+ !strequal(printer->comment, old_printer->comment))) {
+ ok = push_reg_sz(tmp_ctx, &buffer, printer->comment);
+ if (!ok) {
+ DEBUG(0, ("comment data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_DESCRIPTION,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_DESCRIPTION));
+ goto done;
+ }
+
+ if (!force_update) {
+ notify_printer_comment(global_event_context(), msg_ctx,
+ snum, printer->comment ?
+ printer->comment : "");
+ }
+ }
+
+ if (printer->sharename != NULL &&
+ (force_update ||
+ !strequal(printer->sharename, old_printer->sharename))) {
+ ok = push_reg_sz(tmp_ctx, &buffer, printer->sharename);
+ if (!ok) {
+ DEBUG(0, ("sharename data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTSHARENAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PRINTSHARENAME));
+ goto done;
+ }
+
+ if (!force_update) {
+ notify_printer_sharename(global_event_context(),
+ msg_ctx,
+ snum, printer->sharename ?
+ printer->sharename : "");
+ }
+
+ /* name change, purge any cache entries for the old */
+ prune_printername_cache();
+ }
+
+ if (printer->printername != NULL &&
+ (force_update ||
+ !strequal(printer->printername, old_printer->printername))) {
+ const char *p;
+
+ p = strrchr(printer->printername, '\\' );
+ if (p != NULL) {
+ p++;
+ } else {
+ p = printer->printername;
+ }
+
+ ok = push_reg_sz(tmp_ctx, &buffer, p);
+ if (!ok) {
+ DEBUG(0, ("printername data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTERNAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DBG_ERR("Failed to set %s\n", SPOOL_REG_PRINTERNAME);
+ goto done;
+ }
+
+ if (!force_update) {
+ notify_printer_printername(global_event_context(),
+ msg_ctx, snum, p ? p : "");
+ }
+
+ /* name change, purge any cache entries for the old */
+ prune_printername_cache();
+ }
+
+ if (printer->portname != NULL &&
+ (force_update ||
+ !strequal(printer->portname, old_printer->portname))) {
+ ok = push_reg_sz(tmp_ctx, &buffer, printer->portname);
+ if (!ok) {
+ DEBUG(0, ("portname data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PORTNAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PORTNAME));
+ goto done;
+ }
+
+ if (!force_update) {
+ notify_printer_port(global_event_context(),
+ msg_ctx, snum, printer->portname ?
+ printer->portname : "");
+ }
+ }
+
+ if (printer->location != NULL &&
+ (force_update ||
+ !strequal(printer->location, old_printer->location))) {
+ ok = push_reg_sz(tmp_ctx, &buffer, printer->location);
+ if (!ok) {
+ DEBUG(0, ("location data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_LOCATION,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_LOCATION));
+ goto done;
+ }
+
+ if (!force_update) {
+ notify_printer_location(global_event_context(),
+ msg_ctx, snum,
+ printer->location ?
+ printer->location : "");
+ }
+ }
+
+ if (printer->sepfile != NULL &&
+ (force_update ||
+ !strequal(printer->sepfile, old_printer->sepfile))) {
+ ok = push_reg_sz(tmp_ctx, &buffer, printer->sepfile);
+ if (!ok) {
+ DEBUG(0, ("sepfile data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTSEPARATORFILE,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PRINTSEPARATORFILE));
+ goto done;
+ }
+
+ if (!force_update) {
+ notify_printer_sepfile(global_event_context(),
+ msg_ctx, snum,
+ printer->sepfile ?
+ printer->sepfile : "");
+ }
+ }
+
+ if (printer->starttime != 0 &&
+ (force_update ||
+ printer->starttime != old_printer->starttime)) {
+ buffer = data_blob_talloc(tmp_ctx, NULL, 4);
+ SIVAL(buffer.data, 0, printer->starttime);
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTSTARTTIME,
+ REG_DWORD,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PRINTSTARTTIME));
+ goto done;
+ }
+ }
+
+ if (printer->untiltime != 0 &&
+ (force_update ||
+ printer->untiltime != old_printer->untiltime)) {
+ buffer = data_blob_talloc(tmp_ctx, NULL, 4);
+ SIVAL(buffer.data, 0, printer->untiltime);
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTENDTIME,
+ REG_DWORD,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PRINTENDTIME));
+ goto done;
+ }
+ }
+
+ if (force_update || printer->priority != old_printer->priority) {
+ buffer = data_blob_talloc(tmp_ctx, NULL, 4);
+ SIVAL(buffer.data, 0, printer->priority);
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRIORITY,
+ REG_DWORD,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PRINTENDTIME));
+ goto done;
+ }
+ }
+
+ if (force_update || printer->attributes != old_printer->attributes) {
+ buffer = data_blob_talloc(tmp_ctx, NULL, 4);
+ SIVAL(buffer.data, 0, (printer->attributes &
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS));
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTKEEPPRINTEDJOBS,
+ REG_DWORD,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_PRINTENDTIME));
+ goto done;
+ }
+
+ switch (printer->attributes & 0x3) {
+ case 0:
+ spooling = SPOOL_REGVAL_PRINTWHILESPOOLING;
+ break;
+ case 1:
+ spooling = SPOOL_REGVAL_PRINTAFTERSPOOLED;
+ break;
+ case 2:
+ spooling = SPOOL_REGVAL_PRINTDIRECT;
+ break;
+ default:
+ spooling = "unknown";
+ }
+ ok = push_reg_sz(tmp_ctx, &buffer, spooling);
+ if (!ok) {
+ DEBUG(0, ("printSpooling data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_PRINTSPOOLING,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ }
+
+ ok = push_reg_sz(tmp_ctx, &buffer, lp_netbios_name());
+ if (!ok) {
+ DEBUG(0, ("shortServerName data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_SHORTSERVERNAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_SHORTSERVERNAME));
+ goto done;
+ }
+
+ dnsdomname = get_mydnsfullname();
+ if (dnsdomname != NULL && dnsdomname[0] != '\0') {
+ longname = talloc_strdup(tmp_ctx, dnsdomname);
+ } else {
+ longname = talloc_strdup(tmp_ctx, lp_netbios_name());
+ }
+ if (longname == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ ok = push_reg_sz(tmp_ctx, &buffer, longname);
+ if (!ok) {
+ DEBUG(0, ("longname data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_SERVERNAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_SERVERNAME));
+ goto done;
+ }
+
+ uncname = talloc_asprintf(tmp_ctx, "\\\\%s\\%s",
+ lp_netbios_name(), printer->sharename);
+ ok = push_reg_sz(tmp_ctx, &buffer, uncname);
+ if (!ok) {
+ DEBUG(0, ("uncName data corrupted\n"));
+ result = WERR_INVALID_DATA;
+ goto done;
+ }
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ printer->sharename,
+ SPOOL_DSSPOOLER_KEY,
+ SPOOL_REG_UNCNAME,
+ REG_SZ,
+ buffer.data,
+ buffer.length);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("Failed to set %s\n", SPOOL_REG_UNCNAME));
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/********************************************************************
+ * Called by spoolss_api_setprinter
+ * when updating a printer description.
+ ********************************************************************/
+
+static WERROR update_printer(struct pipes_struct *p,
+ struct policy_handle *handle,
+ struct spoolss_SetPrinterInfoCtr *info_ctr,
+ struct spoolss_DeviceMode *devmode)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ uint32_t printer_mask = SPOOLSS_PRINTER_INFO_ALL;
+ struct spoolss_SetPrinterInfo2 *printer = info_ctr->info.info2;
+ struct spoolss_PrinterInfo2 *old_printer;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, handle);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int snum;
+ WERROR result = WERR_OK;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_binding_handle *b;
+
+ DEBUG(8,("update_printer\n"));
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (!Printer) {
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL)) {
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_get_printer(tmp_ctx, b,
+ lp_const_servicename(snum),
+ &old_printer);
+ if (!W_ERROR_IS_OK(result)) {
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* Do sanity check on the requested changes for Samba */
+ if (!check_printer_ok(tmp_ctx, printer, snum)) {
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* FIXME!!! If the driver has changed we really should verify that
+ it is installed before doing much else --jerry */
+
+ /* Check calling user has permission to update printer description */
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("update_printer: printer property change denied by handle\n"));
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* Call addprinter hook */
+ /* Check changes to see if this is really needed */
+
+ if (*lp_addprinter_command(talloc_tos(), lp_sub) &&
+ (!strequal(printer->drivername, old_printer->drivername) ||
+ !strequal(printer->comment, old_printer->comment) ||
+ !strequal(printer->portname, old_printer->portname) ||
+ !strequal(printer->location, old_printer->location)) )
+ {
+ char *raddr;
+
+ raddr = tsocket_address_inet_addr_string(remote_address,
+ p->mem_ctx);
+ if (raddr == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /* add_printer_hook() will call reload_services() */
+ if (!add_printer_hook(tmp_ctx, session_info->security_token,
+ printer, raddr,
+ p->msg_ctx)) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+ }
+
+ result = update_dsspooler(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ snum,
+ printer,
+ old_printer);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ printer_mask &= ~SPOOLSS_PRINTER_INFO_SECDESC;
+
+ if (devmode == NULL) {
+ printer_mask &= ~SPOOLSS_PRINTER_INFO_DEVMODE;
+ }
+ result = winreg_update_printer(tmp_ctx, b,
+ printer->sharename,
+ printer_mask,
+ printer,
+ devmode,
+ NULL);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR publish_or_unpublish_printer(struct pipes_struct *p,
+ struct policy_handle *handle,
+ struct spoolss_SetPrinterInfo7 *info7)
+{
+#ifdef HAVE_ADS
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ WERROR result;
+ int snum;
+ struct printer_handle *Printer;
+
+ if ( lp_security() != SEC_ADS ) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("publish_or_unpublish_printer, action = %d\n",info7->action));
+
+ if (!Printer)
+ return WERR_INVALID_HANDLE;
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_INVALID_HANDLE;
+
+ result = winreg_get_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_servicename(talloc_tos(), lp_sub, snum),
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ nt_printer_publish(pinfo2,
+ get_session_info_system(),
+ p->msg_ctx,
+ pinfo2,
+ info7->action);
+
+ TALLOC_FREE(pinfo2);
+ return WERR_OK;
+#else
+ return WERR_INVALID_LEVEL;
+#endif
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static WERROR update_printer_devmode(struct pipes_struct *p,
+ struct policy_handle *handle,
+ struct spoolss_DeviceMode *devmode)
+{
+ int snum;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, handle);
+ uint32_t info2_mask = SPOOLSS_PRINTER_INFO_DEVMODE;
+
+ DEBUG(8,("update_printer_devmode\n"));
+
+ if (!Printer) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* Check calling user has permission to update printer description */
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("update_printer: printer property change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ return winreg_update_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum),
+ info2_mask,
+ NULL,
+ devmode,
+ NULL);
+}
+
+
+/****************************************************************
+ _spoolss_SetPrinter
+****************************************************************/
+
+WERROR _spoolss_SetPrinter(struct pipes_struct *p,
+ struct spoolss_SetPrinter *r)
+{
+ WERROR result;
+
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_SetPrinter: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* check the level */
+ switch (r->in.info_ctr->level) {
+ case 0:
+ return control_printer(r->in.handle, r->in.command, p);
+ case 2:
+ result = update_printer(p, r->in.handle,
+ r->in.info_ctr,
+ r->in.devmode_ctr->devmode);
+ if (!W_ERROR_IS_OK(result))
+ return result;
+ if (r->in.secdesc_ctr->sd)
+ result = update_printer_sec(r->in.handle, p,
+ r->in.secdesc_ctr);
+ return result;
+ case 3:
+ return update_printer_sec(r->in.handle, p,
+ r->in.secdesc_ctr);
+ case 4: {
+ struct spoolss_PrinterInfo2 *old_printer;
+ struct spoolss_SetPrinterInfo2 *set_old_printer;
+ struct spoolss_SetPrinterInfoCtr *info_ctr;
+ struct dcerpc_binding_handle *b;
+ int snum;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INVALID_HANDLE;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_printer(tmp_ctx, b,
+ lp_const_servicename(snum),
+ &old_printer);
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INVALID_HANDLE;
+ }
+
+ old_printer->servername = talloc_strdup(tmp_ctx, r->in.info_ctr->info.info4->servername);
+ if (old_printer->servername == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ old_printer->printername = talloc_strdup(tmp_ctx, r->in.info_ctr->info.info4->printername);
+ if (old_printer->printername == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ old_printer->attributes = r->in.info_ctr->info.info4->attributes;
+
+ set_old_printer = talloc_zero(tmp_ctx, struct spoolss_SetPrinterInfo2);
+ if (set_old_printer == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ spoolss_printerinfo2_to_setprinterinfo2(old_printer, set_old_printer);
+
+ info_ctr = talloc_zero(tmp_ctx, struct spoolss_SetPrinterInfoCtr);
+ if (info_ctr == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ info_ctr->level = 2;
+ info_ctr->info.info2 = set_old_printer;
+
+ result = update_printer(p, r->in.handle,
+ info_ctr,
+ r->in.devmode_ctr->devmode);
+
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(tmp_ctx);
+ return result;
+ }
+
+ if (r->in.secdesc_ctr->sd) {
+ result = update_printer_sec(r->in.handle, p,
+ r->in.secdesc_ctr);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return result;
+ }
+ case 7:
+ return publish_or_unpublish_printer(p, r->in.handle,
+ r->in.info_ctr->info.info7);
+ case 8:
+ return update_printer_devmode(p, r->in.handle,
+ r->in.devmode_ctr->devmode);
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+/****************************************************************
+ _spoolss_FindClosePrinterNotify
+****************************************************************/
+
+WERROR _spoolss_FindClosePrinterNotify(struct pipes_struct *p,
+ struct spoolss_FindClosePrinterNotify *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_FindClosePrinterNotify: "
+ "Invalid handle (%s:%u:%u)\n", OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (Printer->notify.cli_chan != NULL &&
+ Printer->notify.cli_chan->active_connections > 0) {
+ int snum = -1;
+
+ if (Printer->printer_type == SPLHND_PRINTER) {
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+ }
+
+ srv_spoolss_replycloseprinter(snum, Printer);
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ TALLOC_FREE(Printer->notify.option);
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_AddJob
+****************************************************************/
+
+WERROR _spoolss_AddJob(struct pipes_struct *p,
+ struct spoolss_AddJob *r)
+{
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* this is what a NT server returns for AddJob. AddJob must fail on
+ * non-local printers */
+
+ if (r->in.level != 1) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ return WERR_INVALID_PARAMETER;
+}
+
+/****************************************************************************
+fill_job_info1
+****************************************************************************/
+
+static WERROR fill_job_info1(TALLOC_CTX *mem_ctx,
+ struct spoolss_JobInfo1 *r,
+ const print_queue_struct *queue,
+ uint32_t jobid,
+ int position, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct tm *t;
+
+ t = gmtime(&queue->time);
+
+ r->job_id = jobid;
+
+ r->printer_name = lp_servicename(mem_ctx, lp_sub, snum);
+ W_ERROR_HAVE_NO_MEMORY(r->printer_name);
+ r->server_name = talloc_strdup(mem_ctx, pinfo2->servername);
+ W_ERROR_HAVE_NO_MEMORY(r->server_name);
+ r->user_name = talloc_strdup(mem_ctx, queue->fs_user);
+ W_ERROR_HAVE_NO_MEMORY(r->user_name);
+ r->document_name = talloc_strdup(mem_ctx, queue->fs_file);
+ W_ERROR_HAVE_NO_MEMORY(r->document_name);
+ r->data_type = talloc_strdup(mem_ctx, "RAW");
+ W_ERROR_HAVE_NO_MEMORY(r->data_type);
+ r->text_status = talloc_strdup(mem_ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->text_status);
+
+ r->status = nt_printj_status(queue->status);
+ r->priority = queue->priority;
+ r->position = position;
+ r->total_pages = queue->page_count;
+ r->pages_printed = 0; /* ??? */
+
+ init_systemtime(&r->submitted, t);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+fill_job_info2
+****************************************************************************/
+
+static WERROR fill_job_info2(TALLOC_CTX *mem_ctx,
+ struct spoolss_JobInfo2 *r,
+ const print_queue_struct *queue,
+ uint32_t jobid,
+ int position, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ struct spoolss_DeviceMode *devmode)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct tm *t;
+
+ t = gmtime(&queue->time);
+
+ r->job_id = jobid;
+
+ r->printer_name = lp_servicename(mem_ctx, lp_sub, snum);
+ W_ERROR_HAVE_NO_MEMORY(r->printer_name);
+ r->server_name = talloc_strdup(mem_ctx, pinfo2->servername);
+ W_ERROR_HAVE_NO_MEMORY(r->server_name);
+ r->user_name = talloc_strdup(mem_ctx, queue->fs_user);
+ W_ERROR_HAVE_NO_MEMORY(r->user_name);
+ r->document_name = talloc_strdup(mem_ctx, queue->fs_file);
+ W_ERROR_HAVE_NO_MEMORY(r->document_name);
+ r->notify_name = talloc_strdup(mem_ctx, queue->fs_user);
+ W_ERROR_HAVE_NO_MEMORY(r->notify_name);
+ r->data_type = talloc_strdup(mem_ctx, "RAW");
+ W_ERROR_HAVE_NO_MEMORY(r->data_type);
+ r->print_processor = talloc_strdup(mem_ctx, "winprint");
+ W_ERROR_HAVE_NO_MEMORY(r->print_processor);
+ r->parameters = talloc_strdup(mem_ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->parameters);
+ r->driver_name = talloc_strdup(mem_ctx, pinfo2->drivername);
+ W_ERROR_HAVE_NO_MEMORY(r->driver_name);
+
+ r->devmode = devmode;
+
+ r->text_status = talloc_strdup(mem_ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->text_status);
+
+ r->secdesc = NULL;
+
+ r->status = nt_printj_status(queue->status);
+ r->priority = queue->priority;
+ r->position = position;
+ r->start_time = 0;
+ r->until_time = 0;
+ r->total_pages = queue->page_count;
+ r->size = queue->size;
+ init_systemtime(&r->submitted, t);
+ r->time = 0;
+ r->pages_printed = 0; /* ??? */
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Enumjobs at level 1.
+****************************************************************************/
+
+static WERROR enumjobs_level1(TALLOC_CTX *mem_ctx,
+ const print_queue_struct *queue,
+ uint32_t num_queues, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ union spoolss_JobInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_JobInfo *info;
+ int i;
+ WERROR result = WERR_OK;
+ uint32_t num_filled;
+ struct tdb_print_db *pdb;
+
+ info = talloc_array(mem_ctx, union spoolss_JobInfo, num_queues);
+ if (info == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto err_out;
+ }
+
+ pdb = get_print_db_byname(pinfo2->sharename);
+ if (pdb == NULL) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_info_free;
+ }
+
+ num_filled = 0;
+ for (i = 0; i < num_queues; i++) {
+ uint32_t jobid = sysjob_to_jobid_pdb(pdb, queue[i].sysjob);
+ if (jobid == (uint32_t)-1) {
+ DEBUG(4, ("skipping sysjob %d\n", queue[i].sysjob));
+ continue;
+ }
+
+ result = fill_job_info1(info,
+ &info[num_filled].info1,
+ &queue[i],
+ jobid,
+ i,
+ snum,
+ pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_pdb_drop;
+ }
+
+ num_filled++;
+ }
+
+ release_print_db(pdb);
+ *info_p = info;
+ *count = num_filled;
+
+ return WERR_OK;
+
+err_pdb_drop:
+ release_print_db(pdb);
+err_info_free:
+ TALLOC_FREE(info);
+err_out:
+ *count = 0;
+ return result;
+}
+
+/****************************************************************************
+ Enumjobs at level 2.
+****************************************************************************/
+
+static WERROR enumjobs_level2(TALLOC_CTX *mem_ctx,
+ const print_queue_struct *queue,
+ uint32_t num_queues, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ union spoolss_JobInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_JobInfo *info;
+ int i;
+ WERROR result = WERR_OK;
+ uint32_t num_filled;
+ struct tdb_print_db *pdb;
+
+ info = talloc_array(mem_ctx, union spoolss_JobInfo, num_queues);
+ if (info == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto err_out;
+ }
+
+ pdb = get_print_db_byname(pinfo2->sharename);
+ if (pdb == NULL) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_info_free;
+ }
+
+ num_filled = 0;
+ for (i = 0; i< num_queues; i++) {
+ struct spoolss_DeviceMode *devmode;
+ uint32_t jobid = sysjob_to_jobid_pdb(pdb, queue[i].sysjob);
+ if (jobid == (uint32_t)-1) {
+ DEBUG(4, ("skipping sysjob %d\n", queue[i].sysjob));
+ continue;
+ }
+
+ result = spoolss_create_default_devmode(info,
+ pinfo2->printername,
+ &devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Can't proceed w/o a devmode!\n"));
+ goto err_pdb_drop;
+ }
+
+ result = fill_job_info2(info,
+ &info[num_filled].info2,
+ &queue[i],
+ jobid,
+ i,
+ snum,
+ pinfo2,
+ devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_pdb_drop;
+ }
+ num_filled++;
+ }
+
+ release_print_db(pdb);
+ *info_p = info;
+ *count = num_filled;
+
+ return WERR_OK;
+
+err_pdb_drop:
+ release_print_db(pdb);
+err_info_free:
+ TALLOC_FREE(info);
+err_out:
+ *count = 0;
+ return result;
+}
+
+/****************************************************************************
+ Enumjobs at level 3.
+****************************************************************************/
+
+static WERROR enumjobs_level3(TALLOC_CTX *mem_ctx,
+ const print_queue_struct *queue,
+ uint32_t num_queues, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ union spoolss_JobInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_JobInfo *info;
+ int i;
+ WERROR result = WERR_OK;
+ uint32_t num_filled;
+ struct tdb_print_db *pdb;
+
+ info = talloc_array(mem_ctx, union spoolss_JobInfo, num_queues);
+ if (info == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto err_out;
+ }
+
+ pdb = get_print_db_byname(pinfo2->sharename);
+ if (pdb == NULL) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_info_free;
+ }
+
+ num_filled = 0;
+ for (i = 0; i < num_queues; i++) {
+ uint32_t jobid = sysjob_to_jobid_pdb(pdb, queue[i].sysjob);
+ if (jobid == (uint32_t)-1) {
+ DEBUG(4, ("skipping sysjob %d\n", queue[i].sysjob));
+ continue;
+ }
+
+ info[num_filled].info3.job_id = jobid;
+ /* next_job_id is overwritten on next iteration */
+ info[num_filled].info3.next_job_id = 0;
+ info[num_filled].info3.reserved = 0;
+
+ if (num_filled > 0) {
+ info[num_filled - 1].info3.next_job_id = jobid;
+ }
+ num_filled++;
+ }
+
+ release_print_db(pdb);
+ *info_p = info;
+ *count = num_filled;
+
+ return WERR_OK;
+
+err_info_free:
+ TALLOC_FREE(info);
+err_out:
+ *count = 0;
+ return result;
+}
+
+/****************************************************************
+ _spoolss_EnumJobs
+****************************************************************/
+
+WERROR _spoolss_EnumJobs(struct pipes_struct *p,
+ struct spoolss_EnumJobs *r)
+{
+ WERROR result;
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ int snum;
+ print_status_struct prt_status;
+ print_queue_struct *queue = NULL;
+ uint32_t count;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if ((r->in.level != 1) && (r->in.level != 2) && (r->in.level != 3)) {
+ DEBUG(4, ("EnumJobs level %d not supported\n", r->in.level));
+ return WERR_INVALID_LEVEL;
+ }
+
+ DEBUG(4,("_spoolss_EnumJobs\n"));
+
+ *r->out.needed = 0;
+ *r->out.count = 0;
+ *r->out.info = NULL;
+
+ /* lookup the printer snum and tdb entry */
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ result = winreg_get_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum),
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ count = print_queue_status(p->msg_ctx, snum, &queue, &prt_status);
+ DEBUGADD(4,("count:[%d], status:[%d], [%s]\n",
+ count, prt_status.status, prt_status.message));
+
+ if (count == 0) {
+ SAFE_FREE(queue);
+ TALLOC_FREE(pinfo2);
+ return WERR_OK;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ result = enumjobs_level1(p->mem_ctx, queue, count, snum,
+ pinfo2, r->out.info, r->out.count);
+ break;
+ case 2:
+ result = enumjobs_level2(p->mem_ctx, queue, count, snum,
+ pinfo2, r->out.info, r->out.count);
+ break;
+ case 3:
+ result = enumjobs_level3(p->mem_ctx, queue, count, snum,
+ pinfo2, r->out.info, r->out.count);
+ break;
+ default:
+ SMB_ASSERT(false); /* level checked on entry */
+ break;
+ }
+
+ SAFE_FREE(queue);
+ TALLOC_FREE(pinfo2);
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ spoolss_EnumJobs,
+ *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_ScheduleJob
+****************************************************************/
+
+WERROR _spoolss_ScheduleJob(struct pipes_struct *p,
+ struct spoolss_ScheduleJob *r)
+{
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static WERROR spoolss_setjob_1(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *printer_name,
+ uint32_t job_id,
+ struct spoolss_SetJobInfo1 *r)
+{
+ char *old_doc_name;
+
+ if (!print_job_get_name(mem_ctx, printer_name, job_id, &old_doc_name)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (strequal(old_doc_name, r->document_name)) {
+ return WERR_OK;
+ }
+
+ if (!print_job_set_name(global_event_context(), msg_ctx,
+ printer_name, job_id, r->document_name)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_SetJob
+****************************************************************/
+
+WERROR _spoolss_SetJob(struct pipes_struct *p,
+ struct spoolss_SetJob *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ int snum;
+ WERROR errcode = WERR_INVALID_FUNCTION;
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!print_job_exists(lp_const_servicename(snum), r->in.job_id)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ switch (r->in.command) {
+ case SPOOLSS_JOB_CONTROL_CANCEL:
+ case SPOOLSS_JOB_CONTROL_DELETE:
+ errcode = print_job_delete(session_info, p->msg_ctx,
+ snum, r->in.job_id);
+ if (W_ERROR_EQUAL(errcode, WERR_PRINTER_HAS_JOBS_QUEUED)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case SPOOLSS_JOB_CONTROL_PAUSE:
+ errcode = print_job_pause(session_info, p->msg_ctx,
+ snum, r->in.job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RESTART:
+ case SPOOLSS_JOB_CONTROL_RESUME:
+ errcode = print_job_resume(session_info, p->msg_ctx,
+ snum, r->in.job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_NOOP:
+ errcode = WERR_OK;
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!W_ERROR_IS_OK(errcode)) {
+ return errcode;
+ }
+
+ if (r->in.ctr == NULL) {
+ return errcode;
+ }
+
+ switch (r->in.ctr->level) {
+ case 1:
+ errcode = spoolss_setjob_1(p->mem_ctx, p->msg_ctx,
+ lp_const_servicename(snum),
+ r->in.job_id,
+ r->in.ctr->info.info1);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ return errcode;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers by level and architecture.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level_by_architecture(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ const char *architecture,
+ uint32_t level,
+ union spoolss_DriverInfo **info_p,
+ uint32_t *count_p)
+{
+ int i;
+ uint32_t version;
+ struct spoolss_DriverInfo8 *driver;
+ union spoolss_DriverInfo *info = NULL;
+ uint32_t count = 0;
+ WERROR result = WERR_OK;
+ uint32_t num_drivers;
+ const char **drivers;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ *count_p = 0;
+ *info_p = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ session_info,
+ msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ result = winreg_get_driver_list(tmp_ctx, b,
+ architecture, version,
+ &num_drivers, &drivers);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+ DEBUG(4, ("we have:[%d] drivers in environment"
+ " [%s] and version [%d]\n",
+ num_drivers, architecture, version));
+
+ if (num_drivers != 0) {
+ info = talloc_realloc(tmp_ctx, info,
+ union spoolss_DriverInfo,
+ count + num_drivers);
+ if (!info) {
+ DEBUG(0,("enumprinterdrivers_level_by_architecture: "
+ "failed to enlarge driver info buffer!\n"));
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < num_drivers; i++) {
+ DEBUG(5, ("\tdriver: [%s]\n", drivers[i]));
+
+ result = winreg_get_driver(tmp_ctx, b,
+ architecture, drivers[i],
+ version, &driver);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ switch (level) {
+ case 1:
+ result = fill_printer_driver_info1(info, &info[count+i].info1,
+ driver, servername);
+ break;
+ case 2:
+ result = fill_printer_driver_info2(info, &info[count+i].info2,
+ driver, servername);
+ break;
+ case 3:
+ result = fill_printer_driver_info3(info, &info[count+i].info3,
+ driver, servername);
+ break;
+ case 4:
+ result = fill_printer_driver_info4(info, &info[count+i].info4,
+ driver, servername);
+ break;
+ case 5:
+ result = fill_printer_driver_info5(info, &info[count+i].info5,
+ driver, servername);
+ break;
+ case 6:
+ result = fill_printer_driver_info6(info, &info[count+i].info6,
+ driver, servername);
+ break;
+ case 8:
+ result = fill_printer_driver_info8(info, &info[count+i].info8,
+ driver, servername);
+ break;
+ default:
+ result = WERR_INVALID_LEVEL;
+ break;
+ }
+
+ TALLOC_FREE(driver);
+
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+ }
+
+ count += num_drivers;
+ TALLOC_FREE(drivers);
+ }
+
+out:
+ if (W_ERROR_IS_OK(result)) {
+ *info_p = talloc_move(mem_ctx, &info);
+ *count_p = count;
+ }
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers by level.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servername,
+ const char *architecture,
+ uint32_t level,
+ union spoolss_DriverInfo **info_p,
+ uint32_t *count_p)
+{
+ uint32_t a,i;
+ WERROR result = WERR_OK;
+
+ if (strequal(architecture, SPOOLSS_ARCHITECTURE_ALL)) {
+
+ for (a=0; archi_table[a].long_archi != NULL; a++) {
+
+ union spoolss_DriverInfo *info = NULL;
+ uint32_t count = 0;
+
+ result = enumprinterdrivers_level_by_architecture(mem_ctx,
+ session_info,
+ msg_ctx,
+ servername,
+ archi_table[a].long_archi,
+ level,
+ &info,
+ &count);
+ if (!W_ERROR_IS_OK(result)) {
+ continue;
+ }
+
+ for (i=0; i < count; i++) {
+ ADD_TO_ARRAY(mem_ctx, union spoolss_DriverInfo,
+ info[i], info_p, count_p);
+ }
+ }
+
+ return result;
+ }
+
+ return enumprinterdrivers_level_by_architecture(mem_ctx,
+ session_info,
+ msg_ctx,
+ servername,
+ architecture,
+ level,
+ info_p,
+ count_p);
+}
+
+/****************************************************************
+ _spoolss_EnumPrinterDrivers
+****************************************************************/
+
+WERROR _spoolss_EnumPrinterDrivers(struct pipes_struct *p,
+ struct spoolss_EnumPrinterDrivers *r)
+{
+ const char *cservername;
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("_spoolss_EnumPrinterDrivers\n"));
+
+ *r->out.needed = 0;
+ *r->out.count = 0;
+ *r->out.info = NULL;
+
+ cservername = canon_servername(r->in.server);
+
+ if (!is_myname_or_ipaddr(cservername)) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ result = enumprinterdrivers_level(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ cservername,
+ r->in.environment,
+ r->in.level,
+ r->out.info,
+ r->out.count);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ 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_EnumForms
+****************************************************************/
+
+WERROR _spoolss_EnumForms(struct pipes_struct *p,
+ struct spoolss_EnumForms *r)
+{
+ WERROR result;
+
+ *r->out.count = 0;
+ *r->out.needed = 0;
+ *r->out.info = NULL;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0) ) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("_spoolss_EnumForms\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", r->in.offered));
+ DEBUGADD(5,("Info level [%d]\n", r->in.level));
+
+ switch (r->in.level) {
+ case 1:
+ result = winreg_printer_enumforms1_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ r->out.count,
+ r->out.info);
+ break;
+ default:
+ result = WERR_INVALID_LEVEL;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ if (*r->out.count == 0) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ 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_GetForm
+****************************************************************/
+
+WERROR _spoolss_GetForm(struct pipes_struct *p,
+ struct spoolss_GetForm *r)
+{
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ TALLOC_FREE(r->out.info);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("_spoolss_GetForm\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", r->in.offered));
+ DEBUGADD(5,("Info level [%d]\n", r->in.level));
+
+ switch (r->in.level) {
+ case 1:
+ result = winreg_printer_getform1_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ r->in.form_name,
+ &r->out.info->info1);
+ break;
+ default:
+ result = WERR_INVALID_LEVEL;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(r->out.info);
+ return result;
+ }
+
+ *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);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR fill_port_1(TALLOC_CTX *mem_ctx,
+ struct spoolss_PortInfo1 *r,
+ const char *name)
+{
+ r->port_name = talloc_strdup(mem_ctx, name);
+ W_ERROR_HAVE_NO_MEMORY(r->port_name);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ TODO: This probably needs distinguish between TCP/IP and Local ports
+ somehow.
+****************************************************************************/
+
+static WERROR fill_port_2(TALLOC_CTX *mem_ctx,
+ struct spoolss_PortInfo2 *r,
+ const char *name)
+{
+ r->port_name = talloc_strdup(mem_ctx, name);
+ W_ERROR_HAVE_NO_MEMORY(r->port_name);
+
+ r->monitor_name = talloc_strdup(mem_ctx, "Local Monitor");
+ W_ERROR_HAVE_NO_MEMORY(r->monitor_name);
+
+ r->description = talloc_strdup(mem_ctx, SPL_LOCAL_PORT);
+ W_ERROR_HAVE_NO_MEMORY(r->description);
+
+ r->port_type = SPOOLSS_PORT_TYPE_WRITE;
+ r->reserved = 0;
+
+ return WERR_OK;
+}
+
+
+/****************************************************************************
+ wrapper around the enum ports command
+****************************************************************************/
+
+static WERROR enumports_hook(TALLOC_CTX *ctx, int *count, char ***lines)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *cmd = lp_enumports_command(talloc_tos(), lp_sub);
+ char **qlines = NULL;
+ char *command = NULL;
+ int numlines;
+ int ret;
+ int fd;
+
+ *count = 0;
+ *lines = NULL;
+
+ /* if no hook then just fill in the default port */
+
+ if ( !*cmd ) {
+ if (!(qlines = talloc_array( NULL, char*, 2 ))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (!(qlines[0] = talloc_strdup(qlines, SAMBA_PRINTER_PORT_NAME ))) {
+ TALLOC_FREE(qlines);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ qlines[1] = NULL;
+ numlines = 1;
+ }
+ else {
+ /* we have a valid enumport command */
+
+ command = talloc_asprintf(ctx, "%s \"%d\"", cmd, 1);
+ if (!command) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, &fd, NULL);
+ DEBUG(10,("Returned [%d]\n", ret));
+ TALLOC_FREE(command);
+ if (ret != 0) {
+ if (fd != -1) {
+ close(fd);
+ }
+ return WERR_ACCESS_DENIED;
+ }
+
+ numlines = 0;
+ qlines = fd_lines_load(fd, &numlines, 0, NULL);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+ }
+
+ *count = numlines;
+ *lines = qlines;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports level 1.
+****************************************************************************/
+
+static WERROR enumports_level_1(TALLOC_CTX *mem_ctx,
+ union spoolss_PortInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_PortInfo *info = NULL;
+ int i=0;
+ WERROR result = WERR_OK;
+ char **qlines = NULL;
+ int numlines = 0;
+
+ result = enumports_hook(talloc_tos(), &numlines, &qlines );
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ if (numlines) {
+ info = talloc_array(mem_ctx, union spoolss_PortInfo, numlines);
+ if (!info) {
+ DEBUG(10,("Returning WERR_NOT_ENOUGH_MEMORY\n"));
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ result = fill_port_1(info, &info[i].info1, qlines[i]);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+ }
+ }
+ TALLOC_FREE(qlines);
+
+out:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(info);
+ TALLOC_FREE(qlines);
+ *count = 0;
+ *info_p = NULL;
+ return result;
+ }
+
+ *info_p = info;
+ *count = numlines;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports level 2.
+****************************************************************************/
+
+static WERROR enumports_level_2(TALLOC_CTX *mem_ctx,
+ union spoolss_PortInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_PortInfo *info = NULL;
+ int i=0;
+ WERROR result = WERR_OK;
+ char **qlines = NULL;
+ int numlines = 0;
+
+ result = enumports_hook(talloc_tos(), &numlines, &qlines );
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ if (numlines) {
+ info = talloc_array(mem_ctx, union spoolss_PortInfo, numlines);
+ if (!info) {
+ DEBUG(10,("Returning WERR_NOT_ENOUGH_MEMORY\n"));
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ result = fill_port_2(info, &info[i].info2, qlines[i]);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+ }
+ }
+ TALLOC_FREE(qlines);
+
+out:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(info);
+ TALLOC_FREE(qlines);
+ *count = 0;
+ *info_p = NULL;
+ return result;
+ }
+
+ *info_p = info;
+ *count = numlines;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_EnumPorts
+****************************************************************/
+
+WERROR _spoolss_EnumPorts(struct pipes_struct *p,
+ struct spoolss_EnumPorts *r)
+{
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("_spoolss_EnumPorts\n"));
+
+ *r->out.count = 0;
+ *r->out.needed = 0;
+ *r->out.info = NULL;
+
+ switch (r->in.level) {
+ case 1:
+ result = enumports_level_1(p->mem_ctx, r->out.info,
+ r->out.count);
+ break;
+ case 2:
+ result = enumports_level_2(p->mem_ctx, r->out.info,
+ r->out.count);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ 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);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR spoolss_addprinterex_level_2(struct pipes_struct *p,
+ const char *server,
+ struct spoolss_SetPrinterInfoCtr *info_ctr,
+ struct spoolss_DeviceMode *devmode,
+ struct security_descriptor *secdesc,
+ struct spoolss_UserLevelCtr *user_ctr,
+ struct policy_handle *handle)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct spoolss_SetPrinterInfo2 *info2 = info_ctr->info.info2;
+ uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ALL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int snum;
+ WERROR err = WERR_OK;
+
+ /* samba does not have a concept of local, non-shared printers yet, so
+ * make sure we always setup sharename - gd */
+ if ((info2->sharename == NULL || info2->sharename[0] == '\0') &&
+ (info2->printername != NULL && info2->printername[0] != '\0')) {
+ DEBUG(5, ("spoolss_addprinterex_level_2: "
+ "no sharename has been set, setting printername %s as sharename\n",
+ info2->printername));
+ info2->sharename = info2->printername;
+ }
+
+ /* check to see if the printer already exists */
+ if ((snum = print_queue_snum(info2->sharename)) != -1) {
+ DEBUG(5, ("spoolss_addprinterex_level_2: Attempted to add a printer named [%s] when one already existed!\n",
+ info2->sharename));
+ return WERR_PRINTER_ALREADY_EXISTS;
+ }
+
+ if (!lp_force_printername(GLOBAL_SECTION_SNUM)) {
+ if ((snum = print_queue_snum(info2->printername)) != -1) {
+ DEBUG(5, ("spoolss_addprinterex_level_2: Attempted to add a printer named [%s] when one already existed!\n",
+ info2->printername));
+ return WERR_PRINTER_ALREADY_EXISTS;
+ }
+ }
+
+ /* validate printer info struct */
+ if (!info2->printername || strlen(info2->printername) == 0) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+ if (!info2->portname || strlen(info2->portname) == 0) {
+ return WERR_UNKNOWN_PORT;
+ }
+ if (!info2->drivername || strlen(info2->drivername) == 0) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ if (!info2->printprocessor || strlen(info2->printprocessor) == 0) {
+ return WERR_UNKNOWN_PRINTPROCESSOR;
+ }
+
+ /* FIXME!!! smbd should check to see if the driver is installed before
+ trying to add a printer like this --jerry */
+
+ if (*lp_addprinter_command(talloc_tos(), lp_sub) ) {
+ char *raddr;
+
+ raddr = tsocket_address_inet_addr_string(remote_address,
+ p->mem_ctx);
+ if (raddr == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if ( !add_printer_hook(p->mem_ctx, session_info->security_token,
+ info2, raddr,
+ p->msg_ctx) ) {
+ return WERR_ACCESS_DENIED;
+ }
+ } else {
+ DEBUG(0,("spoolss_addprinterex_level_2: add printer for printer %s called and no "
+ "smb.conf parameter \"addprinter command\" is defined. This "
+ "parameter must exist for this call to succeed\n",
+ info2->sharename ));
+ }
+
+ if ((snum = print_queue_snum(info2->sharename)) == -1) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* you must be a printer admin to add a new printer */
+ if (!W_ERROR_IS_OK(print_access_check(session_info,
+ p->msg_ctx,
+ snum,
+ PRINTER_ACCESS_ADMINISTER))) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * Do sanity check on the requested changes for Samba.
+ */
+
+ if (!check_printer_ok(p->mem_ctx, info2, snum)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (devmode == NULL) {
+ info2_mask &= ~SPOOLSS_PRINTER_INFO_DEVMODE;
+ }
+
+ err = update_dsspooler(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ 0,
+ info2,
+ NULL);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ err = winreg_update_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ info2->sharename,
+ info2_mask,
+ info2,
+ devmode,
+ secdesc);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ err = open_printer_hnd(p, handle, info2->printername, PRINTER_ACCESS_ADMINISTER);
+ if (!W_ERROR_IS_OK(err)) {
+ /* Handle open failed - remove addition. */
+ ZERO_STRUCTP(handle);
+ return err;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_AddPrinterEx
+****************************************************************/
+
+WERROR _spoolss_AddPrinterEx(struct pipes_struct *p,
+ struct spoolss_AddPrinterEx *r)
+{
+ switch (r->in.info_ctr->level) {
+ case 1:
+ /* we don't handle yet */
+ /* but I know what to do ... */
+ return WERR_INVALID_LEVEL;
+ case 2:
+ return spoolss_addprinterex_level_2(p, r->in.server,
+ r->in.info_ctr,
+ r->in.devmode_ctr->devmode,
+ r->in.secdesc_ctr->sd,
+ r->in.userlevel_ctr,
+ r->out.handle);
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+}
+
+/****************************************************************
+ _spoolss_AddPrinter
+****************************************************************/
+
+WERROR _spoolss_AddPrinter(struct pipes_struct *p,
+ struct spoolss_AddPrinter *r)
+{
+ struct spoolss_AddPrinterEx a;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+
+ ZERO_STRUCT(userlevel_ctr);
+
+ userlevel_ctr.level = 1;
+
+ a.in.server = r->in.server;
+ a.in.info_ctr = r->in.info_ctr;
+ a.in.devmode_ctr = r->in.devmode_ctr;
+ a.in.secdesc_ctr = r->in.secdesc_ctr;
+ a.in.userlevel_ctr = &userlevel_ctr;
+ a.out.handle = r->out.handle;
+
+ return _spoolss_AddPrinterEx(p, &a);
+}
+
+/****************************************************************
+ _spoolss_AddPrinterDriverEx
+****************************************************************/
+
+WERROR _spoolss_AddPrinterDriverEx(struct pipes_struct *p,
+ struct spoolss_AddPrinterDriverEx *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ WERROR err = WERR_OK;
+ const char *driver_name = NULL;
+ const char *driver_directory = NULL;
+ uint32_t version;
+
+ /*
+ * we only support the semantics of AddPrinterDriver()
+ * i.e. only copy files that are newer than existing ones
+ */
+
+ if (r->in.flags == 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!(r->in.flags & APD_COPY_ALL_FILES) &&
+ !(r->in.flags & APD_COPY_NEW_FILES)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* FIXME */
+ if (r->in.info_ctr->level != 3 &&
+ r->in.info_ctr->level != 6 &&
+ r->in.info_ctr->level != 8) {
+ DEBUG(0,("%s: level %d not yet implemented\n", __func__,
+ r->in.info_ctr->level));
+ return WERR_INVALID_LEVEL;
+ }
+
+ DEBUG(5,("Cleaning driver's information\n"));
+ err = clean_up_driver_struct(p->mem_ctx,
+ session_info,
+ r->in.info_ctr,
+ r->in.flags,
+ &driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ DBG_ERR("clean_up_driver_struct failed - %s\n",
+ win_errstr(err));
+ goto done;
+ }
+
+ DEBUG(5,("Moving driver to final destination\n"));
+ err = move_driver_to_download_area(session_info,
+ r->in.info_ctr,
+ driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ DBG_ERR("move_driver_to_download_area failed - %s\n",
+ win_errstr(err));
+ goto done;
+ }
+
+ err = winreg_add_driver_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ r->in.info_ctr,
+ &driver_name,
+ &version);
+ if (!W_ERROR_IS_OK(err)) {
+ DBG_ERR("winreg_add_driver_internal failed - %s\n",
+ win_errstr(err));
+ goto done;
+ }
+
+ /*
+ * I think this is where the DrvUpgradePrinter() hook would be
+ * be called in a driver's interface DLL on a Windows NT 4.0/2k
+ * server. Right now, we just need to send ourselves a message
+ * to update each printer bound to this driver. --jerry
+ */
+
+ if (!srv_spoolss_drv_upgrade_printer(driver_name, p->msg_ctx)) {
+ DEBUG(0,("%s: Failed to send message about upgrading driver [%s]!\n",
+ __func__, driver_name));
+ }
+
+done:
+ return err;
+}
+
+/****************************************************************
+ _spoolss_AddPrinterDriver
+****************************************************************/
+
+WERROR _spoolss_AddPrinterDriver(struct pipes_struct *p,
+ struct spoolss_AddPrinterDriver *r)
+{
+ struct spoolss_AddPrinterDriverEx a;
+
+ switch (r->in.info_ctr->level) {
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ a.in.servername = r->in.servername;
+ a.in.info_ctr = r->in.info_ctr;
+ a.in.flags = APD_COPY_NEW_FILES;
+
+ return _spoolss_AddPrinterDriverEx(p, &a);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+struct _spoolss_paths {
+ int type;
+ const char *share;
+ const char *dir;
+};
+
+enum { SPOOLSS_DRIVER_PATH, SPOOLSS_PRTPROCS_PATH };
+
+static const struct _spoolss_paths spoolss_paths[]= {
+ { SPOOLSS_DRIVER_PATH, "print$", "DRIVERS" },
+ { SPOOLSS_PRTPROCS_PATH, "prnproc$", "PRTPROCS" }
+};
+
+static WERROR compose_spoolss_server_path(TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *environment,
+ int component,
+ char **path)
+{
+ const char *pservername = NULL;
+ const char *long_archi;
+ const char *short_archi;
+
+ *path = NULL;
+
+ /* environment may be empty */
+ if (environment && strlen(environment)) {
+ long_archi = environment;
+ } else {
+ long_archi = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+ "spoolss", "architecture",
+ GLOBAL_SPOOLSS_ARCHITECTURE);
+ }
+
+ /* servername may be empty */
+ if (servername && strlen(servername)) {
+ pservername = canon_servername(servername);
+
+ if (!is_myname_or_ipaddr(pservername)) {
+ return WERR_INVALID_PARAMETER;
+ }
+ }
+
+ if (!(short_archi = get_short_archi(long_archi))) {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ switch (component) {
+ case SPOOLSS_PRTPROCS_PATH:
+ case SPOOLSS_DRIVER_PATH:
+ if (pservername) {
+ *path = talloc_asprintf(mem_ctx,
+ "\\\\%s\\%s\\%s",
+ pservername,
+ spoolss_paths[component].share,
+ short_archi);
+ } else {
+ *path = talloc_asprintf(mem_ctx, "%s\\%s\\%s",
+ SPOOLSS_DEFAULT_SERVER_PATH,
+ spoolss_paths[component].dir,
+ short_archi);
+ }
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!*path) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriverdir_level_1(TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *environment,
+ struct spoolss_DriverDirectoryInfo1 *r)
+{
+ WERROR werr;
+ char *path = NULL;
+
+ werr = compose_spoolss_server_path(mem_ctx,
+ servername,
+ environment,
+ SPOOLSS_DRIVER_PATH,
+ &path);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ DEBUG(4,("printer driver directory: [%s]\n", path));
+
+ r->directory_name = path;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_GetPrinterDriverDirectory
+****************************************************************/
+
+WERROR _spoolss_GetPrinterDriverDirectory(struct pipes_struct *p,
+ struct spoolss_GetPrinterDriverDirectory *r)
+{
+ WERROR werror;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ TALLOC_FREE(r->out.info);
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("_spoolss_GetPrinterDriverDirectory: level %d\n",
+ r->in.level));
+
+ *r->out.needed = 0;
+
+ /* r->in.level is ignored */
+
+ werror = getprinterdriverdir_level_1(p->mem_ctx,
+ r->in.server,
+ r->in.environment,
+ &r->out.info->info1);
+ if (!W_ERROR_IS_OK(werror)) {
+ TALLOC_FREE(r->out.info);
+ return werror;
+ }
+
+ *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_EnumPrinterData
+****************************************************************/
+
+WERROR _spoolss_EnumPrinterData(struct pipes_struct *p,
+ struct spoolss_EnumPrinterData *r)
+{
+ WERROR result;
+ struct spoolss_EnumPrinterDataEx r2;
+ uint32_t count;
+ struct spoolss_PrinterEnumValues *info, *val = NULL;
+ uint32_t needed;
+
+ r2.in.handle = r->in.handle;
+ r2.in.key_name = "PrinterDriverData";
+ r2.in.offered = 0;
+ r2.out.count = &count;
+ r2.out.info = &info;
+ r2.out.needed = &needed;
+
+ result = _spoolss_EnumPrinterDataEx(p, &r2);
+ if (W_ERROR_EQUAL(result, WERR_MORE_DATA)) {
+ r2.in.offered = needed;
+ result = _spoolss_EnumPrinterDataEx(p, &r2);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ /*
+ * The NT machine wants to know the biggest size of value and data
+ *
+ * cf: MSDN EnumPrinterData remark section
+ */
+
+ if (!r->in.value_offered && !r->in.data_offered) {
+ uint32_t biggest_valuesize = 0;
+ uint32_t biggest_datasize = 0;
+ int i, name_length;
+
+ DEBUGADD(6,("Activating NT mega-hack to find sizes\n"));
+
+ for (i=0; i<count; i++) {
+
+ name_length = strlen(info[i].value_name);
+ if (strlen(info[i].value_name) > biggest_valuesize) {
+ biggest_valuesize = name_length;
+ }
+
+ if (info[i].data_length > biggest_datasize) {
+ biggest_datasize = info[i].data_length;
+ }
+
+ DEBUG(6,("current values: [%d], [%d]\n", biggest_valuesize,
+ biggest_datasize));
+ }
+
+ /* the value is an UNICODE string but real_value_size is the length
+ in bytes including the trailing 0 */
+
+ *r->out.value_needed = 2 * (1 + biggest_valuesize);
+ *r->out.data_needed = biggest_datasize;
+
+ DEBUG(6,("final values: [%d], [%d]\n",
+ *r->out.value_needed, *r->out.data_needed));
+
+ return WERR_OK;
+ }
+
+ if (r->in.enum_index < count) {
+ val = &info[r->in.enum_index];
+ }
+
+ if (val == NULL) {
+ /* out_value should default to "" or else NT4 has
+ problems unmarshalling the response */
+
+ if (r->in.value_offered) {
+ *r->out.value_needed = 1;
+ r->out.value_name = talloc_strdup(r, "");
+ if (!r->out.value_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ r->out.value_name = NULL;
+ *r->out.value_needed = 0;
+ }
+
+ /* the data is counted in bytes */
+
+ *r->out.data_needed = r->in.data_offered;
+
+ result = WERR_NO_MORE_ITEMS;
+ } else {
+ /*
+ * the value is:
+ * - counted in bytes in the request
+ * - counted in UNICODE chars in the max reply
+ * - counted in bytes in the real size
+ *
+ * take a pause *before* coding not *during* coding
+ */
+
+ /* name */
+ if (r->in.value_offered) {
+ r->out.value_name = talloc_strdup(r, val->value_name);
+ if (!r->out.value_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ *r->out.value_needed = val->value_name_len;
+ } else {
+ r->out.value_name = NULL;
+ *r->out.value_needed = 0;
+ }
+
+ /* type */
+
+ *r->out.type = val->type;
+
+ /* data - counted in bytes */
+
+ /*
+ * See the section "Dynamically Typed Query Parameters"
+ * in MS-RPRN.
+ */
+
+ if (r->out.data && val->data && val->data->data &&
+ val->data_length && r->in.data_offered) {
+ memcpy(r->out.data, val->data->data,
+ MIN(val->data_length,r->in.data_offered));
+ }
+
+ *r->out.data_needed = val->data_length;
+
+ result = WERR_OK;
+ }
+
+ return result;
+}
+
+/****************************************************************
+ _spoolss_SetPrinterData
+****************************************************************/
+
+WERROR _spoolss_SetPrinterData(struct pipes_struct *p,
+ struct spoolss_SetPrinterData *r)
+{
+ struct spoolss_SetPrinterDataEx r2;
+
+ r2.in.handle = r->in.handle;
+ r2.in.key_name = "PrinterDriverData";
+ r2.in.value_name = r->in.value_name;
+ r2.in.type = r->in.type;
+ r2.in.data = r->in.data;
+ r2.in.offered = r->in.offered;
+
+ return _spoolss_SetPrinterDataEx(p, &r2);
+}
+
+/****************************************************************
+ _spoolss_ResetPrinter
+****************************************************************/
+
+WERROR _spoolss_ResetPrinter(struct pipes_struct *p,
+ struct spoolss_ResetPrinter *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ int snum;
+
+ DEBUG(5,("_spoolss_ResetPrinter\n"));
+
+ /*
+ * All we do is to check to see if the handle and queue is valid.
+ * This call really doesn't mean anything to us because we only
+ * support RAW printing. --jerry
+ */
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_ResetPrinter: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL))
+ return WERR_INVALID_HANDLE;
+
+
+ /* blindly return success */
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterData
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterData(struct pipes_struct *p,
+ struct spoolss_DeletePrinterData *r)
+{
+ struct spoolss_DeletePrinterDataEx r2;
+
+ r2.in.handle = r->in.handle;
+ r2.in.key_name = "PrinterDriverData";
+ r2.in.value_name = r->in.value_name;
+
+ return _spoolss_DeletePrinterDataEx(p, &r2);
+}
+
+/****************************************************************
+ _spoolss_AddForm
+****************************************************************/
+
+WERROR _spoolss_AddForm(struct pipes_struct *p,
+ struct spoolss_AddForm *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct spoolss_AddFormInfo1 *form;
+ int snum = -1;
+ WERROR status = WERR_OK;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ DEBUG(5,("_spoolss_AddForm\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_AddForm: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ !security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR)) {
+ DEBUG(2,("_spoolss_Addform: denied by insufficient permissions.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.info_ctr->level != 1) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ form = r->in.info_ctr->info.info1;
+ if (!form) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ switch (form->flags) {
+ case SPOOLSS_FORM_USER:
+ case SPOOLSS_FORM_BUILTIN:
+ case SPOOLSS_FORM_PRINTER:
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ status = winreg_printer_addform1(tmp_ctx, b, form);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+ if (Printer->printer_type == SPLHND_PRINTER) {
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ status = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ status = winreg_printer_update_changeid(tmp_ctx, b,
+ lp_const_servicename(snum));
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/****************************************************************
+ _spoolss_DeleteForm
+****************************************************************/
+
+WERROR _spoolss_DeleteForm(struct pipes_struct *p,
+ struct spoolss_DeleteForm *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const char *form_name = r->in.form_name;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ int snum = -1;
+ WERROR status = WERR_OK;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ DEBUG(5,("_spoolss_DeleteForm\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_DeleteForm: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ !security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR)) {
+ DEBUG(2,("_spoolss_DeleteForm: denied by insufficient permissions.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ status = winreg_printer_deleteform1(tmp_ctx, b, form_name);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+ if (Printer->printer_type == SPLHND_PRINTER) {
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ status = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ status = winreg_printer_update_changeid(tmp_ctx, b,
+ lp_const_servicename(snum));
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/****************************************************************
+ _spoolss_SetForm
+****************************************************************/
+
+WERROR _spoolss_SetForm(struct pipes_struct *p,
+ struct spoolss_SetForm *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct spoolss_AddFormInfo1 *form;
+ const char *form_name = r->in.form_name;
+ int snum = -1;
+ WERROR status = WERR_OK;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ DEBUG(5,("_spoolss_SetForm\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_SetForm: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ !security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR)) {
+ DEBUG(2,("_spoolss_SetForm: denied by insufficient permissions.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (r->in.info_ctr->level != 1) {
+ return WERR_INVALID_LEVEL;
+ }
+
+ form = r->in.info_ctr->info.info1;
+ if (!form) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ status = winreg_printer_setform1(tmp_ctx, b,
+ form_name,
+ form);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+ if (Printer->printer_type == SPLHND_PRINTER) {
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ status = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ status = winreg_printer_update_changeid(tmp_ctx, b,
+ lp_const_servicename(snum));
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/****************************************************************************
+ fill_print_processor1
+****************************************************************************/
+
+static WERROR fill_print_processor1(TALLOC_CTX *mem_ctx,
+ struct spoolss_PrintProcessorInfo1 *r,
+ const char *print_processor_name)
+{
+ r->print_processor_name = talloc_strdup(mem_ctx, print_processor_name);
+ W_ERROR_HAVE_NO_MEMORY(r->print_processor_name);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintprocessors level 1.
+****************************************************************************/
+
+static WERROR enumprintprocessors_level_1(TALLOC_CTX *mem_ctx,
+ union spoolss_PrintProcessorInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_PrintProcessorInfo *info;
+ WERROR result;
+
+ info = talloc_array(mem_ctx, union spoolss_PrintProcessorInfo, 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ *count = 1;
+
+ result = fill_print_processor1(info, &info[0].info1, "winprint");
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ out:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(info);
+ *count = 0;
+ return result;
+ }
+
+ *info_p = info;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_EnumPrintProcessors
+****************************************************************/
+
+WERROR _spoolss_EnumPrintProcessors(struct pipes_struct *p,
+ struct spoolss_EnumPrintProcessors *r)
+{
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("_spoolss_EnumPrintProcessors\n"));
+
+ /*
+ * Enumerate the print processors ...
+ *
+ * Just reply with "winprint", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *r->out.count = 0;
+ *r->out.needed = 0;
+ *r->out.info = NULL;
+
+ if (!get_short_archi(r->in.environment)) {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ result = enumprintprocessors_level_1(p->mem_ctx, r->out.info,
+ r->out.count);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ spoolss_EnumPrintProcessors,
+ *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);
+}
+
+/****************************************************************************
+ fill_printprocdatatype1
+****************************************************************************/
+
+static WERROR fill_printprocdatatype1(TALLOC_CTX *mem_ctx,
+ struct spoolss_PrintProcDataTypesInfo1 *r,
+ const char *name_array)
+{
+ r->name_array = talloc_strdup(mem_ctx, name_array);
+ W_ERROR_HAVE_NO_MEMORY(r->name_array);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintprocdatatypes level 1.
+****************************************************************************/
+
+static WERROR enumprintprocdatatypes_level_1(TALLOC_CTX *mem_ctx,
+ union spoolss_PrintProcDataTypesInfo **info_p,
+ uint32_t *count)
+{
+ WERROR result;
+ union spoolss_PrintProcDataTypesInfo *info;
+
+ info = talloc_array(mem_ctx, union spoolss_PrintProcDataTypesInfo, 1);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ *count = 1;
+
+ result = fill_printprocdatatype1(info, &info[0].info1, "RAW");
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ out:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(info);
+ *count = 0;
+ return result;
+ }
+
+ *info_p = info;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_EnumPrintProcessorDataTypes
+****************************************************************/
+
+WERROR _spoolss_EnumPrintProcessorDataTypes(struct pipes_struct *p,
+ struct spoolss_EnumPrintProcessorDataTypes *r)
+{
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("_spoolss_EnumPrintProcessorDataTypes\n"));
+
+ *r->out.count = 0;
+ *r->out.needed = 0;
+ *r->out.info = NULL;
+
+ if (r->in.print_processor_name == NULL ||
+ !strequal(r->in.print_processor_name, "winprint")) {
+ return WERR_UNKNOWN_PRINTPROCESSOR;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ result = enumprintprocdatatypes_level_1(p->mem_ctx, r->out.info,
+ r->out.count);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ spoolss_EnumPrintProcessorDataTypes,
+ *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);
+}
+
+/****************************************************************************
+ fill_monitor_1
+****************************************************************************/
+
+static WERROR fill_monitor_1(TALLOC_CTX *mem_ctx,
+ struct spoolss_MonitorInfo1 *r,
+ const char *monitor_name)
+{
+ r->monitor_name = talloc_strdup(mem_ctx, monitor_name);
+ W_ERROR_HAVE_NO_MEMORY(r->monitor_name);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ fill_monitor_2
+****************************************************************************/
+
+static WERROR fill_monitor_2(TALLOC_CTX *mem_ctx,
+ struct spoolss_MonitorInfo2 *r,
+ const char *monitor_name,
+ const char *environment,
+ const char *dll_name)
+{
+ r->monitor_name = talloc_strdup(mem_ctx, monitor_name);
+ W_ERROR_HAVE_NO_MEMORY(r->monitor_name);
+ r->environment = talloc_strdup(mem_ctx, environment);
+ W_ERROR_HAVE_NO_MEMORY(r->environment);
+ r->dll_name = talloc_strdup(mem_ctx, dll_name);
+ W_ERROR_HAVE_NO_MEMORY(r->dll_name);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintmonitors level 1.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_1(TALLOC_CTX *mem_ctx,
+ union spoolss_MonitorInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_MonitorInfo *info;
+ WERROR result = WERR_OK;
+
+ info = talloc_array(mem_ctx, union spoolss_MonitorInfo, 2);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ *count = 2;
+
+ result = fill_monitor_1(info, &info[0].info1,
+ SPL_LOCAL_PORT);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ result = fill_monitor_1(info, &info[1].info1,
+ SPL_TCPIP_PORT);
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+out:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(info);
+ *count = 0;
+ return result;
+ }
+
+ *info_p = info;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumprintmonitors level 2.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_2(TALLOC_CTX *mem_ctx,
+ union spoolss_MonitorInfo **info_p,
+ uint32_t *count)
+{
+ union spoolss_MonitorInfo *info;
+ WERROR result = WERR_OK;
+ const char *architecture;
+
+ info = talloc_array(mem_ctx, union spoolss_MonitorInfo, 2);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ *count = 2;
+
+ architecture = lp_parm_const_string(GLOBAL_SECTION_SNUM,
+ "spoolss",
+ "architecture",
+ GLOBAL_SPOOLSS_ARCHITECTURE);
+
+ result = fill_monitor_2(info, &info[0].info2,
+ SPL_LOCAL_PORT,
+ architecture,
+ "localmon.dll");
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+ result = fill_monitor_2(info, &info[1].info2,
+ SPL_TCPIP_PORT,
+ architecture,
+ "tcpmon.dll");
+ if (!W_ERROR_IS_OK(result)) {
+ goto out;
+ }
+
+out:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(info);
+ *count = 0;
+ return result;
+ }
+
+ *info_p = info;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_EnumMonitors
+****************************************************************/
+
+WERROR _spoolss_EnumMonitors(struct pipes_struct *p,
+ struct spoolss_EnumMonitors *r)
+{
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("_spoolss_EnumMonitors\n"));
+
+ /*
+ * Enumerate the print monitors ...
+ *
+ * Just reply with "Local Port", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *r->out.count = 0;
+ *r->out.needed = 0;
+ *r->out.info = NULL;
+
+ switch (r->in.level) {
+ case 1:
+ result = enumprintmonitors_level_1(p->mem_ctx, r->out.info,
+ r->out.count);
+ break;
+ case 2:
+ result = enumprintmonitors_level_2(p->mem_ctx, r->out.info,
+ r->out.count);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(p->mem_ctx,
+ 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);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_1(TALLOC_CTX *mem_ctx,
+ const print_queue_struct *queue,
+ int count, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ uint32_t jobid,
+ int sysjob,
+ struct spoolss_JobInfo1 *r)
+{
+ int i = 0;
+ bool found = false;
+
+ for (i=0; i<count; i++) {
+ if (queue[i].sysjob == sysjob) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false) {
+ /* NT treats not found as bad param... yet another bad choice */
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return fill_job_info1(mem_ctx,
+ r,
+ &queue[i],
+ jobid,
+ i,
+ snum,
+ pinfo2);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_2(TALLOC_CTX *mem_ctx,
+ const print_queue_struct *queue,
+ int count, int snum,
+ struct spoolss_PrinterInfo2 *pinfo2,
+ uint32_t jobid,
+ int sysjob,
+ struct spoolss_JobInfo2 *r)
+{
+ int i = 0;
+ bool found = false;
+ struct spoolss_DeviceMode *devmode;
+ WERROR result;
+
+ for (i=0; i<count; i++) {
+ if (queue[i].sysjob == sysjob) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false) {
+ /* NT treats not found as bad param... yet another bad
+ choice */
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * if the print job does not have a DEVMODE associated with it,
+ * just use the one for the printer. A NULL devicemode is not
+ * a failure condition
+ */
+
+ devmode = print_job_devmode(mem_ctx, lp_const_servicename(snum), jobid);
+ if (!devmode) {
+ result = spoolss_create_default_devmode(mem_ctx,
+ pinfo2->printername,
+ &devmode);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3, ("Can't proceed w/o a devmode!\n"));
+ return result;
+ }
+ }
+
+ return fill_job_info2(mem_ctx,
+ r,
+ &queue[i],
+ jobid,
+ i,
+ snum,
+ pinfo2,
+ devmode);
+}
+
+/****************************************************************
+ _spoolss_GetJob
+****************************************************************/
+
+WERROR _spoolss_GetJob(struct pipes_struct *p,
+ struct spoolss_GetJob *r)
+{
+ WERROR result = WERR_OK;
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ const char *svc_name;
+ int sysjob;
+ int snum;
+ int count;
+ struct tdb_print_db *pdb;
+ print_queue_struct *queue = NULL;
+ print_status_struct prt_status;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_jinfo_free;
+ }
+
+ DEBUG(5,("_spoolss_GetJob\n"));
+
+ *r->out.needed = 0;
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ result = WERR_INVALID_HANDLE;
+ goto err_jinfo_free;
+ }
+
+ svc_name = lp_const_servicename(snum);
+ if (svc_name == NULL) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_jinfo_free;
+ }
+
+ result = winreg_get_printer_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ svc_name,
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_jinfo_free;
+ }
+
+ pdb = get_print_db_byname(svc_name);
+ if (pdb == NULL) {
+ DEBUG(3, ("failed to get print db for svc %s\n", svc_name));
+ result = WERR_INVALID_PARAMETER;
+ goto err_pinfo_free;
+ }
+
+ sysjob = jobid_to_sysjob_pdb(pdb, r->in.job_id);
+ release_print_db(pdb);
+ if (sysjob == -1) {
+ DEBUG(3, ("no sysjob for spoolss jobid %u\n", r->in.job_id));
+ result = WERR_INVALID_PARAMETER;
+ goto err_pinfo_free;
+ }
+
+ count = print_queue_status(p->msg_ctx, snum, &queue, &prt_status);
+
+ DEBUGADD(4,("count:[%d], prt_status:[%d], [%s]\n",
+ count, prt_status.status, prt_status.message));
+
+ switch (r->in.level) {
+ case 1:
+ result = getjob_level_1(p->mem_ctx,
+ queue, count, snum, pinfo2,
+ r->in.job_id, sysjob,
+ &r->out.info->info1);
+ break;
+ case 2:
+ result = getjob_level_2(p->mem_ctx,
+ queue, count, snum, pinfo2,
+ r->in.job_id, sysjob,
+ &r->out.info->info2);
+ break;
+ default:
+ result = WERR_INVALID_LEVEL;
+ break;
+ }
+
+ SAFE_FREE(queue);
+ TALLOC_FREE(pinfo2);
+
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_jinfo_free;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_JobInfo, 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);
+
+err_pinfo_free:
+ TALLOC_FREE(pinfo2);
+err_jinfo_free:
+ TALLOC_FREE(r->out.info);
+ return result;
+}
+
+/****************************************************************
+ _spoolss_GetPrinterDataEx
+****************************************************************/
+
+WERROR _spoolss_GetPrinterDataEx(struct pipes_struct *p,
+ struct spoolss_GetPrinterDataEx *r)
+{
+
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ const char *printer;
+ int snum = 0;
+ WERROR result = WERR_OK;
+ DATA_BLOB blob;
+ enum winreg_Type val_type = REG_NONE;
+ uint8_t *val_data = NULL;
+ uint32_t val_size = 0;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ DEBUG(4,("_spoolss_GetPrinterDataEx\n"));
+
+ DEBUG(10, ("_spoolss_GetPrinterDataEx: key => [%s], value => [%s]\n",
+ r->in.key_name, r->in.value_name));
+
+ /* in case of problem, return some default values */
+
+ *r->out.needed = 0;
+ *r->out.type = REG_NONE;
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_GetPrinterDataEx: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+
+ /* Is the handle to a printer or to the server? */
+
+ if (Printer->printer_type == SPLHND_SERVER) {
+
+ union spoolss_PrinterData data;
+
+ result = getprinterdata_printer_server(tmp_ctx,
+ r->in.value_name,
+ r->out.type,
+ &data);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = push_spoolss_PrinterData(tmp_ctx, &blob,
+ *r->out.type, &data);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ *r->out.needed = blob.length;
+
+ if (r->in.offered >= *r->out.needed) {
+ memcpy(r->out.data, blob.data, blob.length);
+ }
+
+ result = WERR_OK;
+ goto done;
+ }
+
+ /* check to see if the keyname is valid */
+ if (!strlen(r->in.key_name)) {
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ result = WERR_INVALID_HANDLE;
+ goto done;
+ }
+ printer = lp_const_servicename(snum);
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* XP sends this and wants the ChangeID value from PRINTER_INFO_0 */
+ if (strequal(r->in.key_name, SPOOL_PRINTERDATA_KEY) &&
+ strequal(r->in.value_name, "ChangeId")) {
+ *r->out.type = REG_DWORD;
+ *r->out.needed = 4;
+ if (r->in.offered >= *r->out.needed) {
+ uint32_t changeid = 0;
+
+ result = winreg_printer_get_changeid(tmp_ctx, b,
+ printer,
+ &changeid);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ SIVAL(r->out.data, 0, changeid);
+ result = WERR_OK;
+ }
+ goto done;
+ }
+
+ result = winreg_get_printer_dataex(tmp_ctx, b,
+ printer,
+ r->in.key_name,
+ r->in.value_name,
+ &val_type,
+ &val_data,
+ &val_size);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ *r->out.needed = val_size;
+ *r->out.type = val_type;
+
+ if (r->in.offered >= *r->out.needed) {
+ memcpy(r->out.data, val_data, val_size);
+ }
+
+done:
+ /* NOTE: do not replace type when returning WERR_MORE_DATA */
+
+ if (W_ERROR_IS_OK(result)) {
+ result = SPOOLSS_BUFFER_OK(WERR_OK, WERR_MORE_DATA);
+ }
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/****************************************************************
+ _spoolss_SetPrinterDataEx
+****************************************************************/
+
+WERROR _spoolss_SetPrinterDataEx(struct pipes_struct *p,
+ struct spoolss_SetPrinterDataEx *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ int snum = 0;
+ WERROR result = WERR_OK;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ char *oid_string;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ DEBUG(4,("_spoolss_SetPrinterDataEx\n"));
+
+ /* From MSDN documentation of SetPrinterDataEx: pass request to
+ SetPrinterData if key is "PrinterDriverData" */
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_SetPrinterDataEx: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (Printer->printer_type == SPLHND_SERVER) {
+ DEBUG(10,("_spoolss_SetPrinterDataEx: "
+ "Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privilege.
+ * we were originally returning OK if nothing changed
+ * which made Win2k issue **a lot** of SetPrinterData
+ * when connecting to a printer --jerry
+ */
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_SetPrinterDataEx: "
+ "change denied by handle access permissions\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ result = winreg_get_printer(tmp_ctx, b,
+ lp_servicename(talloc_tos(), lp_sub, snum),
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* check for OID in valuename */
+
+ oid_string = strchr(r->in.value_name, ',');
+ if (oid_string) {
+ *oid_string = '\0';
+ oid_string++;
+ }
+
+ /* save the registry data */
+
+ result = winreg_set_printer_dataex(tmp_ctx, b,
+ pinfo2->sharename,
+ r->in.key_name,
+ r->in.value_name,
+ r->in.type,
+ r->in.data,
+ r->in.offered);
+
+ if (W_ERROR_IS_OK(result)) {
+ /* save the OID if one was specified */
+ if (oid_string) {
+ char *str = talloc_asprintf(tmp_ctx, "%s\\%s",
+ r->in.key_name, SPOOL_OID_KEY);
+ if (!str) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /*
+ * I'm not checking the status here on purpose. Don't know
+ * if this is right, but I'm returning the status from the
+ * previous set_printer_dataex() call. I have no idea if
+ * this is right. --jerry
+ */
+ winreg_set_printer_dataex(tmp_ctx, b,
+ pinfo2->sharename,
+ str,
+ r->in.value_name,
+ REG_SZ,
+ (uint8_t *) oid_string,
+ strlen(oid_string) + 1);
+ }
+
+ result = winreg_printer_update_changeid(tmp_ctx, b,
+ lp_const_servicename(snum));
+
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterDataEx
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterDataEx(struct pipes_struct *p,
+ struct spoolss_DeletePrinterDataEx *r)
+{
+ const char *printer;
+ int snum=0;
+ WERROR status = WERR_OK;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+
+ DEBUG(5,("_spoolss_DeletePrinterDataEx\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_DeletePrinterDataEx: "
+ "Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_DeletePrinterDataEx: "
+ "printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!r->in.value_name || !r->in.key_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+ printer = lp_const_servicename(snum);
+
+ status = winreg_delete_printer_dataex_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ printer,
+ r->in.key_name,
+ r->in.value_name);
+ if (W_ERROR_IS_OK(status)) {
+ status = winreg_printer_update_changeid_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ printer);
+ }
+
+ return status;
+}
+
+/****************************************************************
+ _spoolss_EnumPrinterKey
+****************************************************************/
+
+WERROR _spoolss_EnumPrinterKey(struct pipes_struct *p,
+ struct spoolss_EnumPrinterKey *r)
+{
+ uint32_t num_keys;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ int snum = 0;
+ WERROR result = WERR_FILE_NOT_FOUND;
+ const char **array = NULL;
+ DATA_BLOB blob;
+
+ DEBUG(4,("_spoolss_EnumPrinterKey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_EnumPrinterKey: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ result = winreg_enum_printer_key_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum),
+ r->in.key_name,
+ &num_keys,
+ &array);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ if (!push_reg_multi_sz(p->mem_ctx, &blob, array)) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ *r->out._ndr_size = r->in.offered / 2;
+ *r->out.needed = blob.length;
+
+ if (r->in.offered < *r->out.needed) {
+ result = WERR_MORE_DATA;
+ } else {
+ result = WERR_OK;
+ r->out.key_buffer->string_array = array;
+ }
+
+ done:
+ if (!W_ERROR_IS_OK(result)) {
+ TALLOC_FREE(array);
+ if (!W_ERROR_EQUAL(result, WERR_MORE_DATA)) {
+ *r->out.needed = 0;
+ }
+ }
+
+ return result;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterKey
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterKey(struct pipes_struct *p,
+ struct spoolss_DeletePrinterKey *r)
+{
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ int snum=0;
+ WERROR status;
+ const char *printer;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ DEBUG(5,("_spoolss_DeletePrinterKey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_DeletePrinterKey: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* if keyname == NULL, return error */
+ if ( !r->in.key_name )
+ return WERR_INVALID_PARAMETER;
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_DeletePrinterKey: "
+ "printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ printer = lp_const_servicename(snum);
+
+ tmp_ctx = talloc_new(p->mem_ctx);
+ if (!tmp_ctx) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = winreg_printer_binding_handle(tmp_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ &b);
+ if (!W_ERROR_IS_OK(status)) {
+ goto done;
+ }
+
+ /* delete the key and all subkeys */
+ status = winreg_delete_printer_key(tmp_ctx, b,
+ printer,
+ r->in.key_name);
+ if (W_ERROR_IS_OK(status)) {
+ status = winreg_printer_update_changeid(tmp_ctx, b,
+ printer);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/****************************************************************
+ _spoolss_EnumPrinterDataEx
+****************************************************************/
+
+WERROR _spoolss_EnumPrinterDataEx(struct pipes_struct *p,
+ struct spoolss_EnumPrinterDataEx *r)
+{
+ uint32_t count = 0;
+ struct spoolss_PrinterEnumValues *info = NULL;
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ int snum;
+ WERROR result;
+
+ DEBUG(4,("_spoolss_EnumPrinterDataEx\n"));
+
+ *r->out.count = 0;
+ *r->out.needed = 0;
+ *r->out.info = NULL;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_EnumPrinterDataEx: Invalid handle (%s:%u:%u1<).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /*
+ * first check for a keyname of NULL or "". Win2k seems to send
+ * this a lot and we should send back WERR_INVALID_PARAMETER
+ * no need to spend time looking up the printer in this case.
+ * --jerry
+ */
+
+ if (!strlen(r->in.key_name)) {
+ result = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (!get_printer_snum(p, r->in.handle, &snum, NULL)) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* now look for a match on the key name */
+ result = winreg_enum_printer_dataex_internal(p->mem_ctx,
+ get_session_info_system(),
+ p->msg_ctx,
+ lp_const_servicename(snum),
+ r->in.key_name,
+ &count,
+ &info);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ *r->out.count = count;
+ *r->out.info = info;
+
+ done:
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_ARRAY(p->mem_ctx,
+ spoolss_EnumPrinterDataEx,
+ *r->out.info,
+ *r->out.count);
+ *r->out.info = SPOOLSS_BUFFER_OK(*r->out.info, NULL);
+ *r->out.count = SPOOLSS_BUFFER_OK(*r->out.count, *r->out.count);
+
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_MORE_DATA);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprintprocessordirectory_level_1(TALLOC_CTX *mem_ctx,
+ const char *servername,
+ const char *environment,
+ struct spoolss_PrintProcessorDirectoryInfo1 *r)
+{
+ WERROR werr;
+ char *path = NULL;
+
+ werr = compose_spoolss_server_path(mem_ctx,
+ servername,
+ environment,
+ SPOOLSS_PRTPROCS_PATH,
+ &path);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ DEBUG(4,("print processor directory: [%s]\n", path));
+
+ r->directory_name = path;
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_GetPrintProcessorDirectory
+****************************************************************/
+
+WERROR _spoolss_GetPrintProcessorDirectory(struct pipes_struct *p,
+ struct spoolss_GetPrintProcessorDirectory *r)
+{
+ WERROR result;
+ char *prnproc_share = NULL;
+ bool prnproc_share_exists = false;
+ int snum;
+
+ /* that's an [in out] buffer */
+
+ if (!r->in.buffer && (r->in.offered != 0)) {
+ result = WERR_INVALID_PARAMETER;
+ goto err_info_free;
+ }
+
+ DEBUG(5,("_spoolss_GetPrintProcessorDirectory: level %d\n",
+ r->in.level));
+
+ *r->out.needed = 0;
+
+ /* r->in.level is ignored */
+
+ /* We always should reply with a local print processor directory so that
+ * users are not forced to have a [prnproc$] share on the Samba spoolss
+ * server, if users decide to do so, lets announce it though - Guenther */
+
+ snum = find_service(talloc_tos(), "prnproc$", &prnproc_share);
+ if (!prnproc_share) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto err_info_free;
+ }
+ if (snum != -1) {
+ prnproc_share_exists = true;
+ }
+
+ result = getprintprocessordirectory_level_1(p->mem_ctx,
+ prnproc_share_exists ? r->in.server : NULL,
+ r->in.environment,
+ &r->out.info->info1);
+ if (!W_ERROR_IS_OK(result)) {
+ goto err_info_free;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_PrintProcessorDirectoryInfo,
+ 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);
+
+err_info_free:
+ TALLOC_FREE(r->out.info);
+ return result;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool push_monitorui_buf(TALLOC_CTX *mem_ctx, DATA_BLOB *buf,
+ const char *dllname)
+{
+ enum ndr_err_code ndr_err;
+ struct spoolss_MonitorUi ui;
+
+ ui.dll_name = dllname;
+
+ ndr_err = ndr_push_struct_blob(buf, mem_ctx, &ui,
+ (ndr_push_flags_fn_t)ndr_push_spoolss_MonitorUi);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err) && (DEBUGLEVEL >= 10)) {
+ NDR_PRINT_DEBUG(spoolss_MonitorUi, &ui);
+ }
+ return NDR_ERR_CODE_IS_SUCCESS(ndr_err);
+}
+
+/*******************************************************************
+ Streams the monitor UI DLL name in UNICODE
+*******************************************************************/
+
+static WERROR xcvtcp_monitorui(TALLOC_CTX *mem_ctx,
+ struct security_token *token, DATA_BLOB *in,
+ DATA_BLOB *out, uint32_t *needed)
+{
+ const char *dllname = "tcpmonui.dll";
+
+ *needed = (strlen(dllname)+1) * 2;
+
+ if (out->length < *needed) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if (!push_monitorui_buf(mem_ctx, out, dllname)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool pull_port_data_1(TALLOC_CTX *mem_ctx,
+ struct spoolss_PortData1 *port1,
+ const DATA_BLOB *buf)
+{
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(buf, mem_ctx, port1,
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_PortData1);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err) && (DEBUGLEVEL >= 10)) {
+ NDR_PRINT_DEBUG(spoolss_PortData1, port1);
+ }
+ return NDR_ERR_CODE_IS_SUCCESS(ndr_err);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool pull_port_data_2(TALLOC_CTX *mem_ctx,
+ struct spoolss_PortData2 *port2,
+ const DATA_BLOB *buf)
+{
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(buf, mem_ctx, port2,
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_PortData2);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err) && (DEBUGLEVEL >= 10)) {
+ NDR_PRINT_DEBUG(spoolss_PortData2, port2);
+ }
+ return NDR_ERR_CODE_IS_SUCCESS(ndr_err);
+}
+
+/*******************************************************************
+ Create a new TCP/IP port
+*******************************************************************/
+
+static WERROR xcvtcp_addport(TALLOC_CTX *mem_ctx,
+ struct security_token *token, DATA_BLOB *in,
+ DATA_BLOB *out, uint32_t *needed)
+{
+ struct spoolss_PortData1 port1;
+ struct spoolss_PortData2 port2;
+ char *device_uri = NULL;
+ uint32_t version;
+
+ const char *portname;
+ const char *hostaddress;
+ const char *queue;
+ uint32_t port_number;
+ uint32_t protocol;
+
+ /* peek for spoolss_PortData version */
+
+ if (!in || (in->length < (128 + 4))) {
+ return WERR_GEN_FAILURE;
+ }
+
+ version = IVAL(in->data, 128);
+
+ switch (version) {
+ case 1:
+ ZERO_STRUCT(port1);
+
+ if (!pull_port_data_1(mem_ctx, &port1, in)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ portname = port1.portname;
+ hostaddress = port1.hostaddress;
+ queue = port1.queue;
+ protocol = port1.protocol;
+ port_number = port1.port_number;
+
+ break;
+ case 2:
+ ZERO_STRUCT(port2);
+
+ if (!pull_port_data_2(mem_ctx, &port2, in)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ portname = port2.portname;
+ hostaddress = port2.hostaddress;
+ queue = port2.queue;
+ protocol = port2.protocol;
+ port_number = port2.port_number;
+
+ break;
+ default:
+ DEBUG(1,("xcvtcp_addport: "
+ "unknown version of port_data: %d\n", version));
+ return WERR_UNKNOWN_PORT;
+ }
+
+ /* create the device URI and call the add_port_hook() */
+
+ switch (protocol) {
+ case PROTOCOL_RAWTCP_TYPE:
+ device_uri = talloc_asprintf(mem_ctx,
+ "socket://%s:%d/", hostaddress,
+ port_number);
+ break;
+
+ case PROTOCOL_LPR_TYPE:
+ device_uri = talloc_asprintf(mem_ctx,
+ "lpr://%s/%s", hostaddress, queue );
+ break;
+
+ default:
+ return WERR_UNKNOWN_PORT;
+ }
+
+ if (!device_uri) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return add_port_hook(mem_ctx, token, portname, device_uri);
+}
+
+/*******************************************************************
+*******************************************************************/
+
+struct xcv_api_table xcvtcp_cmds[] = {
+ { "MonitorUI", xcvtcp_monitorui },
+ { "AddPort", xcvtcp_addport},
+ { NULL, NULL }
+};
+
+static WERROR process_xcvtcp_command(TALLOC_CTX *mem_ctx,
+ struct security_token *token, const char *command,
+ DATA_BLOB *inbuf,
+ DATA_BLOB *outbuf,
+ uint32_t *needed )
+{
+ int i;
+
+ DEBUG(10,("process_xcvtcp_command: Received command \"%s\"\n", command));
+
+ for ( i=0; xcvtcp_cmds[i].name; i++ ) {
+ if ( strcmp( command, xcvtcp_cmds[i].name ) == 0 )
+ return xcvtcp_cmds[i].fn(mem_ctx, token, inbuf, outbuf, needed);
+ }
+
+ return WERR_INVALID_FUNCTION;
+}
+
+/*******************************************************************
+*******************************************************************/
+#if 0 /* don't support management using the "Local Port" monitor */
+
+static WERROR xcvlocal_monitorui(TALLOC_CTX *mem_ctx,
+ struct security_token *token, DATA_BLOB *in,
+ DATA_BLOB *out, uint32_t *needed)
+{
+ const char *dllname = "localui.dll";
+
+ *needed = (strlen(dllname)+1) * 2;
+
+ if (out->length < *needed) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if (!push_monitorui_buf(mem_ctx, out, dllname)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+struct xcv_api_table xcvlocal_cmds[] = {
+ { "MonitorUI", xcvlocal_monitorui },
+ { NULL, NULL }
+};
+#else
+struct xcv_api_table xcvlocal_cmds[] = {
+ { NULL, NULL }
+};
+#endif
+
+
+
+/*******************************************************************
+*******************************************************************/
+
+static WERROR process_xcvlocal_command(TALLOC_CTX *mem_ctx,
+ struct security_token *token, const char *command,
+ DATA_BLOB *inbuf, DATA_BLOB *outbuf,
+ uint32_t *needed)
+{
+ int i;
+
+ DEBUG(10,("process_xcvlocal_command: Received command \"%s\"\n", command));
+
+ for ( i=0; xcvlocal_cmds[i].name; i++ ) {
+ if ( strcmp( command, xcvlocal_cmds[i].name ) == 0 )
+ return xcvlocal_cmds[i].fn(mem_ctx, token, inbuf, outbuf, needed);
+ }
+ return WERR_INVALID_FUNCTION;
+}
+
+/****************************************************************
+ _spoolss_XcvData
+****************************************************************/
+
+WERROR _spoolss_XcvData(struct pipes_struct *p,
+ struct spoolss_XcvData *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct printer_handle *Printer = find_printer_index_by_hnd(p, r->in.handle);
+ DATA_BLOB out_data = data_blob_null;
+ WERROR werror;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_XcvData: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(r->in.handle)));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* Has to be a handle to the TCP/IP port monitor */
+
+ if ( !(Printer->printer_type & (SPLHND_PORTMON_LOCAL|SPLHND_PORTMON_TCP)) ) {
+ DEBUG(2,("_spoolss_XcvData: Call only valid for Port Monitors\n"));
+ return WERR_INVALID_HANDLE;
+ }
+
+ /* requires administrative access to the server */
+
+ if ( !(Printer->access_granted & SERVER_ACCESS_ADMINISTER) ) {
+ DEBUG(2,("_spoolss_XcvData: denied by handle permissions.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Allocate the outgoing buffer */
+
+ if (r->in.out_data_size) {
+ out_data = data_blob_talloc_zero(p->mem_ctx, r->in.out_data_size);
+ if (out_data.data == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ switch ( Printer->printer_type ) {
+ case SPLHND_PORTMON_TCP:
+ werror = process_xcvtcp_command(p->mem_ctx,
+ session_info->security_token,
+ r->in.function_name,
+ &r->in.in_data, &out_data,
+ r->out.needed);
+ break;
+ case SPLHND_PORTMON_LOCAL:
+ werror = process_xcvlocal_command(p->mem_ctx,
+ session_info->security_token,
+ r->in.function_name,
+ &r->in.in_data, &out_data,
+ r->out.needed);
+ break;
+ default:
+ werror = WERR_INVALID_PRINT_MONITOR;
+ }
+
+ if (!W_ERROR_IS_OK(werror)) {
+ return werror;
+ }
+
+ *r->out.status_code = 0;
+
+ if (r->out.out_data && out_data.data && r->in.out_data_size && out_data.length) {
+ memcpy(r->out.out_data, out_data.data,
+ MIN(r->in.out_data_size, out_data.length));
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_AddPrintProcessor
+****************************************************************/
+
+WERROR _spoolss_AddPrintProcessor(struct pipes_struct *p,
+ struct spoolss_AddPrintProcessor *r)
+{
+ /* for now, just indicate success and ignore the add. We'll
+ automatically set the winprint processor for printer
+ entries later. Used to debug the LexMark Optra S 1855 PCL
+ driver --jerry */
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _spoolss_AddPort
+****************************************************************/
+
+WERROR _spoolss_AddPort(struct pipes_struct *p,
+ struct spoolss_AddPort *r)
+{
+ /* do what w2k3 does */
+
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_GetPrinterDriver
+****************************************************************/
+
+WERROR _spoolss_GetPrinterDriver(struct pipes_struct *p,
+ struct spoolss_GetPrinterDriver *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_ReadPrinter
+****************************************************************/
+
+WERROR _spoolss_ReadPrinter(struct pipes_struct *p,
+ struct spoolss_ReadPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_WaitForPrinterChange
+****************************************************************/
+
+WERROR _spoolss_WaitForPrinterChange(struct pipes_struct *p,
+ struct spoolss_WaitForPrinterChange *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_ConfigurePort
+****************************************************************/
+
+WERROR _spoolss_ConfigurePort(struct pipes_struct *p,
+ struct spoolss_ConfigurePort *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeletePort
+****************************************************************/
+
+WERROR _spoolss_DeletePort(struct pipes_struct *p,
+ struct spoolss_DeletePort *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_CreatePrinterIC
+****************************************************************/
+
+WERROR _spoolss_CreatePrinterIC(struct pipes_struct *p,
+ struct spoolss_CreatePrinterIC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_PlayGDIScriptOnPrinterIC
+****************************************************************/
+
+WERROR _spoolss_PlayGDIScriptOnPrinterIC(struct pipes_struct *p,
+ struct spoolss_PlayGDIScriptOnPrinterIC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterIC
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterIC(struct pipes_struct *p,
+ struct spoolss_DeletePrinterIC *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_AddPrinterConnection
+****************************************************************/
+
+WERROR _spoolss_AddPrinterConnection(struct pipes_struct *p,
+ struct spoolss_AddPrinterConnection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeletePrinterConnection
+****************************************************************/
+
+WERROR _spoolss_DeletePrinterConnection(struct pipes_struct *p,
+ struct spoolss_DeletePrinterConnection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_PrinterMessageBox
+****************************************************************/
+
+WERROR _spoolss_PrinterMessageBox(struct pipes_struct *p,
+ struct spoolss_PrinterMessageBox *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_AddMonitor
+****************************************************************/
+
+WERROR _spoolss_AddMonitor(struct pipes_struct *p,
+ struct spoolss_AddMonitor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeleteMonitor
+****************************************************************/
+
+WERROR _spoolss_DeleteMonitor(struct pipes_struct *p,
+ struct spoolss_DeleteMonitor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeletePrintProcessor
+****************************************************************/
+
+WERROR _spoolss_DeletePrintProcessor(struct pipes_struct *p,
+ struct spoolss_DeletePrintProcessor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_AddPrintProvidor
+****************************************************************/
+
+WERROR _spoolss_AddPrintProvidor(struct pipes_struct *p,
+ struct spoolss_AddPrintProvidor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeletePrintProvidor
+****************************************************************/
+
+WERROR _spoolss_DeletePrintProvidor(struct pipes_struct *p,
+ struct spoolss_DeletePrintProvidor *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_FindFirstPrinterChangeNotification
+****************************************************************/
+
+WERROR _spoolss_FindFirstPrinterChangeNotification(struct pipes_struct *p,
+ struct spoolss_FindFirstPrinterChangeNotification *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_FindNextPrinterChangeNotification
+****************************************************************/
+
+WERROR _spoolss_FindNextPrinterChangeNotification(struct pipes_struct *p,
+ struct spoolss_FindNextPrinterChangeNotification *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_RouterFindFirstPrinterChangeNotificationOld
+****************************************************************/
+
+WERROR _spoolss_RouterFindFirstPrinterChangeNotificationOld(struct pipes_struct *p,
+ struct spoolss_RouterFindFirstPrinterChangeNotificationOld *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_ReplyOpenPrinter
+****************************************************************/
+
+WERROR _spoolss_ReplyOpenPrinter(struct pipes_struct *p,
+ struct spoolss_ReplyOpenPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_RouterReplyPrinter
+****************************************************************/
+
+WERROR _spoolss_RouterReplyPrinter(struct pipes_struct *p,
+ struct spoolss_RouterReplyPrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_ReplyClosePrinter
+****************************************************************/
+
+WERROR _spoolss_ReplyClosePrinter(struct pipes_struct *p,
+ struct spoolss_ReplyClosePrinter *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_AddPortEx
+****************************************************************/
+
+WERROR _spoolss_AddPortEx(struct pipes_struct *p,
+ struct spoolss_AddPortEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_RouterFindFirstPrinterChangeNotification
+****************************************************************/
+
+WERROR _spoolss_RouterFindFirstPrinterChangeNotification(struct pipes_struct *p,
+ struct spoolss_RouterFindFirstPrinterChangeNotification *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_SpoolerInit
+****************************************************************/
+
+WERROR _spoolss_SpoolerInit(struct pipes_struct *p,
+ struct spoolss_SpoolerInit *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_ResetPrinterEx
+****************************************************************/
+
+WERROR _spoolss_ResetPrinterEx(struct pipes_struct *p,
+ struct spoolss_ResetPrinterEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_RouterReplyPrinterEx
+****************************************************************/
+
+WERROR _spoolss_RouterReplyPrinterEx(struct pipes_struct *p,
+ struct spoolss_RouterReplyPrinterEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_44
+****************************************************************/
+
+WERROR _spoolss_44(struct pipes_struct *p,
+ struct spoolss_44 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_SetPort
+****************************************************************/
+
+WERROR _spoolss_SetPort(struct pipes_struct *p,
+ struct spoolss_SetPort *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_4a
+****************************************************************/
+
+WERROR _spoolss_4a(struct pipes_struct *p,
+ struct spoolss_4a *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_4b
+****************************************************************/
+
+WERROR _spoolss_4b(struct pipes_struct *p,
+ struct spoolss_4b *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_4c
+****************************************************************/
+
+WERROR _spoolss_4c(struct pipes_struct *p,
+ struct spoolss_4c *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_53
+****************************************************************/
+
+WERROR _spoolss_53(struct pipes_struct *p,
+ struct spoolss_53 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_AddPerMachineConnection
+****************************************************************/
+
+WERROR _spoolss_AddPerMachineConnection(struct pipes_struct *p,
+ struct spoolss_AddPerMachineConnection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeletePerMachineConnection
+****************************************************************/
+
+WERROR _spoolss_DeletePerMachineConnection(struct pipes_struct *p,
+ struct spoolss_DeletePerMachineConnection *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_EnumPerMachineConnections
+****************************************************************/
+
+WERROR _spoolss_EnumPerMachineConnections(struct pipes_struct *p,
+ struct spoolss_EnumPerMachineConnections *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_5a
+****************************************************************/
+
+WERROR _spoolss_5a(struct pipes_struct *p,
+ struct spoolss_5a *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_5b
+****************************************************************/
+
+WERROR _spoolss_5b(struct pipes_struct *p,
+ struct spoolss_5b *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_5c
+****************************************************************/
+
+WERROR _spoolss_5c(struct pipes_struct *p,
+ struct spoolss_5c *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_5d
+****************************************************************/
+
+WERROR _spoolss_5d(struct pipes_struct *p,
+ struct spoolss_5d *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_5e
+****************************************************************/
+
+WERROR _spoolss_5e(struct pipes_struct *p,
+ struct spoolss_5e *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_5f
+****************************************************************/
+
+WERROR _spoolss_5f(struct pipes_struct *p,
+ struct spoolss_5f *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_60
+****************************************************************/
+
+WERROR _spoolss_60(struct pipes_struct *p,
+ struct spoolss_60 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_SendRecvBidiData
+****************************************************************/
+
+WERROR _spoolss_SendRecvBidiData(struct pipes_struct *p,
+ struct spoolss_SendRecvBidiData *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_62
+****************************************************************/
+
+WERROR _spoolss_62(struct pipes_struct *p,
+ struct spoolss_62 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_63
+****************************************************************/
+
+WERROR _spoolss_63(struct pipes_struct *p,
+ struct spoolss_63 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_64
+****************************************************************/
+
+WERROR _spoolss_64(struct pipes_struct *p,
+ struct spoolss_64 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_65
+****************************************************************/
+
+WERROR _spoolss_65(struct pipes_struct *p,
+ struct spoolss_65 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_GetCorePrinterDrivers
+****************************************************************/
+
+HRESULT _spoolss_GetCorePrinterDrivers(struct pipes_struct *p,
+ struct spoolss_GetCorePrinterDrivers *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_67
+****************************************************************/
+
+WERROR _spoolss_67(struct pipes_struct *p,
+ struct spoolss_67 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_GetPrinterDriverPackagePath
+****************************************************************/
+
+HRESULT _spoolss_GetPrinterDriverPackagePath(struct pipes_struct *p,
+ struct spoolss_GetPrinterDriverPackagePath *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return HRES_ERROR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_69
+****************************************************************/
+
+WERROR _spoolss_69(struct pipes_struct *p,
+ struct spoolss_69 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_6a
+****************************************************************/
+
+WERROR _spoolss_6a(struct pipes_struct *p,
+ struct spoolss_6a *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_6b
+****************************************************************/
+
+WERROR _spoolss_6b(struct pipes_struct *p,
+ struct spoolss_6b *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_6c
+****************************************************************/
+
+WERROR _spoolss_6c(struct pipes_struct *p,
+ struct spoolss_6c *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_6d
+****************************************************************/
+
+WERROR _spoolss_6d(struct pipes_struct *p,
+ struct spoolss_6d *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_GetJobNamedPropertyValue
+****************************************************************/
+
+WERROR _spoolss_GetJobNamedPropertyValue(struct pipes_struct *p,
+ struct spoolss_GetJobNamedPropertyValue *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_SetJobNamedProperty
+****************************************************************/
+
+WERROR _spoolss_SetJobNamedProperty(struct pipes_struct *p,
+ struct spoolss_SetJobNamedProperty *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_DeleteJobNamedProperty
+****************************************************************/
+
+WERROR _spoolss_DeleteJobNamedProperty(struct pipes_struct *p,
+ struct spoolss_DeleteJobNamedProperty *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_EnumJobNamedProperties
+****************************************************************/
+
+WERROR _spoolss_EnumJobNamedProperties(struct pipes_struct *p,
+ struct spoolss_EnumJobNamedProperties *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_72
+****************************************************************/
+
+WERROR _spoolss_72(struct pipes_struct *p,
+ struct spoolss_72 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_73
+****************************************************************/
+
+WERROR _spoolss_73(struct pipes_struct *p,
+ struct spoolss_73 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _spoolss_RpcLogJobInfoForBranchOffice
+****************************************************************/
+
+WERROR _spoolss_LogJobInfoForBranchOffice(struct pipes_struct *p,
+ struct spoolss_LogJobInfoForBranchOffice *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+static NTSTATUS spoolss__op_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+static NTSTATUS spoolss__op_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+#define DCESRV_INTERFACE_SPOOLSS_INIT_SERVER \
+ spoolss_init_server
+
+#define DCESRV_INTERFACE_SPOOLSS_SHUTDOWN_SERVER \
+ spoolss_shutdown_server
+
+static NTSTATUS spoolss_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ bool ok;
+
+ /*
+ * Migrate the printers first.
+ */
+ ok = nt_printing_tdb_migrate(msg_ctx);
+ if (!ok) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return spoolss__op_init_server(dce_ctx, ep_server);
+}
+
+static NTSTATUS spoolss_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ srv_spoolss_cleanup();
+
+ return spoolss__op_shutdown_server(dce_ctx, ep_server);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_spoolss_scompat.c"
diff --git a/source3/rpc_server/spoolss/srv_spoolss_nt.h b/source3/rpc_server/spoolss/srv_spoolss_nt.h
new file mode 100644
index 0000000..d6d141a
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_spoolss_nt.h
@@ -0,0 +1,40 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000,
+ * Copyright (C) Jeremy Allison 2001-2002,
+ * Copyright (C) Gerald Carter 2000-2004,
+ * Copyright (C) Tim Potter 2001-2002.
+ * Copyright (C) Guenther Deschner 2009-2010.
+ * Copyright (C) Andreas Schneider 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_SERVER_SPOOLSS_SRV_SPOOLSS_NT_H_
+#define _RPC_SERVER_SPOOLSS_SRV_SPOOLSS_NT_H_
+
+/* The following definitions come from rpc_server/srv_spoolss_nt.c */
+void srv_spoolss_cleanup(void);
+
+void do_drv_upgrade_printer(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void update_monitored_printq_cache(struct messaging_context *msg_ctx);
+
+#endif /* _RPC_SERVER_SPOOLSS_SRV_SPOOLSS_NT_H_ */
diff --git a/source3/rpc_server/spoolss/srv_spoolss_util.c b/source3/rpc_server/spoolss/srv_spoolss_util.c
new file mode 100644
index 0000000..be3c8fc
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_spoolss_util.c
@@ -0,0 +1,917 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SPOOLSS RPC Pipe server / winreg client routines
+ *
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../librpc/gen_ndr/ndr_spoolss.h"
+#include "../librpc/gen_ndr/ndr_winreg.h"
+#include "srv_spoolss_util.h"
+#include "rpc_client/cli_winreg_spoolss.h"
+
+WERROR winreg_printer_binding_handle(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **winreg_binding_handle)
+{
+ struct tsocket_address *local;
+ NTSTATUS status;
+ int rc;
+
+ rc = tsocket_address_inet_from_strings(mem_ctx,
+ "ip",
+ "127.0.0.1",
+ 0,
+ &local);
+ if (rc < 0) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = rpcint_binding_handle(mem_ctx,
+ &ndr_table_winreg,
+ local,
+ NULL,
+ session_info,
+ msg_ctx,
+ winreg_binding_handle);
+ talloc_free(local);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("winreg_printer_binding_handle: Could not connect to winreg pipe: %s\n",
+ nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return WERR_OK;
+}
+
+WERROR winreg_delete_printer_key_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_delete_printer_key(tmp_ctx,
+ b,
+ printer,
+ key);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_update_changeid_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_update_changeid(mem_ctx,
+ b,
+ printer);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_get_changeid_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ uint32_t *pchangeid)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_get_changeid(mem_ctx,
+ b,
+ printer,
+ pchangeid);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_printer_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ struct spoolss_PrinterInfo2 **pinfo2)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_printer(mem_ctx,
+ b,
+ printer,
+ pinfo2);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_create_printer_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_create_printer(mem_ctx,
+ b,
+ sharename);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_update_printer_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename,
+ uint32_t info2_mask,
+ struct spoolss_SetPrinterInfo2 *info2,
+ struct spoolss_DeviceMode *devmode,
+ struct security_descriptor *secdesc)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_update_printer(mem_ctx,
+ b,
+ sharename,
+ info2_mask,
+ info2,
+ devmode,
+ secdesc);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_set_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t data_size)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_set_printer_dataex(mem_ctx,
+ b,
+ printer,
+ key,
+ value,
+ type,
+ data,
+ data_size);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_enum_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_values,
+ struct spoolss_PrinterEnumValues **penum_values)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_enum_printer_dataex(mem_ctx,
+ b,
+ printer,
+ key,
+ pnum_values,
+ penum_values);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type *type,
+ uint8_t **data,
+ uint32_t *data_size)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_printer_dataex(mem_ctx,
+ b,
+ printer,
+ key,
+ value,
+ type,
+ data,
+ data_size);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_delete_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ const char *value)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_delete_printer_dataex(mem_ctx,
+ b,
+ printer,
+ key,
+ value);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ const char *driver_name,
+ uint32_t driver_version,
+ struct spoolss_DriverInfo8 **_info8)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_driver(mem_ctx,
+ b,
+ architecture,
+ driver_name,
+ driver_version,
+ _info8);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_driver_list_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ uint32_t version,
+ uint32_t *num_drivers,
+ const char ***drivers_p)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_driver_list(mem_ctx,
+ b,
+ architecture,
+ version,
+ num_drivers,
+ drivers_p);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_del_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_DriverInfo8 *info8,
+ uint32_t version)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_del_driver(mem_ctx,
+ b,
+ info8,
+ version);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_add_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_AddDriverInfoCtr *r,
+ const char **driver_name,
+ uint32_t *driver_version)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_add_driver(mem_ctx,
+ b,
+ r,
+ driver_name,
+ driver_version);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_core_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ const struct GUID *core_driver_guid,
+ struct spoolss_CorePrinterDriver **core_printer_driver)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_core_driver(mem_ctx,
+ b,
+ architecture,
+ core_driver_guid,
+ core_printer_driver);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_add_core_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ const struct spoolss_CorePrinterDriver *core_printer_driver)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_add_core_driver(mem_ctx,
+ b,
+ architecture,
+ core_printer_driver);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_add_driver_package_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *package_id,
+ const char *architecture,
+ const char *driver_store_path,
+ const char *cab_path)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_add_driver_package(mem_ctx,
+ b,
+ package_id,
+ architecture,
+ driver_store_path,
+ cab_path);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_driver_package_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *package_id,
+ const char *architecture,
+ const char **driver_store_path,
+ const char **cab_path)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_driver_package(mem_ctx,
+ b,
+ package_id,
+ architecture,
+ driver_store_path,
+ cab_path);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_del_driver_package_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *package_id,
+ const char *architecture)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_del_driver_package(mem_ctx,
+ b,
+ package_id,
+ architecture);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_get_printer_secdesc_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename,
+ struct spoolss_security_descriptor **psecdesc)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_get_printer_secdesc(mem_ctx,
+ b,
+ sharename,
+ psecdesc);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_set_printer_secdesc_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename,
+ const struct spoolss_security_descriptor *secdesc)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_set_printer_secdesc(mem_ctx,
+ b,
+ sharename,
+ secdesc);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_enumforms1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t *pnum_info,
+ union spoolss_FormInfo **pinfo)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_enumforms1(mem_ctx,
+ b,
+ pnum_info,
+ pinfo);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_getform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *form_name,
+ struct spoolss_FormInfo1 *r)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_getform1(mem_ctx,
+ b,
+ form_name,
+ r);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_addform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_AddFormInfo1 *form)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_addform1(mem_ctx,
+ b,
+ form);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_setform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *form_name,
+ struct spoolss_AddFormInfo1 *form)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_setform1(mem_ctx,
+ b,
+ form_name,
+ form);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_printer_deleteform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *form_name)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_printer_deleteform1(mem_ctx,
+ b,
+ form_name);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+WERROR winreg_enum_printer_key_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_subkeys,
+ const char ***psubkeys)
+{
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_printer_binding_handle(tmp_ctx, session_info, msg_ctx, &b);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(tmp_ctx);
+ return result;
+ }
+
+ result = winreg_enum_printer_key(mem_ctx,
+ b,
+ printer,
+ key,
+ pnum_subkeys,
+ psubkeys);
+
+ talloc_free(tmp_ctx);
+ return result;
+}
diff --git a/source3/rpc_server/spoolss/srv_spoolss_util.h b/source3/rpc_server/spoolss/srv_spoolss_util.h
new file mode 100644
index 0000000..a9b3072
--- /dev/null
+++ b/source3/rpc_server/spoolss/srv_spoolss_util.h
@@ -0,0 +1,190 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SPOOLSS RPC Pipe server / winreg client routines
+ *
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _SRV_SPOOLSS_UITL_H
+#define _SRV_SPOOLSS_UITL_H
+
+struct auth_session_info;
+struct dcerpc_binding_handle;
+
+WERROR winreg_printer_binding_handle(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle **winreg_binding_handle);
+
+WERROR winreg_delete_printer_key_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key);
+WERROR winreg_printer_update_changeid_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer);
+WERROR winreg_printer_get_changeid_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ uint32_t *pchangeid);
+WERROR winreg_get_printer_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ struct spoolss_PrinterInfo2 **pinfo2);
+WERROR winreg_create_printer_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename);
+WERROR winreg_update_printer_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename,
+ uint32_t info2_mask,
+ struct spoolss_SetPrinterInfo2 *info2,
+ struct spoolss_DeviceMode *devmode,
+ struct security_descriptor *secdesc);
+WERROR winreg_set_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t data_size);
+WERROR winreg_enum_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_values,
+ struct spoolss_PrinterEnumValues **penum_values);
+WERROR winreg_get_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ const char *value,
+ enum winreg_Type *type,
+ uint8_t **data,
+ uint32_t *data_size);
+WERROR winreg_delete_printer_dataex_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ const char *value);
+WERROR winreg_get_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ const char *driver_name,
+ uint32_t driver_version,
+ struct spoolss_DriverInfo8 **_info8);
+WERROR winreg_get_driver_list_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ uint32_t version,
+ uint32_t *num_drivers,
+ const char ***drivers_p);
+WERROR winreg_del_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_DriverInfo8 *info8,
+ uint32_t version);
+WERROR winreg_add_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_AddDriverInfoCtr *r,
+ const char **driver_name,
+ uint32_t *driver_version);
+WERROR winreg_get_printer_secdesc_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename,
+ struct spoolss_security_descriptor **psecdesc);
+WERROR winreg_set_printer_secdesc_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *sharename,
+ const struct spoolss_security_descriptor *secdesc);
+WERROR winreg_printer_enumforms1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ uint32_t *pnum_info,
+ union spoolss_FormInfo **pinfo);
+WERROR winreg_printer_getform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *form_name,
+ struct spoolss_FormInfo1 *r);
+WERROR winreg_printer_addform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ struct spoolss_AddFormInfo1 *form);
+WERROR winreg_printer_setform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *form_name,
+ struct spoolss_AddFormInfo1 *form);
+WERROR winreg_printer_deleteform1_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *form_name);
+WERROR winreg_enum_printer_key_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer,
+ const char *key,
+ uint32_t *pnum_subkeys,
+ const char ***psubkeys);
+WERROR winreg_get_core_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ const struct GUID *core_driver_guid,
+ struct spoolss_CorePrinterDriver **core_printer_driver);
+WERROR winreg_add_core_driver_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *architecture,
+ const struct spoolss_CorePrinterDriver *core_printer_driver);
+WERROR winreg_add_driver_package_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *package_id,
+ const char *architecture,
+ const char *driver_store_path,
+ const char *cab_path);
+WERROR winreg_get_driver_package_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *package_id,
+ const char *architecture,
+ const char **driver_store_path,
+ const char **cab_path);
+WERROR winreg_del_driver_package_internal(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *package_id,
+ const char *architecture);
+#endif /* _SRV_SPOOLSS_UITL_H */
diff --git a/source3/rpc_server/srv_access_check.c b/source3/rpc_server/srv_access_check.c
new file mode 100644
index 0000000..23d9252
--- /dev/null
+++ b/source3/rpc_server/srv_access_check.c
@@ -0,0 +1,168 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jeremy Allison 2001-2008,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002,
+ * Copyright (C) Gerald (Jerry) Carter 2003-2004,
+ * Copyright (C) Simo Sorce 2003.
+ * Copyright (C) Volker Lendecke 2005.
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/passwd.h" /* uid_wrapper */
+#include "rpc_server/srv_access_check.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ Checks if access to an object should be granted, and returns that
+ level of access for further checks.
+
+ If the user has either of needed_priv_1 or needed_priv_2 then they
+ get the rights in rights_mask in addition to any calculated rights.
+
+ This handles the unusual case where we need to allow two different
+ privileges to obtain exactly the same rights, which occurs only in
+ SAMR.
+********************************************************************/
+
+NTSTATUS access_check_object( struct security_descriptor *psd, struct security_token *token,
+ enum sec_privilege needed_priv_1, enum sec_privilege needed_priv_2,
+ uint32_t rights_mask,
+ uint32_t des_access, uint32_t *acc_granted,
+ const char *debug )
+{
+ NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+ uint32_t saved_mask = 0;
+ bool priv_granted = false;
+ bool is_system = false;
+ bool is_root = false;
+
+ /* Check if we are are the system token */
+ if (security_token_is_system(token) &&
+ security_token_system_privilege(token)) {
+ is_system = true;
+ }
+
+ /* Check if we are root */
+ if (root_mode()) {
+ is_root = true;
+ }
+
+ /* check privileges; certain SAM access bits should be overridden
+ by privileges (mostly having to do with creating/modifying/deleting
+ users and groups) */
+
+ if ((needed_priv_1 != SEC_PRIV_INVALID && security_token_has_privilege(token, needed_priv_1)) ||
+ (needed_priv_2 != SEC_PRIV_INVALID && security_token_has_privilege(token, needed_priv_2))) {
+ priv_granted = true;
+ saved_mask = (des_access & rights_mask);
+ des_access &= ~saved_mask;
+
+ DEBUG(4,("access_check_object: user rights access mask [0x%x]\n",
+ rights_mask));
+ }
+
+
+ /* check the security descriptor first */
+ status = se_access_check(psd, token, des_access, acc_granted);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (is_system || is_root) {
+ DEBUG(4,
+ ("%s: ACCESS should be DENIED (requested: %#010x)\n"
+ "but overritten by %s\n",
+ debug,
+ des_access,
+ is_root ? "euid == initial uid" : "system token"));
+
+ priv_granted = true;
+ *acc_granted = des_access;
+
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+
+done:
+ if (priv_granted) {
+ /* add in any bits saved during the privilege check (only
+ matters if status is ok) */
+
+ *acc_granted |= rights_mask;
+ }
+
+ DEBUG(4,("%s: access %s (requested: 0x%08x, granted: 0x%08x)\n",
+ debug, NT_STATUS_IS_OK(status) ? "GRANTED" : "DENIED",
+ des_access, *acc_granted));
+
+ return status;
+}
+
+
+/*******************************************************************
+ Map any MAXIMUM_ALLOWED_ACCESS request to a valid access set.
+********************************************************************/
+
+void map_max_allowed_access(const struct security_token *nt_token,
+ const struct security_unix_token *unix_token,
+ uint32_t *pacc_requested)
+{
+ if (!((*pacc_requested) & MAXIMUM_ALLOWED_ACCESS)) {
+ return;
+ }
+ *pacc_requested &= ~MAXIMUM_ALLOWED_ACCESS;
+
+ /* At least try for generic read|execute - Everyone gets that. */
+ *pacc_requested |= GENERIC_READ_ACCESS|GENERIC_EXECUTE_ACCESS;
+
+ /* root gets anything. */
+ if (unix_token->uid == sec_initial_uid()) {
+ *pacc_requested |= GENERIC_ALL_ACCESS;
+ return;
+ }
+
+ /* Full Access for 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */
+
+ if (security_token_has_sid(nt_token, &global_sid_Builtin_Administrators) ||
+ security_token_has_sid(nt_token, &global_sid_Builtin_Account_Operators)) {
+ *pacc_requested |= GENERIC_ALL_ACCESS;
+ return;
+ }
+
+ /* Full access for DOMAIN\Domain Admins. */
+ if ( IS_DC ) {
+ struct dom_sid domadmin_sid;
+ sid_compose(&domadmin_sid, get_global_sam_sid(),
+ DOMAIN_RID_ADMINS);
+ if (security_token_has_sid(nt_token, &domadmin_sid)) {
+ *pacc_requested |= GENERIC_ALL_ACCESS;
+ return;
+ }
+ }
+ /* TODO ! Check privileges. */
+}
diff --git a/source3/rpc_server/srv_access_check.h b/source3/rpc_server/srv_access_check.h
new file mode 100644
index 0000000..4f30989
--- /dev/null
+++ b/source3/rpc_server/srv_access_check.h
@@ -0,0 +1,44 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1997,
+ * Copyright (C) Paul Ashton 1997,
+ * Copyright (C) Marc Jacobsen 1999,
+ * Copyright (C) Jeremy Allison 2001-2008,
+ * Copyright (C) Jean François Micouleau 1998-2001,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002,
+ * Copyright (C) Gerald (Jerry) Carter 2003-2004,
+ * Copyright (C) Simo Sorce 2003.
+ * Copyright (C) Volker Lendecke 2005.
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_SERVER_SRV_ACCESS_CHECK_H_
+#define _RPC_SERVER_SRV_ACCESS_CHECK_H_
+
+/* The following definitions come from rpc_server/srv_access_check.c */
+
+NTSTATUS access_check_object( struct security_descriptor *psd, struct security_token *token,
+ enum sec_privilege needed_priv_1, enum sec_privilege needed_priv_2,
+ uint32_t rights_mask,
+ uint32_t des_access, uint32_t *acc_granted,
+ const char *debug );
+void map_max_allowed_access(const struct security_token *nt_token,
+ const struct security_unix_token *unix_token,
+ uint32_t *pacc_requested);
+
+#endif /* _RPC_SERVER_SRV_ACCESS_CHECK_H_ */
diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c
new file mode 100644
index 0000000..c73a4c7
--- /dev/null
+++ b/source3/rpc_server/srv_pipe_hnd.c
@@ -0,0 +1,368 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Largely re-written : 2005
+ * Copyright (C) Jeremy Allison 1998 - 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 "fake_file.h"
+#include "rpc_dce.h"
+#include "ntdomain.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "rpc_server/srv_pipe_hnd.h"
+#include "rpc_client/local_np.h"
+#include "rpc_server/rpc_server.h"
+#include "rpc_server/rpc_config.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "librpc/ndr/ndr_table.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+bool fsp_is_np(struct files_struct *fsp)
+{
+ enum FAKE_FILE_TYPE type;
+
+ if ((fsp == NULL) || (fsp->fake_file_handle == NULL)) {
+ return false;
+ }
+
+ type = fsp->fake_file_handle->type;
+
+ return (type == FAKE_FILE_TYPE_NAMED_PIPE_PROXY);
+}
+
+NTSTATUS np_open(TALLOC_CTX *mem_ctx, const char *name,
+ const struct tsocket_address *remote_client_address,
+ const struct tsocket_address *local_server_address,
+ struct auth_session_info *session_info,
+ struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ struct dcesrv_context *dce_ctx,
+ struct fake_file_handle **phandle)
+{
+ struct fake_file_handle *handle;
+ struct npa_state *npa = NULL;
+ int ret;
+
+ handle = talloc(mem_ctx, struct fake_file_handle);
+ if (handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ npa = npa_state_init(handle);
+ if (npa == NULL) {
+ TALLOC_FREE(handle);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *handle = (struct fake_file_handle) {
+ .type = FAKE_FILE_TYPE_NAMED_PIPE_PROXY,
+ .private_data = npa,
+ };
+
+ ret = local_np_connect(
+ name,
+ NCACN_NP,
+ NULL,
+ remote_client_address,
+ NULL,
+ local_server_address,
+ session_info,
+ false,
+ npa,
+ &npa->stream);
+ if (ret != 0) {
+ DBG_DEBUG("local_np_connect failed: %s\n",
+ strerror(ret));
+ TALLOC_FREE(handle);
+ return map_nt_error_from_unix(ret);
+ }
+
+ *phandle = handle;
+
+ return NT_STATUS_OK;
+}
+
+bool np_read_in_progress(struct fake_file_handle *handle)
+{
+ if (handle->type == FAKE_FILE_TYPE_NAMED_PIPE_PROXY) {
+ struct npa_state *p =
+ talloc_get_type_abort(handle->private_data,
+ struct npa_state);
+ size_t read_count;
+
+ read_count = tevent_queue_length(p->read_queue);
+ if (read_count > 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+}
+
+struct np_write_state {
+ struct tevent_context *ev;
+ struct npa_state *p;
+ struct iovec iov;
+ ssize_t nwritten;
+};
+
+static void np_write_done(struct tevent_req *subreq);
+
+struct tevent_req *np_write_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct fake_file_handle *handle,
+ const uint8_t *data, size_t len)
+{
+ struct tevent_req *req;
+ struct np_write_state *state;
+ struct npa_state *p = NULL;
+ struct tevent_req *subreq = NULL;
+
+ DBG_INFO("len: %zu\n", len);
+ dump_data(50, data, len);
+
+ req = tevent_req_create(mem_ctx, &state, struct np_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (handle->type != FAKE_FILE_TYPE_NAMED_PIPE_PROXY) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
+ return tevent_req_post(req, ev);
+ }
+
+ if (len == 0) {
+ state->nwritten = 0;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ p = talloc_get_type_abort(handle->private_data, struct npa_state);
+
+ state->ev = ev;
+ state->p = p;
+ state->iov.iov_base = discard_const_p(void, data);
+ state->iov.iov_len = len;
+
+ subreq = tstream_writev_queue_send(
+ state, ev, p->stream, p->write_queue, &state->iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, np_write_done, req);
+ return req;
+}
+
+static void np_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct np_write_state *state = tevent_req_data(
+ req, struct np_write_state);
+ ssize_t received;
+ int err;
+
+ received = tstream_writev_queue_recv(subreq, &err);
+ if (received < 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ state->nwritten = received;
+ tevent_req_done(req);
+}
+
+NTSTATUS np_write_recv(struct tevent_req *req, ssize_t *pnwritten)
+{
+ struct np_write_state *state = tevent_req_data(
+ req, struct np_write_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pnwritten = state->nwritten;
+ return NT_STATUS_OK;
+}
+
+struct np_ipc_readv_next_vector_state {
+ uint8_t *buf;
+ size_t len;
+ off_t ofs;
+ size_t remaining;
+};
+
+static void np_ipc_readv_next_vector_init(struct np_ipc_readv_next_vector_state *s,
+ uint8_t *buf, size_t len)
+{
+ ZERO_STRUCTP(s);
+
+ s->buf = buf;
+ s->len = MIN(len, UINT16_MAX);
+}
+
+static int np_ipc_readv_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count)
+{
+ struct np_ipc_readv_next_vector_state *state =
+ (struct np_ipc_readv_next_vector_state *)private_data;
+ struct iovec *vector;
+ ssize_t pending;
+ size_t wanted;
+
+ if (state->ofs == state->len) {
+ *_vector = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ pending = tstream_pending_bytes(stream);
+ if (pending == -1) {
+ return -1;
+ }
+
+ if (pending == 0 && state->ofs != 0) {
+ /* return a short read */
+ *_vector = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ if (pending == 0) {
+ /* we want at least one byte and recheck again */
+ wanted = 1;
+ } else {
+ size_t missing = state->len - state->ofs;
+ if (pending > missing) {
+ /* there's more available */
+ state->remaining = pending - missing;
+ wanted = missing;
+ } else {
+ /* read what we can get and recheck in the next cycle */
+ wanted = pending;
+ }
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0].iov_base = state->buf + state->ofs;
+ vector[0].iov_len = wanted;
+
+ state->ofs += wanted;
+
+ *_vector = vector;
+ *count = 1;
+ return 0;
+}
+
+struct np_read_state {
+ struct npa_state *p;
+ struct np_ipc_readv_next_vector_state next_vector;
+
+ ssize_t nread;
+ bool is_data_outstanding;
+};
+
+static void np_read_done(struct tevent_req *subreq);
+
+struct tevent_req *np_read_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct fake_file_handle *handle,
+ uint8_t *data, size_t len)
+{
+ struct tevent_req *req;
+ struct np_read_state *state;
+ struct npa_state *p = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct np_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (handle->type != FAKE_FILE_TYPE_NAMED_PIPE_PROXY) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
+ return tevent_req_post(req, ev);
+ }
+
+ p = talloc_get_type_abort(handle->private_data, struct npa_state);
+
+ np_ipc_readv_next_vector_init(&state->next_vector, data, len);
+
+ subreq = tstream_readv_pdu_queue_send(
+ state,
+ ev,
+ p->stream,
+ p->read_queue,
+ np_ipc_readv_next_vector,
+ &state->next_vector);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, np_read_done, req);
+ return req;
+}
+
+static void np_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct np_read_state *state = tevent_req_data(
+ req, struct np_read_state);
+ ssize_t ret;
+ int err;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ state->nread = ret;
+ state->is_data_outstanding = (state->next_vector.remaining > 0);
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS np_read_recv(struct tevent_req *req, ssize_t *nread,
+ bool *is_data_outstanding)
+{
+ struct np_read_state *state = tevent_req_data(
+ req, struct np_read_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ DEBUG(10, ("Received %d bytes. There is %smore data outstanding\n",
+ (int)state->nread, state->is_data_outstanding?"":"no "));
+
+ *nread = state->nread;
+ *is_data_outstanding = state->is_data_outstanding;
+ return NT_STATUS_OK;
+}
diff --git a/source3/rpc_server/srv_pipe_hnd.h b/source3/rpc_server/srv_pipe_hnd.h
new file mode 100644
index 0000000..ba35135
--- /dev/null
+++ b/source3/rpc_server/srv_pipe_hnd.h
@@ -0,0 +1,50 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998,
+ * Largely re-written : 2005
+ * Copyright (C) Jeremy Allison 1998 - 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_SERVER_SRV_PIPE_HND_H_
+#define _RPC_SERVER_SRV_PIPE_HND_H_
+
+struct tsocket_address;
+struct pipes_struct;
+
+/* The following definitions come from rpc_server/srv_pipe_hnd.c */
+
+bool fsp_is_np(struct files_struct *fsp);
+NTSTATUS np_open(TALLOC_CTX *mem_ctx, const char *name,
+ const struct tsocket_address *remote_client_address,
+ const struct tsocket_address *local_server_address,
+ struct auth_session_info *session_info,
+ struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ struct dcesrv_context *dce_ctx,
+ struct fake_file_handle **phandle);
+bool np_read_in_progress(struct fake_file_handle *handle);
+struct tevent_req *np_write_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct fake_file_handle *handle,
+ const uint8_t *data, size_t len);
+NTSTATUS np_write_recv(struct tevent_req *req, ssize_t *pnwritten);
+struct tevent_req *np_read_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct fake_file_handle *handle,
+ uint8_t *data, size_t len);
+NTSTATUS np_read_recv(struct tevent_req *req, ssize_t *nread,
+ bool *is_data_outstanding);
+
+#endif /* _RPC_SERVER_SRV_PIPE_HND_H_ */
diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
new file mode 100644
index 0000000..29d224c
--- /dev/null
+++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
@@ -0,0 +1,3202 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Jeremy Allison 2001.
+ * Copyright (C) Nigel Williams 2001.
+ * Copyright (C) Gerald (Jerry) Carter 2006.
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This is the implementation of the srvsvc pipe. */
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "lib/util/server_id.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc_scompat.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/open_files.h"
+#include "dbwrap/dbwrap.h"
+#include "session.h"
+#include "../lib/util/util_pw.h"
+#include "locking/share_mode_lock.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "auth.h"
+#include "messages.h"
+#include "serverid.h"
+#include "lib/global_contexts.h"
+#include "source3/lib/substitute.h"
+#include "lib/tsocket/tsocket.h"
+#include "librpc/rpc/dcesrv_core.h"
+
+extern const struct generic_mapping file_generic_mapping;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define MAX_SERVER_DISK_ENTRIES 15
+
+/* Use for enumerating connections, pipes, & files */
+
+struct file_enum_count {
+ TALLOC_CTX *ctx;
+ const char *username;
+ struct srvsvc_NetFileCtr3 *ctr3;
+ struct file_id *fids;
+};
+
+struct sess_file_info {
+ struct srvsvc_NetSessCtr1 *ctr;
+ struct sessionid *session_list;
+ uint32_t resume_handle;
+ uint32_t num_entries;
+};
+
+struct share_file_stat {
+ struct srvsvc_NetConnInfo1 *netconn_arr;
+ struct server_id *svrid_arr;
+ const char *in_sharepath;
+ uint32_t resp_entries;
+ uint32_t total_entries;
+};
+
+struct share_conn_stat {
+ TALLOC_CTX *ctx;
+ const char *sharename;
+ struct server_id *svrid_arr;
+ int count;
+};
+
+/*******************************************************************
+********************************************************************/
+
+static int enum_file_fn(struct file_id id,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ void *private_data)
+{
+ struct file_enum_count *fenum =
+ (struct file_enum_count *)private_data;
+ struct srvsvc_NetFileCtr3 *ctr3 = fenum->ctr3;
+ struct srvsvc_NetFileInfo3 *f;
+ struct file_id *fids = NULL;
+ char *fullpath = NULL;
+ uint32_t permissions;
+ const char *username;
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if ( !process_exists(e->pid) ) {
+ return 0;
+ }
+
+ username = uidtoname(e->uid);
+
+ if ((fenum->username != NULL)
+ && !strequal(username, fenum->username)) {
+ return 0;
+ }
+
+ f = talloc_realloc(
+ fenum->ctx,
+ ctr3->array,
+ struct srvsvc_NetFileInfo3,
+ ctr3->count+1);
+ if ( !f ) {
+ DBG_ERR("realloc failed for %"PRIu32" items\n", ctr3->count+1);
+ return 0;
+ }
+ ctr3->array = f;
+
+ fids = talloc_realloc(
+ fenum->ctx, fenum->fids, struct file_id, ctr3->count+1);
+ if (fids == NULL) {
+ DBG_ERR("realloc failed for %"PRIu32" items\n", ctr3->count+1);
+ return 0;
+ }
+ fids[ctr3->count] = id;
+ fenum->fids = fids;
+
+ if ( strcmp(d->base_name, "." ) == 0 ) {
+ fullpath = talloc_asprintf(
+ fenum->ctx,
+ "C:%s",
+ d->servicepath);
+ } else {
+ fullpath = talloc_asprintf(
+ fenum->ctx,
+ "C:%s/%s%s",
+ d->servicepath,
+ d->base_name,
+ (d->stream_name != NULL) ? d->stream_name : "");
+ }
+ if (!fullpath) {
+ return 0;
+ }
+ string_replace( fullpath, '/', '\\' );
+
+ /* mask out create (what ever that is) */
+ permissions = e->access_mask & (FILE_READ_DATA|FILE_WRITE_DATA);
+
+ /* now fill in the srvsvc_NetFileInfo3 struct */
+
+ ctr3->array[ctr3->count] = (struct srvsvc_NetFileInfo3) {
+ .fid = (((uint32_t)(procid_to_pid(&e->pid))<<16) |
+ e->share_file_id),
+ .permissions = permissions,
+ .path = fullpath,
+ .user = username,
+ };
+
+ ctr3->count++;
+
+ return 0;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static WERROR net_enum_files(TALLOC_CTX *ctx,
+ const char *username,
+ struct srvsvc_NetFileCtr3 **ctr3,
+ uint32_t resume)
+{
+ struct file_enum_count f_enum_cnt = {
+ .ctx = ctx, .username = username, .ctr3 = *ctr3,
+ };
+ uint32_t i;
+
+ share_entry_forall(enum_file_fn, (void *)&f_enum_cnt );
+
+ *ctr3 = f_enum_cnt.ctr3;
+
+ /* need to count the number of locks on a file */
+
+ for (i=0; i<(*ctr3)->count; i++) {
+ struct files_struct fsp = { .file_id = f_enum_cnt.fids[i], };
+ struct byte_range_lock *brl = NULL;
+
+ brl = brl_get_locks(ctx, &fsp);
+ if (brl == NULL) {
+ continue;
+ }
+
+ (*ctr3)->array[i].num_locks = brl_num_locks(brl);
+
+ TALLOC_FREE(brl);
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Utility function to get the 'type' of a share from an snum.
+ ********************************************************************/
+static enum srvsvc_ShareType get_share_type(int snum)
+{
+ /* work out the share type */
+ enum srvsvc_ShareType type = STYPE_DISKTREE;
+
+ if (lp_printable(snum)) {
+ type = lp_administrative_share(snum)
+ ? STYPE_PRINTQ_HIDDEN : STYPE_PRINTQ;
+ }
+ if (strequal(lp_fstype(snum), "IPC")) {
+ type = lp_administrative_share(snum)
+ ? STYPE_IPC_HIDDEN : STYPE_IPC;
+ }
+ return type;
+}
+
+/*******************************************************************
+ Fill in a share info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_0(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo0 *r, int snum)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ r->name = lp_servicename(talloc_tos(), lp_sub, snum);
+}
+
+/*******************************************************************
+ Fill in a share info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo1 *r,
+ int snum)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *net_name = lp_servicename(talloc_tos(), lp_sub, snum);
+ char *remark = lp_comment(p->mem_ctx, lp_sub, snum);
+
+ if (remark) {
+ remark = talloc_sub_full(
+ p->mem_ctx, lp_servicename(talloc_tos(), lp_sub, snum),
+ get_current_username(), lp_path(talloc_tos(), lp_sub, snum),
+ session_info->unix_token->uid, get_current_username(),
+ "", remark);
+ }
+
+ r->name = net_name;
+ r->type = get_share_type(snum);
+ r->comment = remark ? remark : "";
+}
+
+/*******************************************************************
+ Fill in a share info level 2 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_2(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo2 *r,
+ int snum)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *remark = NULL;
+ char *path = NULL;
+ int max_connections = lp_max_connections(snum);
+ uint32_t max_uses = UINT32_MAX;
+ char *net_name = lp_servicename(talloc_tos(), lp_sub, snum);
+
+ if (max_connections > 0) {
+ max_uses = MIN(max_connections, UINT32_MAX);
+ }
+
+ remark = lp_comment(p->mem_ctx, lp_sub, snum);
+ if (remark) {
+ remark = talloc_sub_full(
+ p->mem_ctx, lp_servicename(talloc_tos(), lp_sub, snum),
+ get_current_username(), lp_path(talloc_tos(), lp_sub, snum),
+ session_info->unix_token->uid, get_current_username(),
+ "", remark);
+ }
+ path = talloc_asprintf(p->mem_ctx,
+ "C:%s", lp_path(talloc_tos(), lp_sub, snum));
+
+ if (path) {
+ /*
+ * Change / to \\ so that win2k will see it as a valid path.
+ * This was added to enable use of browsing in win2k add
+ * share dialog.
+ */
+
+ string_replace(path, '/', '\\');
+ }
+
+ r->name = net_name;
+ r->type = get_share_type(snum);
+ r->comment = remark ? remark : "";
+ r->permissions = 0;
+ r->max_users = max_uses;
+ r->current_users = 0; /* computed later */
+ r->path = path ? path : "";
+ r->password = "";
+}
+
+/*******************************************************************
+ Map any generic bits to file specific bits.
+********************************************************************/
+
+static void map_generic_share_sd_bits(struct security_descriptor *psd)
+{
+ uint32_t i;
+ struct security_acl *ps_dacl = NULL;
+
+ if (!psd)
+ return;
+
+ ps_dacl = psd->dacl;
+ if (!ps_dacl)
+ return;
+
+ for (i = 0; i < ps_dacl->num_aces; i++) {
+ struct security_ace *psa = &ps_dacl->aces[i];
+ uint32_t orig_mask = psa->access_mask;
+
+ se_map_generic(&psa->access_mask, &file_generic_mapping);
+ psa->access_mask |= orig_mask;
+ }
+}
+
+/*******************************************************************
+ Fill in a share info level 501 structure.
+********************************************************************/
+
+static void init_srv_share_info_501(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo501 *r, int snum)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *net_name = lp_servicename(talloc_tos(), lp_sub, snum);
+ char *remark = lp_comment(p->mem_ctx, lp_sub, snum);
+
+ if (remark) {
+ remark = talloc_sub_full(
+ p->mem_ctx, lp_servicename(talloc_tos(), lp_sub, snum),
+ get_current_username(), lp_path(talloc_tos(), lp_sub, snum),
+ session_info->unix_token->uid, get_current_username(),
+ "", remark);
+ }
+
+ r->name = net_name;
+ r->type = get_share_type(snum);
+ r->comment = remark ? remark : "";
+
+ /*
+ * According to [MS-SRVS] 2.2.4.25, the flags field is the same as in
+ * level 1005.
+ */
+ r->csc_policy = (lp_csc_policy(snum) << SHARE_1005_CSC_POLICY_SHIFT);
+}
+
+/*******************************************************************
+ Fill in a share info level 502 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_502(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo502 *r, int snum)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *net_name = lp_servicename(talloc_tos(), lp_sub, snum);
+ char *path = NULL;
+ struct security_descriptor *sd = NULL;
+ struct sec_desc_buf *sd_buf = NULL;
+ size_t sd_size = 0;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ char *remark = lp_comment(ctx, lp_sub, snum);
+
+ if (remark) {
+ remark = talloc_sub_full(
+ p->mem_ctx, lp_servicename(talloc_tos(), lp_sub, snum),
+ get_current_username(), lp_path(talloc_tos(), lp_sub, snum),
+ session_info->unix_token->uid, get_current_username(),
+ "", remark);
+ }
+ path = talloc_asprintf(ctx, "C:%s", lp_path(talloc_tos(), lp_sub, snum));
+ if (path) {
+ /*
+ * Change / to \\ so that win2k will see it as a valid path. This was added to
+ * enable use of browsing in win2k add share dialog.
+ */
+ string_replace(path, '/', '\\');
+ }
+
+ sd = get_share_security(ctx, lp_servicename(talloc_tos(), lp_sub, snum), &sd_size);
+
+ sd_buf = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+
+ r->name = net_name;
+ r->type = get_share_type(snum);
+ r->comment = remark ? remark : "";
+ r->permissions = 0;
+ r->max_users = (uint32_t)-1;
+ r->current_users = 1; /* ??? */
+ r->path = path ? path : "";
+ r->password = "";
+ r->sd_buf = *sd_buf;
+}
+
+/***************************************************************************
+ Fill in a share info level 1004 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1004(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo1004 *r,
+ int snum)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *remark = lp_comment(p->mem_ctx, lp_sub, snum);
+
+ if (remark) {
+ remark = talloc_sub_full(
+ p->mem_ctx, lp_servicename(talloc_tos(), lp_sub, snum),
+ get_current_username(), lp_path(talloc_tos(), lp_sub, snum),
+ session_info->unix_token->uid, get_current_username(),
+ "", remark);
+ }
+
+ r->comment = remark ? remark : "";
+}
+
+/***************************************************************************
+ Fill in a share info level 1005 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1005(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo1005 *r,
+ int snum)
+{
+ uint32_t dfs_flags = 0;
+
+ if (lp_host_msdfs() && lp_msdfs_root(snum)) {
+ dfs_flags |= SHARE_1005_IN_DFS | SHARE_1005_DFS_ROOT;
+ }
+
+ dfs_flags |= lp_csc_policy(snum) << SHARE_1005_CSC_POLICY_SHIFT;
+
+ r->dfs_flags = dfs_flags;
+}
+
+/***************************************************************************
+ Fill in a share info level 1006 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1006(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo1006 *r,
+ int snum)
+{
+ r->max_users = (uint32_t)-1;
+}
+
+/***************************************************************************
+ Fill in a share info level 1007 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1007(struct pipes_struct *p,
+ struct srvsvc_NetShareInfo1007 *r,
+ int snum)
+{
+ r->flags = 0;
+ r->alternate_directory_name = "";
+}
+
+/*******************************************************************
+ Fill in a share info level 1501 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1501(struct pipes_struct *p,
+ struct sec_desc_buf **r,
+ int snum)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct security_descriptor *sd;
+ struct sec_desc_buf *sd_buf = NULL;
+ size_t sd_size;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ sd = get_share_security(ctx, lp_servicename(talloc_tos(), lp_sub, snum), &sd_size);
+ if (sd) {
+ sd_buf = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+ }
+
+ *r = sd_buf;
+}
+
+/*******************************************************************
+ True if it ends in '$'.
+ ********************************************************************/
+
+static bool is_hidden_share(int snum)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *net_name = lp_servicename(talloc_tos(), lp_sub, snum);
+
+ return (net_name[strlen(net_name) - 1] == '$') ? True : False;
+}
+
+/*******************************************************************
+ Verify user is allowed to view share, access based enumeration
+********************************************************************/
+static bool is_enumeration_allowed(struct pipes_struct *p,
+ int snum)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ if (!lp_access_based_share_enum(snum)) {
+ return true;
+ }
+
+ if (!user_ok_token(session_info->unix_info->unix_name,
+ session_info->info->domain_name,
+ session_info->security_token, snum)) {
+ return false;
+ }
+
+ return share_access_check(session_info->security_token,
+ lp_servicename(talloc_tos(), lp_sub, snum),
+ FILE_READ_DATA, NULL);
+}
+
+/****************************************************************************
+ Count an entry against the respective service.
+****************************************************************************/
+
+static int count_for_all_fn(struct smbXsrv_tcon_global0 *tcon, void *udp)
+{
+ union srvsvc_NetShareCtr *ctr = udp;
+
+ /* Only called for level2 */
+ struct srvsvc_NetShareCtr2 *ctr2 = ctr->ctr2;
+
+ uint32_t share_entries = ctr2->count;
+ struct srvsvc_NetShareInfo2 *info2 = ctr2->array;
+ uint32_t i = 0;
+
+ for (i = 0; i < share_entries; i++, info2++) {
+ if (strequal(tcon->share_name, info2->name)) {
+ info2->current_users++;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Count the entries belonging to all services in the connection db.
+****************************************************************************/
+
+static void count_connections_for_all_shares(union srvsvc_NetShareCtr *ctr)
+{
+ NTSTATUS status;
+ status = smbXsrv_tcon_global_traverse(count_for_all_fn, ctr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("count_connections_for_all_shares: traverse of "
+ "smbXsrv_tcon_global.tdb failed - %s\n",
+ nt_errstr(status)));
+ }
+}
+
+/*******************************************************************
+ Fill in a share info structure.
+ ********************************************************************/
+
+static WERROR init_srv_share_info_ctr(struct pipes_struct *p,
+ struct srvsvc_NetShareInfoCtr *info_ctr,
+ uint32_t *resume_handle_p,
+ uint32_t *total_entries,
+ bool all_shares)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t num_entries = 0;
+ uint32_t alloc_entries = 0;
+ int num_services = 0;
+ int snum;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ uint32_t i = 0;
+ uint32_t valid_share_count = 0;
+ bool *allowed = 0;
+ union srvsvc_NetShareCtr ctr;
+ uint32_t resume_handle = resume_handle_p ? *resume_handle_p : 0;
+ const char *unix_name = session_info->unix_info->unix_name;
+ int existing_home = -1;
+ int added_home = -1;
+ WERROR ret = WERR_OK;
+
+ DEBUG(5,("init_srv_share_info_ctr\n"));
+
+ /*
+ * We need to make sure to reload the services for the connecting user.
+ * It is possible that we have includes with substitutions.
+ *
+ * include = /etc/samba/%U.conf
+ *
+ * We also need all printers and usershares.
+ *
+ * We need to be root in order to have access to registry shares
+ * and root only smb.conf files.
+ */
+ become_root();
+ lp_kill_all_services();
+ lp_load_with_shares(get_dyn_CONFIGFILE());
+ delete_and_reload_printers();
+ load_usershare_shares(NULL, connections_snum_used);
+ load_registry_shares();
+ existing_home = lp_servicenumber(unix_name);
+ if (existing_home == -1) {
+ added_home = register_homes_share(unix_name);
+ }
+ unbecome_root();
+
+ num_services = lp_numservices();
+
+ allowed = talloc_zero_array(ctx, bool, num_services);
+ if (allowed == NULL) {
+ goto nomem;
+ }
+
+ /* Count the number of entries. */
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) &&
+ lp_allow_local_address(snum, local_address) &&
+ is_enumeration_allowed(p, snum) &&
+ (all_shares || !is_hidden_share(snum))) {
+ DEBUG(10, ("counting service %s\n",
+ lp_servicename(talloc_tos(), lp_sub, snum) ? lp_servicename(talloc_tos(), lp_sub, snum) : "(null)"));
+ allowed[snum] = true;
+ num_entries++;
+ } else {
+ DEBUG(10, ("NOT counting service %s\n",
+ lp_servicename(talloc_tos(), lp_sub, snum) ? lp_servicename(talloc_tos(), lp_sub, snum) : "(null)"));
+ }
+ }
+
+ if (!num_entries || (resume_handle >= num_entries)) {
+ goto done;
+ }
+
+ /* Calculate alloc entries. */
+ alloc_entries = num_entries - resume_handle;
+ switch (info_ctr->level) {
+ case 0:
+ ctr.ctr0 = talloc_zero(ctx, struct srvsvc_NetShareCtr0);
+ if (ctr.ctr0 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr0->count = alloc_entries;
+ ctr.ctr0->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo0, alloc_entries);
+ if (ctr.ctr0->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_0(p, &ctr.ctr0->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 1:
+ ctr.ctr1 = talloc_zero(ctx, struct srvsvc_NetShareCtr1);
+ if (ctr.ctr1 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr1->count = alloc_entries;
+ ctr.ctr1->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo1, alloc_entries);
+ if (ctr.ctr1->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_1(p, &ctr.ctr1->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 2:
+ ctr.ctr2 = talloc_zero(ctx, struct srvsvc_NetShareCtr2);
+ if (ctr.ctr2 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr2->count = alloc_entries;
+ ctr.ctr2->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo2, alloc_entries);
+ if (ctr.ctr2->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_2(p, &ctr.ctr2->array[i++], snum);
+ }
+ }
+
+ count_connections_for_all_shares(&ctr);
+ break;
+
+ case 501:
+ ctr.ctr501 = talloc_zero(ctx, struct srvsvc_NetShareCtr501);
+ if (ctr.ctr501 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr501->count = alloc_entries;
+ ctr.ctr501->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo501, alloc_entries);
+ if (ctr.ctr501->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_501(p, &ctr.ctr501->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 502:
+ ctr.ctr502 = talloc_zero(ctx, struct srvsvc_NetShareCtr502);
+ if (ctr.ctr502 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr502->count = alloc_entries;
+ ctr.ctr502->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo502, alloc_entries);
+ if (ctr.ctr502->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_502(p, &ctr.ctr502->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 1004:
+ ctr.ctr1004 = talloc_zero(ctx, struct srvsvc_NetShareCtr1004);
+ if (ctr.ctr1004 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr1004->count = alloc_entries;
+ ctr.ctr1004->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo1004, alloc_entries);
+ if (ctr.ctr1004->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_1004(p, &ctr.ctr1004->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 1005:
+ ctr.ctr1005 = talloc_zero(ctx, struct srvsvc_NetShareCtr1005);
+ if (ctr.ctr1005 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr1005->count = alloc_entries;
+ ctr.ctr1005->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo1005, alloc_entries);
+ if (ctr.ctr1005->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_1005(p, &ctr.ctr1005->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 1006:
+ ctr.ctr1006 = talloc_zero(ctx, struct srvsvc_NetShareCtr1006);
+ if (ctr.ctr1006 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr1006->count = alloc_entries;
+ ctr.ctr1006->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo1006, alloc_entries);
+ if (ctr.ctr1006->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_1006(p, &ctr.ctr1006->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 1007:
+ ctr.ctr1007 = talloc_zero(ctx, struct srvsvc_NetShareCtr1007);
+ if (ctr.ctr1007 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr1007->count = alloc_entries;
+ ctr.ctr1007->array = talloc_zero_array(ctx, struct srvsvc_NetShareInfo1007, alloc_entries);
+ if (ctr.ctr1007->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_1007(p, &ctr.ctr1007->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 1501:
+ ctr.ctr1501 = talloc_zero(ctx, struct srvsvc_NetShareCtr1501);
+ if (ctr.ctr1501 == NULL) {
+ goto nomem;
+ }
+
+ ctr.ctr1501->count = alloc_entries;
+ ctr.ctr1501->array = talloc_zero_array(ctx, struct sec_desc_buf, alloc_entries);
+ if (ctr.ctr1501->array == NULL) {
+ goto nomem;
+ }
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (allowed[snum] &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ struct sec_desc_buf *sd_buf = NULL;
+ init_srv_share_info_1501(p, &sd_buf, snum);
+ ctr.ctr1501->array[i++] = *sd_buf;
+ }
+ }
+
+ break;
+
+ default:
+ DEBUG(5,("init_srv_share_info_ctr: unsupported switch value %d\n",
+ info_ctr->level));
+ ret = WERR_INVALID_LEVEL;
+ goto done;
+ }
+
+ *total_entries = alloc_entries;
+ if (resume_handle_p) {
+ if (all_shares) {
+ *resume_handle_p = (num_entries == 0) ? *resume_handle_p : 0;
+ } else {
+ *resume_handle_p = num_entries;
+ }
+ }
+
+ info_ctr->ctr = ctr;
+ ret = WERR_OK;
+ goto done;
+nomem:
+ ret = WERR_NOT_ENOUGH_MEMORY;
+done:
+ if (added_home != -1) {
+ lp_killservice(added_home);
+ }
+ return ret;
+}
+
+/*******************************************************************
+ fill in a sess info level 0 structure.
+ ********************************************************************/
+
+static WERROR init_srv_sess_info_0(struct pipes_struct *p,
+ struct srvsvc_NetSessCtr0 *ctr0,
+ uint32_t *resume_handle_p,
+ uint32_t *total_entries)
+{
+ struct sessionid *session_list;
+ uint32_t num_entries = 0;
+ uint32_t resume_handle = resume_handle_p ? *resume_handle_p : 0;
+ *total_entries = list_sessions(p->mem_ctx, &session_list);
+
+ DEBUG(5,("init_srv_sess_info_0\n"));
+
+ if (ctr0 == NULL) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ for (; resume_handle < *total_entries; resume_handle++) {
+
+ ctr0->array = talloc_realloc(p->mem_ctx,
+ ctr0->array,
+ struct srvsvc_NetSessInfo0,
+ num_entries+1);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ ctr0->array[num_entries].client =
+ session_list[resume_handle].remote_machine;
+
+ num_entries++;
+ }
+
+ ctr0->count = num_entries;
+
+ if (resume_handle_p) {
+ if (*resume_handle_p >= *total_entries) {
+ *resume_handle_p = 0;
+ } else {
+ *resume_handle_p = resume_handle;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/***********************************************************************
+ * find out the session on which this file is open and bump up its count
+ **********************************************************************/
+
+static int count_sess_files_fn(struct file_id fid,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ void *data)
+{
+ struct sess_file_info *info = data;
+ uint32_t rh = info->resume_handle;
+ uint32_t i;
+
+ for (i=0; i < info->num_entries; i++) {
+ /* rh+info->num_entries is safe, as we've
+ ensured that:
+ *total_entries > resume_handle &&
+ info->num_entries = *total_entries - resume_handle;
+ inside init_srv_sess_info_1() below.
+ */
+ struct sessionid *sess = &info->session_list[rh + i];
+ if ((e->uid == sess->uid) &&
+ server_id_equal(&e->pid, &sess->pid)) {
+
+ info->ctr->array[i].num_open++;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*******************************************************************
+ * count the num of open files on all sessions
+ *******************************************************************/
+
+static void net_count_files_for_all_sess(struct srvsvc_NetSessCtr1 *ctr1,
+ struct sessionid *session_list,
+ uint32_t resume_handle,
+ uint32_t num_entries)
+{
+ struct sess_file_info s_file_info;
+
+ s_file_info.ctr = ctr1;
+ s_file_info.session_list = session_list;
+ s_file_info.resume_handle = resume_handle;
+ s_file_info.num_entries = num_entries;
+
+ share_entry_forall(count_sess_files_fn, &s_file_info);
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static WERROR init_srv_sess_info_1(struct pipes_struct *p,
+ struct srvsvc_NetSessCtr1 *ctr1,
+ uint32_t *resume_handle_p,
+ uint32_t *total_entries)
+{
+ struct sessionid *session_list;
+ uint32_t num_entries = 0;
+ time_t now = time(NULL);
+ uint32_t resume_handle = resume_handle_p ? *resume_handle_p : 0;
+
+ ZERO_STRUCTP(ctr1);
+
+ if (ctr1 == NULL) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ *total_entries = list_sessions(p->mem_ctx, &session_list);
+
+ if (resume_handle >= *total_entries) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ /* We know num_entries must be positive, due to
+ the check resume_handle >= *total_entries above. */
+
+ num_entries = *total_entries - resume_handle;
+
+ ctr1->array = talloc_zero_array(p->mem_ctx,
+ struct srvsvc_NetSessInfo1,
+ num_entries);
+
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (num_entries = 0; resume_handle < *total_entries; num_entries++, resume_handle++) {
+ uint32_t connect_time;
+ bool guest;
+
+ connect_time = (uint32_t)(now - session_list[resume_handle].connect_start);
+ guest = strequal( session_list[resume_handle].username, lp_guest_account() );
+
+ ctr1->array[num_entries].client = session_list[resume_handle].remote_machine;
+ ctr1->array[num_entries].user = session_list[resume_handle].username;
+ ctr1->array[num_entries].num_open = 0;/* computed later */
+ ctr1->array[num_entries].time = connect_time;
+ ctr1->array[num_entries].idle_time = 0;
+ ctr1->array[num_entries].user_flags = guest;
+ }
+
+ ctr1->count = num_entries;
+
+ /* count open files on all sessions in single tdb traversal */
+ net_count_files_for_all_sess(ctr1, session_list,
+ resume_handle_p ? *resume_handle_p : 0,
+ num_entries);
+
+ if (resume_handle_p) {
+ if (*resume_handle_p >= *total_entries) {
+ *resume_handle_p = 0;
+ } else {
+ *resume_handle_p = resume_handle;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ find the share connection on which this open exists.
+ ********************************************************************/
+
+static int share_file_fn(struct file_id fid,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ void *data)
+{
+ struct share_file_stat *sfs = data;
+ uint32_t i;
+ uint32_t offset = sfs->total_entries - sfs->resp_entries;
+
+ if (strequal(d->servicepath, sfs->in_sharepath)) {
+ for (i=0; i < sfs->resp_entries; i++) {
+ if (server_id_equal(
+ &e->pid, &sfs->svrid_arr[offset + i])) {
+ sfs->netconn_arr[i].num_open ++;
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/*******************************************************************
+ count number of open files on given share connections.
+ ********************************************************************/
+
+static void count_share_opens(struct srvsvc_NetConnInfo1 *arr,
+ struct server_id *svrid_arr, char *sharepath,
+ uint32_t resp_entries, uint32_t total_entries)
+{
+ struct share_file_stat sfs;
+
+ sfs.netconn_arr = arr;
+ sfs.svrid_arr = svrid_arr;
+ sfs.in_sharepath = sharepath;
+ sfs.resp_entries = resp_entries;
+ sfs.total_entries = total_entries;
+
+ share_entry_forall(share_file_fn, &sfs);
+}
+
+/****************************************************************************
+ process an entry from the connection db.
+****************************************************************************/
+
+static int share_conn_fn(struct smbXsrv_tcon_global0 *tcon,
+ void *data)
+{
+ struct share_conn_stat *scs = data;
+
+ if (!process_exists(tcon->server_id)) {
+ return 0;
+ }
+
+ if (strequal(tcon->share_name, scs->sharename)) {
+ scs->svrid_arr = talloc_realloc(scs->ctx, scs->svrid_arr,
+ struct server_id,
+ scs->count + 1);
+ if (!scs->svrid_arr) {
+ return 0;
+ }
+
+ scs->svrid_arr[scs->count] = tcon->server_id;
+ scs->count++;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ Count the connections to a share. Build an array of serverid's owning these
+ connections.
+****************************************************************************/
+
+static uint32_t count_share_conns(TALLOC_CTX *ctx, const char *sharename,
+ struct server_id **arr)
+{
+ struct share_conn_stat scs;
+ NTSTATUS status;
+
+ scs.ctx = ctx;
+ scs.sharename = sharename;
+ scs.svrid_arr = NULL;
+ scs.count = 0;
+
+ status = smbXsrv_tcon_global_traverse(share_conn_fn, &scs);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("count_share_conns: traverse of "
+ "smbXsrv_tcon_global.tdb failed - %s\n",
+ nt_errstr(status)));
+ return 0;
+ }
+
+ *arr = scs.svrid_arr;
+ return scs.count;
+}
+
+/*******************************************************************
+ fill in a conn info level 0 structure.
+ ********************************************************************/
+
+static WERROR init_srv_conn_info_0(struct srvsvc_NetConnCtr0 *ctr0,
+ uint32_t *resume_handle_p,
+ uint32_t *total_entries)
+{
+ uint32_t num_entries = 0;
+ uint32_t resume_handle = resume_handle_p ? *resume_handle_p : 0;
+
+ DEBUG(5,("init_srv_conn_info_0\n"));
+
+ if (ctr0 == NULL) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ *total_entries = 1;
+
+ ZERO_STRUCTP(ctr0);
+
+ for (; resume_handle < *total_entries; resume_handle++) {
+
+ ctr0->array = talloc_realloc(talloc_tos(),
+ ctr0->array,
+ struct srvsvc_NetConnInfo0,
+ num_entries+1);
+ if (!ctr0->array) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ctr0->array[num_entries].conn_id = *total_entries;
+
+ /* move on to creating next connection */
+ num_entries++;
+ }
+
+ ctr0->count = num_entries;
+ *total_entries = num_entries;
+
+ if (resume_handle_p) {
+ if (*resume_handle_p >= *total_entries) {
+ *resume_handle_p = 0;
+ } else {
+ *resume_handle_p = resume_handle;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ fill in a conn info level 1 structure.
+ ********************************************************************/
+
+static WERROR init_srv_conn_info_1(const char *name,
+ struct srvsvc_NetConnCtr1 *ctr1,
+ uint32_t *resume_handle_p,
+ uint32_t *total_entries)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t num_entries = 0;
+ int snum = 0;
+ uint32_t resume_handle = resume_handle_p ? *resume_handle_p : 0;
+ char *share_name = NULL;
+ struct server_id *svrid_arr = NULL;
+
+ DEBUG(5,("init_srv_conn_info_1\n"));
+
+ if (ctr1 == NULL) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ /* check if this is a server name or a share name */
+ if (name && (strlen(name) > 2) && (name[0] == '\\') &&
+ (name[1] == '\\')) {
+
+ /* 'name' is a server name - this part is unimplemented */
+ *total_entries = 1;
+ } else {
+ /* 'name' is a share name */
+ snum = find_service(talloc_tos(), name, &share_name);
+
+ if (!share_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (snum < 0) {
+ return WERR_INVALID_NAME;
+ }
+
+ /*
+ * count the num of connections to this share. Also,
+ * build a list of serverid's that own these
+ * connections. The serverid list is used later to
+ * identify the share connection on which an open exists.
+ */
+
+ *total_entries = count_share_conns(talloc_tos(),
+ share_name,
+ &svrid_arr);
+ }
+
+ if (resume_handle >= *total_entries) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ /*
+ * We know num_entries must be positive, due to
+ * the check resume_handle >= *total_entries above.
+ */
+
+ num_entries = *total_entries - resume_handle;
+
+ ZERO_STRUCTP(ctr1);
+
+ ctr1->array = talloc_zero_array(talloc_tos(),
+ struct srvsvc_NetConnInfo1,
+ num_entries);
+
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (num_entries = 0; resume_handle < *total_entries;
+ num_entries++, resume_handle++) {
+
+ ctr1->array[num_entries].conn_id = *total_entries;
+ ctr1->array[num_entries].conn_type = 0x3;
+
+ /*
+ * if these are connections to a share, we are going to
+ * compute the opens on them later. If it's for the server,
+ * it's unimplemented.
+ */
+
+ if (!share_name) {
+ ctr1->array[num_entries].num_open = 1;
+ }
+
+ ctr1->array[num_entries].num_users = 1;
+ ctr1->array[num_entries].conn_time = 3;
+ ctr1->array[num_entries].user = "dummy_user";
+ ctr1->array[num_entries].share = "IPC$";
+ }
+
+ /* now compute open files on the share connections */
+
+ if (share_name) {
+
+ /*
+ * the locking tdb, which has the open files information,
+ * does not store share name or share (service) number, but
+ * just the share path. So, we can compute open files only
+ * on the share path. If more than one shares are defined
+ * on a share path, open files on all of them are included
+ * in the count.
+ *
+ * To have the correct behavior in case multiple shares
+ * are defined on the same path, changes to tdb records
+ * would be required. That would be lot more effort, so
+ * this seems a good stopgap fix.
+ */
+
+ count_share_opens(ctr1->array, svrid_arr,
+ lp_path(talloc_tos(), lp_sub, snum),
+ num_entries, *total_entries);
+
+ }
+
+ ctr1->count = num_entries;
+ *total_entries = num_entries;
+
+ if (resume_handle_p) {
+ *resume_handle_p = resume_handle;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _srvsvc_NetFileEnum
+*******************************************************************/
+
+WERROR _srvsvc_NetFileEnum(struct pipes_struct *p,
+ struct srvsvc_NetFileEnum *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ TALLOC_CTX *ctx = NULL;
+ struct srvsvc_NetFileCtr3 *ctr3;
+ uint32_t resume_hnd = 0;
+ WERROR werr;
+
+ switch (r->in.info_ctr->level) {
+ case 3:
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ session_info->security_token)) {
+ DEBUG(1, ("Enumerating files only allowed for "
+ "administrators\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ ctx = talloc_tos();
+ ctr3 = r->in.info_ctr->ctr.ctr3;
+ if (!ctr3) {
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* TODO -- Windows enumerates
+ (b) active pipes
+ (c) open directories and files */
+
+ werr = net_enum_files(ctx, r->in.user, &ctr3, resume_hnd);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ *r->out.totalentries = ctr3->count;
+ r->out.info_ctr->ctr.ctr3->array = ctr3->array;
+ r->out.info_ctr->ctr.ctr3->count = ctr3->count;
+
+ werr = WERR_OK;
+
+ done:
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetSrvGetInfo
+********************************************************************/
+
+WERROR _srvsvc_NetSrvGetInfo(struct pipes_struct *p,
+ struct srvsvc_NetSrvGetInfo *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ WERROR status = WERR_OK;
+
+ DEBUG(5,("_srvsvc_NetSrvGetInfo: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _srvsvc_NetSrvGetInfo\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (r->in.level) {
+
+ /* Technically level 102 should only be available to
+ Administrators but there isn't anything super-secret
+ here, as most of it is made up. */
+
+ case 102: {
+ struct srvsvc_NetSrvInfo102 *info102;
+
+ info102 = talloc(p->mem_ctx, struct srvsvc_NetSrvInfo102);
+ if (!info102) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ info102->platform_id = PLATFORM_ID_NT;
+ info102->server_name = lp_netbios_name();
+ info102->version_major = SAMBA_MAJOR_NBT_ANNOUNCE_VERSION;
+ info102->version_minor = SAMBA_MINOR_NBT_ANNOUNCE_VERSION;
+ info102->server_type = lp_default_server_announce();
+ info102->comment =
+ string_truncate(lp_server_string(info102, lp_sub),
+ MAX_SERVER_STRING_LENGTH);
+ info102->users = 0xffffffff;
+ info102->disc = 0xf;
+ info102->hidden = 0;
+ info102->announce = 240;
+ info102->anndelta = 3000;
+ info102->licenses = 100000;
+ info102->userpath = "C:\\";
+
+ r->out.info->info102 = info102;
+ break;
+ }
+ case 101: {
+ struct srvsvc_NetSrvInfo101 *info101;
+
+ info101 = talloc(p->mem_ctx, struct srvsvc_NetSrvInfo101);
+ if (!info101) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ info101->platform_id = PLATFORM_ID_NT;
+ info101->server_name = lp_netbios_name();
+ info101->version_major = SAMBA_MAJOR_NBT_ANNOUNCE_VERSION;
+ info101->version_minor = SAMBA_MINOR_NBT_ANNOUNCE_VERSION;
+ info101->server_type = lp_default_server_announce();
+ info101->comment =
+ string_truncate(lp_server_string(info101, lp_sub),
+ MAX_SERVER_STRING_LENGTH);
+
+ r->out.info->info101 = info101;
+ break;
+ }
+ case 100: {
+ struct srvsvc_NetSrvInfo100 *info100;
+
+ info100 = talloc(p->mem_ctx, struct srvsvc_NetSrvInfo100);
+ if (!info100) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ info100->platform_id = PLATFORM_ID_NT;
+ info100->server_name = lp_netbios_name();
+
+ r->out.info->info100 = info100;
+
+ break;
+ }
+ default:
+ status = WERR_INVALID_LEVEL;
+ break;
+ }
+
+ DEBUG(5,("_srvsvc_NetSrvGetInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _srvsvc_NetSrvSetInfo
+********************************************************************/
+
+WERROR _srvsvc_NetSrvSetInfo(struct pipes_struct *p,
+ struct srvsvc_NetSrvSetInfo *r)
+{
+ WERROR status = WERR_OK;
+
+ DEBUG(5,("_srvsvc_NetSrvSetInfo: %d\n", __LINE__));
+
+ /* Set up the net server set info structure. */
+
+ DEBUG(5,("_srvsvc_NetSrvSetInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _srvsvc_NetConnEnum
+********************************************************************/
+
+WERROR _srvsvc_NetConnEnum(struct pipes_struct *p,
+ struct srvsvc_NetConnEnum *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetConnEnum: %d\n", __LINE__));
+
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ session_info->security_token)) {
+ DEBUG(1, ("Enumerating connections only allowed for "
+ "administrators\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ werr = init_srv_conn_info_0(r->in.info_ctr->ctr.ctr0,
+ r->in.resume_handle,
+ r->out.totalentries);
+ break;
+ case 1:
+ werr = init_srv_conn_info_1(r->in.path,
+ r->in.info_ctr->ctr.ctr1,
+ r->in.resume_handle,
+ r->out.totalentries);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ DEBUG(5,("_srvsvc_NetConnEnum: %d\n", __LINE__));
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetSessEnum
+********************************************************************/
+
+WERROR _srvsvc_NetSessEnum(struct pipes_struct *p,
+ struct srvsvc_NetSessEnum *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetSessEnum: %d\n", __LINE__));
+
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ session_info->security_token)) {
+ DEBUG(1, ("Enumerating sessions only allowed for "
+ "administrators\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ werr = init_srv_sess_info_0(p,
+ r->in.info_ctr->ctr.ctr0,
+ r->in.resume_handle,
+ r->out.totalentries);
+ break;
+ case 1:
+ werr = init_srv_sess_info_1(p,
+ r->in.info_ctr->ctr.ctr1,
+ r->in.resume_handle,
+ r->out.totalentries);
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ DEBUG(5,("_srvsvc_NetSessEnum: %d\n", __LINE__));
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetSessDel
+********************************************************************/
+
+WERROR _srvsvc_NetSessDel(struct pipes_struct *p,
+ struct srvsvc_NetSessDel *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct sessionid *session_list;
+ int num_sessions, snum;
+ const char *username;
+ const char *machine;
+ bool not_root = False;
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetSessDel: %d\n", __LINE__));
+
+ werr = WERR_ACCESS_DENIED;
+
+ /* fail out now if you are not root or not a domain admin */
+
+ if ((session_info->unix_token->uid != sec_initial_uid()) &&
+ ( ! nt_token_check_domain_rid(session_info->security_token,
+ DOMAIN_RID_ADMINS))) {
+
+ goto done;
+ }
+
+ username = r->in.user;
+ machine = r->in.client;
+
+ /* strip leading backslashes if any */
+ if (machine && machine[0] == '\\' && machine[1] == '\\') {
+ machine += 2;
+ }
+
+ num_sessions = find_sessions(p->mem_ctx, username, machine,
+ &session_list);
+
+ for (snum = 0; snum < num_sessions; snum++) {
+
+ NTSTATUS ntstat;
+
+ if (session_info->unix_token->uid != sec_initial_uid()) {
+ not_root = True;
+ become_root();
+ }
+
+ ntstat = messaging_send(p->msg_ctx,
+ session_list[snum].pid,
+ MSG_SHUTDOWN, &data_blob_null);
+
+ if (NT_STATUS_IS_OK(ntstat))
+ werr = WERR_OK;
+
+ if (not_root)
+ unbecome_root();
+ }
+
+ DEBUG(5,("_srvsvc_NetSessDel: %d\n", __LINE__));
+
+done:
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareEnumAll
+********************************************************************/
+
+WERROR _srvsvc_NetShareEnumAll(struct pipes_struct *p,
+ struct srvsvc_NetShareEnumAll *r)
+{
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetShareEnumAll: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _srvsvc_NetShareEnumAll\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Create the list of shares for the response. */
+ werr = init_srv_share_info_ctr(p,
+ r->in.info_ctr,
+ r->in.resume_handle,
+ r->out.totalentries,
+ true);
+
+ DEBUG(5,("_srvsvc_NetShareEnumAll: %d\n", __LINE__));
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareEnum
+********************************************************************/
+
+WERROR _srvsvc_NetShareEnum(struct pipes_struct *p,
+ struct srvsvc_NetShareEnum *r)
+{
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetShareEnum: %d\n", __LINE__));
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _srvsvc_NetShareEnum\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Create the list of shares for the response. */
+ werr = init_srv_share_info_ctr(p,
+ r->in.info_ctr,
+ r->in.resume_handle,
+ r->out.totalentries,
+ false);
+
+ DEBUG(5,("_srvsvc_NetShareEnum: %d\n", __LINE__));
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareGetInfo
+********************************************************************/
+
+WERROR _srvsvc_NetShareGetInfo(struct pipes_struct *p,
+ struct srvsvc_NetShareGetInfo *r)
+{
+ WERROR status = WERR_OK;
+ char *share_name = NULL;
+ int snum;
+ union srvsvc_NetShareInfo *info = r->out.info;
+
+ DEBUG(5,("_srvsvc_NetShareGetInfo: %d\n", __LINE__));
+
+ if (!r->in.share_name) {
+ return WERR_INVALID_NAME;
+ }
+
+ snum = find_service(talloc_tos(), r->in.share_name, &share_name);
+ if (!share_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (snum < 0) {
+ return WERR_INVALID_NAME;
+ }
+
+ switch (r->in.level) {
+ case 0:
+ info->info0 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo0);
+ W_ERROR_HAVE_NO_MEMORY(info->info0);
+ init_srv_share_info_0(p, info->info0, snum);
+ break;
+ case 1:
+ info->info1 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo1);
+ W_ERROR_HAVE_NO_MEMORY(info->info1);
+ init_srv_share_info_1(p, info->info1, snum);
+ break;
+ case 2:
+ info->info2 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo2);
+ W_ERROR_HAVE_NO_MEMORY(info->info2);
+ init_srv_share_info_2(p, info->info2, snum);
+ info->info2->current_users =
+ count_current_connections(info->info2->name, false);
+ break;
+ case 501:
+ info->info501 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo501);
+ W_ERROR_HAVE_NO_MEMORY(info->info501);
+ init_srv_share_info_501(p, info->info501, snum);
+ break;
+ case 502:
+ info->info502 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo502);
+ W_ERROR_HAVE_NO_MEMORY(info->info502);
+ init_srv_share_info_502(p, info->info502, snum);
+ break;
+ case 1004:
+ info->info1004 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo1004);
+ W_ERROR_HAVE_NO_MEMORY(info->info1004);
+ init_srv_share_info_1004(p, info->info1004, snum);
+ break;
+ case 1005:
+ info->info1005 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo1005);
+ W_ERROR_HAVE_NO_MEMORY(info->info1005);
+ init_srv_share_info_1005(p, info->info1005, snum);
+ break;
+ case 1006:
+ info->info1006 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo1006);
+ W_ERROR_HAVE_NO_MEMORY(info->info1006);
+ init_srv_share_info_1006(p, info->info1006, snum);
+ break;
+ case 1007:
+ info->info1007 = talloc(p->mem_ctx, struct srvsvc_NetShareInfo1007);
+ W_ERROR_HAVE_NO_MEMORY(info->info1007);
+ init_srv_share_info_1007(p, info->info1007, snum);
+ break;
+ case 1501:
+ init_srv_share_info_1501(p, &info->info1501, snum);
+ break;
+ default:
+ DEBUG(5,("_srvsvc_NetShareGetInfo: unsupported switch value %d\n",
+ r->in.level));
+ status = WERR_INVALID_LEVEL;
+ break;
+ }
+
+ DEBUG(5,("_srvsvc_NetShareGetInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareSetInfo. Modify share details.
+********************************************************************/
+
+WERROR _srvsvc_NetShareSetInfo(struct pipes_struct *p,
+ struct srvsvc_NetShareSetInfo *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *command = NULL;
+ char *share_name = NULL;
+ char *comment = NULL;
+ const char *pathname = NULL;
+ int type;
+ int snum;
+ int ret;
+ char *path = NULL;
+ struct security_descriptor *psd = NULL;
+ bool is_disk_op = False;
+ const char *csc_policy = NULL;
+ bool csc_policy_changed = false;
+ const char *csc_policies[] = {"manual", "documents", "programs",
+ "disable"};
+ uint32_t client_csc_policy;
+ int max_connections = 0;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ union srvsvc_NetShareInfo *info = r->in.info;
+
+ DEBUG(5,("_srvsvc_NetShareSetInfo: %d\n", __LINE__));
+
+ if (!r->in.share_name) {
+ return WERR_INVALID_NAME;
+ }
+
+ if (r->out.parm_error) {
+ *r->out.parm_error = 0;
+ }
+
+ if ( strequal(r->in.share_name,"IPC$")
+ || ( lp_enable_asu_support() && strequal(r->in.share_name,"ADMIN$") )
+ || strequal(r->in.share_name,"global") )
+ {
+ DEBUG(5,("_srvsvc_NetShareSetInfo: share %s cannot be "
+ "modified by a remote user.\n",
+ r->in.share_name ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ snum = find_service(talloc_tos(), r->in.share_name, &share_name);
+ if (!share_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Does this share exist ? */
+ if (snum < 0)
+ return WERR_NERR_NETNAMENOTFOUND;
+
+ /* No change to printer shares. */
+ if (lp_printable(snum))
+ return WERR_ACCESS_DENIED;
+
+ is_disk_op = security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_DISK_OPERATOR);
+
+ /* fail out now if you are not root and not a disk op */
+
+ if (session_info->unix_token->uid != sec_initial_uid() && !is_disk_op) {
+ DEBUG(2,("_srvsvc_NetShareSetInfo: uid %u doesn't have the "
+ "SeDiskOperatorPrivilege privilege needed to modify "
+ "share %s\n",
+ (unsigned int)session_info->unix_token->uid,
+ share_name ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ max_connections = lp_max_connections(snum);
+ csc_policy = csc_policies[lp_csc_policy(snum)];
+
+ switch (r->in.level) {
+ case 1:
+ pathname = lp_path(ctx, lp_sub, snum);
+ comment = talloc_strdup(ctx, info->info1->comment);
+ type = info->info1->type;
+ psd = NULL;
+ break;
+ case 2:
+ comment = talloc_strdup(ctx, info->info2->comment);
+ pathname = info->info2->path;
+ type = info->info2->type;
+ max_connections = (info->info2->max_users == (uint32_t)-1) ?
+ 0 : info->info2->max_users;
+ psd = NULL;
+ break;
+#if 0
+ /* not supported on set but here for completeness */
+ case 501:
+ comment = talloc_strdup(ctx, info->info501->comment);
+ type = info->info501->type;
+ psd = NULL;
+ break;
+#endif
+ case 502:
+ comment = talloc_strdup(ctx, info->info502->comment);
+ pathname = info->info502->path;
+ type = info->info502->type;
+ psd = info->info502->sd_buf.sd;
+ map_generic_share_sd_bits(psd);
+ break;
+ case 1004:
+ pathname = lp_path(ctx, lp_sub, snum);
+ comment = talloc_strdup(ctx, info->info1004->comment);
+ type = STYPE_DISKTREE;
+ break;
+ case 1005:
+ /* XP re-sets the csc policy even if it wasn't changed by the
+ user, so we must compare it to see if it's what is set in
+ smb.conf, so that we can continue other ops like setting
+ ACLs on a share */
+ client_csc_policy = (info->info1005->dfs_flags &
+ SHARE_1005_CSC_POLICY_MASK) >>
+ SHARE_1005_CSC_POLICY_SHIFT;
+
+ if (client_csc_policy == (uint32_t)lp_csc_policy(snum)) {
+ return WERR_OK;
+ }
+
+ csc_policy = csc_policies[client_csc_policy];
+ csc_policy_changed = true;
+
+ pathname = lp_path(ctx, lp_sub, snum);
+ comment = lp_comment(ctx, lp_sub, snum);
+ type = STYPE_DISKTREE;
+ break;
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ case 1501:
+ pathname = lp_path(ctx, lp_sub, snum);
+ comment = lp_comment(ctx, lp_sub, snum);
+ psd = info->info1501->sd;
+ map_generic_share_sd_bits(psd);
+ type = STYPE_DISKTREE;
+ break;
+ default:
+ DEBUG(5,("_srvsvc_NetShareSetInfo: unsupported switch value %d\n",
+ r->in.level));
+ return WERR_INVALID_LEVEL;
+ }
+
+ /* We can only modify disk shares. */
+ if (type != STYPE_DISKTREE) {
+ DEBUG(5,("_srvsvc_NetShareSetInfo: share %s is not a "
+ "disk share\n",
+ share_name ));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (comment == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname(p->mem_ctx, pathname ))) {
+ DEBUG(5,("_srvsvc_NetShareSetInfo: invalid pathname %s\n",
+ pathname ));
+ return WERR_BAD_PATHNAME;
+ }
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ string_replace(share_name, '"', ' ');
+ string_replace(path, '"', ' ');
+ string_replace(comment, '"', ' ');
+
+ DEBUG(10,("_srvsvc_NetShareSetInfo: change share command = %s\n",
+ lp_change_share_command(talloc_tos(), lp_sub) ? lp_change_share_command(talloc_tos(), lp_sub) : "NULL" ));
+
+ /* Only call modify function if something changed. */
+
+ if (strcmp(path, lp_path(talloc_tos(), lp_sub, snum))
+ || strcmp(comment, lp_comment(talloc_tos(), lp_sub, snum))
+ || (lp_max_connections(snum) != max_connections)
+ || csc_policy_changed) {
+
+ if (!lp_change_share_command(talloc_tos(), lp_sub) || !*lp_change_share_command(talloc_tos(), lp_sub)) {
+ DEBUG(10,("_srvsvc_NetShareSetInfo: No change share command\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ command = talloc_asprintf(p->mem_ctx,
+ "%s \"%s\" \"%s\" \"%s\" \"%s\" %d \"%s\"",
+ lp_change_share_command(talloc_tos(), lp_sub),
+ get_dyn_CONFIGFILE(),
+ share_name,
+ path,
+ comment,
+ max_connections,
+ csc_policy);
+ if (!command) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ DEBUG(10,("_srvsvc_NetShareSetInfo: Running [%s]\n", command ));
+
+ /********* BEGIN SeDiskOperatorPrivilege BLOCK *********/
+
+ if (is_disk_op)
+ become_root();
+
+ ret = smbrun(command, NULL, NULL);
+ if (ret == 0) {
+ reload_services(NULL, NULL, false);
+
+ /* Tell everyone we updated smb.conf. */
+ messaging_send_all(p->msg_ctx, MSG_SMB_CONF_UPDATED,
+ NULL, 0);
+ }
+
+ if ( is_disk_op )
+ unbecome_root();
+
+ /********* END SeDiskOperatorPrivilege BLOCK *********/
+
+ DEBUG(3,("_srvsvc_NetShareSetInfo: Running [%s] returned (%d)\n",
+ command, ret ));
+
+ TALLOC_FREE(command);
+
+ if ( ret != 0 )
+ return WERR_ACCESS_DENIED;
+ } else {
+ DEBUG(10,("_srvsvc_NetShareSetInfo: No change to share name (%s)\n",
+ share_name ));
+ }
+
+ /* Replace SD if changed. */
+ if (psd) {
+ struct security_descriptor *old_sd;
+ size_t sd_size;
+ NTSTATUS status;
+
+ old_sd = get_share_security(p->mem_ctx, lp_servicename(talloc_tos(), lp_sub, snum), &sd_size);
+
+ if (old_sd && !security_descriptor_equal(old_sd, psd)) {
+ status = set_share_security(share_name, psd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("_srvsvc_NetShareSetInfo: Failed to change security info in share %s.\n",
+ share_name ));
+ }
+ }
+ }
+
+ DEBUG(5,("_srvsvc_NetShareSetInfo: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareAdd.
+ Call 'add_share_command "sharename" "pathname"
+ "comment" "max connections = "
+********************************************************************/
+
+WERROR _srvsvc_NetShareAdd(struct pipes_struct *p,
+ struct srvsvc_NetShareAdd *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ char *command = NULL;
+ char *share_name_in = NULL;
+ char *share_name = NULL;
+ char *comment = NULL;
+ char *pathname = NULL;
+ int type;
+ int snum;
+ int ret;
+ char *path;
+ struct security_descriptor *psd = NULL;
+ bool is_disk_op;
+ int max_connections = 0;
+ SMB_STRUCT_STAT st;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ DEBUG(5,("_srvsvc_NetShareAdd: %d\n", __LINE__));
+
+ if (r->out.parm_error) {
+ *r->out.parm_error = 0;
+ }
+
+ is_disk_op = security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_DISK_OPERATOR);
+
+ if (session_info->unix_token->uid != sec_initial_uid() && !is_disk_op) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!lp_add_share_command(talloc_tos(), lp_sub) || !*lp_add_share_command(talloc_tos(), lp_sub)) {
+ DBG_WARNING("_srvsvc_NetShareAdd: No \"add share command\" parameter set in smb.conf.\n");
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (r->in.level) {
+ case 0:
+ /* No path. Not enough info in a level 0 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 1:
+ /* Not enough info in a level 1 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 2:
+ share_name_in = talloc_strdup(ctx, r->in.info->info2->name);
+ comment = talloc_strdup(ctx, r->in.info->info2->comment);
+ pathname = talloc_strdup(ctx, r->in.info->info2->path);
+ max_connections = (r->in.info->info2->max_users == (uint32_t)-1) ?
+ 0 : r->in.info->info2->max_users;
+ type = r->in.info->info2->type;
+ break;
+ case 501:
+ /* No path. Not enough info in a level 501 to do anything. */
+ return WERR_ACCESS_DENIED;
+ case 502:
+ share_name_in = talloc_strdup(ctx, r->in.info->info502->name);
+ comment = talloc_strdup(ctx, r->in.info->info502->comment);
+ pathname = talloc_strdup(ctx, r->in.info->info502->path);
+ max_connections = (r->in.info->info502->max_users == (uint32_t)-1) ?
+ 0 : r->in.info->info502->max_users;
+ type = r->in.info->info502->type;
+ psd = r->in.info->info502->sd_buf.sd;
+ map_generic_share_sd_bits(psd);
+ break;
+
+ /* none of the following contain share names. NetShareAdd does not have a separate parameter for the share name */
+
+ case 1004:
+ case 1005:
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ case 1501:
+ /* DFS only level. */
+ return WERR_ACCESS_DENIED;
+ default:
+ DEBUG(5,("_srvsvc_NetShareAdd: unsupported switch value %d\n",
+ r->in.level));
+ return WERR_INVALID_LEVEL;
+ }
+
+ /* check for invalid share names */
+
+ if (!share_name_in || !validate_net_name(share_name_in,
+ INVALID_SHARENAME_CHARS,
+ strlen(share_name_in))) {
+ DEBUG(5,("_srvsvc_NetShareAdd: Bad sharename \"%s\"\n",
+ share_name_in ? share_name_in : ""));
+ return WERR_INVALID_NAME;
+ }
+
+ if (strequal(share_name_in,"IPC$") || strequal(share_name_in,"global")
+ || (lp_enable_asu_support() &&
+ strequal(share_name_in,"ADMIN$"))) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ snum = find_service(ctx, share_name_in, &share_name);
+ if (!share_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Share already exists. */
+ if (snum >= 0) {
+ return WERR_FILE_EXISTS;
+ }
+
+ /* We can only add disk shares. */
+ if (type != STYPE_DISKTREE) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname(p->mem_ctx, pathname))) {
+ return WERR_BAD_PATHNAME;
+ }
+
+ ret = sys_lstat(path, &st, false);
+ if (ret == -1 && (errno != EACCES)) {
+ /*
+ * If path has any other than permission
+ * problem, return WERR_FILE_NOT_FOUND (as Windows
+ * does.
+ */
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ string_replace(share_name_in, '"', ' ');
+ string_replace(share_name, '"', ' ');
+ string_replace(path, '"', ' ');
+ if (comment) {
+ string_replace(comment, '"', ' ');
+ }
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\" \"%s\" \"%s\" \"%s\" %d",
+ lp_add_share_command(talloc_tos(), lp_sub),
+ get_dyn_CONFIGFILE(),
+ share_name_in,
+ path,
+ comment ? comment : "",
+ max_connections);
+ if (!command) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ DEBUG(10,("_srvsvc_NetShareAdd: Running [%s]\n", command ));
+
+ /********* BEGIN SeDiskOperatorPrivilege BLOCK *********/
+
+ if ( is_disk_op )
+ become_root();
+
+ /* FIXME: use libnetconf here - gd */
+
+ ret = smbrun(command, NULL, NULL);
+ if (ret == 0) {
+ /* Tell everyone we updated smb.conf. */
+ messaging_send_all(p->msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0);
+ }
+
+ if ( is_disk_op )
+ unbecome_root();
+
+ /********* END SeDiskOperatorPrivilege BLOCK *********/
+
+ DEBUG(3,("_srvsvc_NetShareAdd: Running [%s] returned (%d)\n",
+ command, ret ));
+
+ TALLOC_FREE(command);
+
+ if ( ret != 0 )
+ return WERR_ACCESS_DENIED;
+
+ if (psd) {
+ NTSTATUS status;
+ /* Note we use share_name here, not share_name_in as
+ we need a canonicalized name for setting security. */
+ status = set_share_security(share_name, psd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("_srvsvc_NetShareAdd: Failed to add security info to share %s.\n",
+ share_name ));
+ }
+ }
+
+ /*
+ * We don't call reload_services() here, the message will
+ * cause this to be done before the next packet is read
+ * from the client. JRA.
+ */
+
+ DEBUG(5,("_srvsvc_NetShareAdd: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareDel
+ Call "delete share command" with the share name as
+ a parameter.
+********************************************************************/
+
+WERROR _srvsvc_NetShareDel(struct pipes_struct *p,
+ struct srvsvc_NetShareDel *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ char *command = NULL;
+ char *share_name = NULL;
+ int ret;
+ int snum;
+ bool is_disk_op;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ DEBUG(5,("_srvsvc_NetShareDel: %d\n", __LINE__));
+
+ if (!r->in.share_name) {
+ return WERR_NERR_NETNAMENOTFOUND;
+ }
+
+ if ( strequal(r->in.share_name,"IPC$")
+ || ( lp_enable_asu_support() && strequal(r->in.share_name,"ADMIN$") )
+ || strequal(r->in.share_name,"global") )
+ {
+ return WERR_ACCESS_DENIED;
+ }
+
+ snum = find_service(talloc_tos(), r->in.share_name, &share_name);
+ if (!share_name) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (snum < 0) {
+ return WERR_BAD_NET_NAME;
+ }
+
+ /* No change to printer shares. */
+ if (lp_printable(snum))
+ return WERR_ACCESS_DENIED;
+
+ is_disk_op = security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_DISK_OPERATOR);
+
+ if (session_info->unix_token->uid != sec_initial_uid() && !is_disk_op) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!lp_delete_share_command(talloc_tos(), lp_sub) || !*lp_delete_share_command(talloc_tos(), lp_sub)) {
+ DBG_WARNING("_srvsvc_NetShareDel: No \"delete share command\" parameter set in smb.conf.\n");
+ return WERR_ACCESS_DENIED;
+ }
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\" \"%s\"",
+ lp_delete_share_command(talloc_tos(), lp_sub),
+ get_dyn_CONFIGFILE(),
+ share_name);
+ if (!command) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ DEBUG(10,("_srvsvc_NetShareDel: Running [%s]\n", command ));
+
+ /********* BEGIN SeDiskOperatorPrivilege BLOCK *********/
+
+ if ( is_disk_op )
+ become_root();
+
+ ret = smbrun(command, NULL, NULL);
+ if (ret == 0) {
+ /* Tell everyone we updated smb.conf. */
+ messaging_send_all(p->msg_ctx, MSG_SMB_CONF_UPDATED, NULL, 0);
+ }
+
+ if ( is_disk_op )
+ unbecome_root();
+
+ /********* END SeDiskOperatorPrivilege BLOCK *********/
+
+ DEBUG(3,("_srvsvc_NetShareDel: Running [%s] returned (%d)\n", command, ret ));
+
+ if ( ret != 0 )
+ return WERR_ACCESS_DENIED;
+
+ /* Delete the SD in the database. */
+ delete_share_security(share_name);
+
+ lp_killservice(snum);
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareDelSticky
+********************************************************************/
+
+WERROR _srvsvc_NetShareDelSticky(struct pipes_struct *p,
+ struct srvsvc_NetShareDelSticky *r)
+{
+ struct srvsvc_NetShareDel q;
+
+ DEBUG(5,("_srvsvc_NetShareDelSticky: %d\n", __LINE__));
+
+ q.in.server_unc = r->in.server_unc;
+ q.in.share_name = r->in.share_name;
+ q.in.reserved = r->in.reserved;
+
+ return _srvsvc_NetShareDel(p, &q);
+}
+
+/*******************************************************************
+ _srvsvc_NetRemoteTOD
+********************************************************************/
+
+WERROR _srvsvc_NetRemoteTOD(struct pipes_struct *p,
+ struct srvsvc_NetRemoteTOD *r)
+{
+ struct srvsvc_NetRemoteTODInfo *tod;
+ struct tm *t;
+ time_t unixdate = time(NULL);
+
+ /* We do this call first as if we do it *after* the gmtime call
+ it overwrites the pointed-to values. JRA */
+
+ uint32_t zone = get_time_zone(unixdate)/60;
+
+ DEBUG(5,("_srvsvc_NetRemoteTOD: %d\n", __LINE__));
+
+ if ( !(tod = talloc_zero(p->mem_ctx, struct srvsvc_NetRemoteTODInfo)) )
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ *r->out.info = tod;
+
+ DEBUG(5,("_srvsvc_NetRemoteTOD: %d\n", __LINE__));
+
+ t = gmtime(&unixdate);
+
+ /* set up the */
+ tod->elapsed = unixdate;
+ tod->msecs = 0;
+ tod->hours = t->tm_hour;
+ tod->mins = t->tm_min;
+ tod->secs = t->tm_sec;
+ tod->hunds = 0;
+ tod->timezone = zone;
+ tod->tinterval = 10000;
+ tod->day = t->tm_mday;
+ tod->month = t->tm_mon + 1;
+ tod->year = 1900+t->tm_year;
+ tod->weekday = t->tm_wday;
+
+ DEBUG(5,("_srvsvc_NetRemoteTOD: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/***********************************************************************************
+ _srvsvc_NetGetFileSecurity
+ Win9x NT tools get security descriptor.
+***********************************************************************************/
+
+WERROR _srvsvc_NetGetFileSecurity(struct pipes_struct *p,
+ struct srvsvc_NetGetFileSecurity *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct smb_filename *smb_fname = NULL;
+ size_t sd_size;
+ char *servicename = NULL;
+ SMB_STRUCT_STAT st;
+ NTSTATUS nt_status;
+ WERROR werr;
+ struct conn_struct_tos *c = NULL;
+ connection_struct *conn = NULL;
+ struct sec_desc_buf *sd_buf = NULL;
+ struct files_struct *dirfsp = NULL;
+ files_struct *fsp = NULL;
+ int snum;
+ uint32_t ucf_flags = 0;
+ NTTIME twrp = 0;
+
+ ZERO_STRUCT(st);
+
+ if (!r->in.share) {
+ werr = WERR_NERR_NETNAMENOTFOUND;
+ goto error_exit;
+ }
+ snum = find_service(frame, r->in.share, &servicename);
+ if (!servicename) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto error_exit;
+ }
+ if (snum == -1) {
+ DEBUG(10, ("Could not find service %s\n", servicename));
+ werr = WERR_NERR_NETNAMENOTFOUND;
+ goto error_exit;
+ }
+
+ nt_status = create_conn_struct_tos_cwd(global_messaging_context(),
+ snum,
+ lp_path(frame, lp_sub, snum),
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(10, ("create_conn_struct failed: %s\n",
+ nt_errstr(nt_status)));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+ conn = c->conn;
+
+ nt_status = filename_convert_dirfsp(frame,
+ conn,
+ r->in.file,
+ ucf_flags,
+ twrp,
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ nt_status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ NULL, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ FILE_READ_ATTRIBUTES, /* access_mask */
+ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("_srvsvc_NetGetFileSecurity: can't open %s\n",
+ smb_fname_str_dbg(smb_fname)));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ sd_buf = talloc_zero(p->mem_ctx, struct sec_desc_buf);
+ if (!sd_buf) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto error_exit;
+ }
+
+ nt_status = SMB_VFS_FGET_NT_ACL(metadata_fsp(fsp),
+ (SECINFO_OWNER
+ |SECINFO_GROUP
+ |SECINFO_DACL), sd_buf, &sd_buf->sd);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("_srvsvc_NetGetFileSecurity: Unable to get NT ACL "
+ "for file %s\n", smb_fname_str_dbg(smb_fname)));
+ werr = ntstatus_to_werror(nt_status);
+ TALLOC_FREE(sd_buf);
+ goto error_exit;
+ }
+
+ if (sd_buf->sd->dacl) {
+ sd_buf->sd->dacl->revision = NT4_ACL_REVISION;
+ }
+
+ sd_size = ndr_size_security_descriptor(sd_buf->sd, 0);
+
+ sd_buf->sd_size = sd_size;
+
+ *r->out.sd_buf = sd_buf;
+
+ werr = WERR_OK;
+
+error_exit:
+
+ if (fsp) {
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+
+ TALLOC_FREE(frame);
+ return werr;
+}
+
+/***********************************************************************************
+ _srvsvc_NetSetFileSecurity
+ Win9x NT tools set security descriptor.
+***********************************************************************************/
+
+WERROR _srvsvc_NetSetFileSecurity(struct pipes_struct *p,
+ struct srvsvc_NetSetFileSecurity *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct smb_filename *smb_fname = NULL;
+ char *servicename = NULL;
+ struct files_struct *dirfsp = NULL;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ NTSTATUS nt_status;
+ WERROR werr;
+ struct conn_struct_tos *c = NULL;
+ connection_struct *conn = NULL;
+ int snum;
+ struct security_descriptor *psd = NULL;
+ uint32_t security_info_sent = 0;
+ uint32_t ucf_flags = 0;
+ NTTIME twrp = 0;
+
+ ZERO_STRUCT(st);
+
+ if (!r->in.share) {
+ werr = WERR_NERR_NETNAMENOTFOUND;
+ goto error_exit;
+ }
+
+ snum = find_service(frame, r->in.share, &servicename);
+ if (!servicename) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto error_exit;
+ }
+
+ if (snum == -1) {
+ DEBUG(10, ("Could not find service %s\n", servicename));
+ werr = WERR_NERR_NETNAMENOTFOUND;
+ goto error_exit;
+ }
+
+ nt_status = create_conn_struct_tos_cwd(global_messaging_context(),
+ snum,
+ lp_path(frame, lp_sub, snum),
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(10, ("create_conn_struct failed: %s\n",
+ nt_errstr(nt_status)));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+ conn = c->conn;
+
+ nt_status = filename_convert_dirfsp(frame,
+ conn,
+ r->in.file,
+ ucf_flags,
+ twrp,
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ nt_status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ NULL, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ FILE_WRITE_ATTRIBUTES, /* access_mask */
+ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("_srvsvc_NetSetFileSecurity: can't open %s\n",
+ smb_fname_str_dbg(smb_fname)));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ psd = r->in.sd_buf->sd;
+ security_info_sent = r->in.securityinformation;
+
+ nt_status = set_sd(fsp, psd, security_info_sent);
+
+ if (!NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(3,("_srvsvc_NetSetFileSecurity: Unable to set NT ACL "
+ "on file %s\n", r->in.share));
+ werr = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ werr = WERR_OK;
+
+error_exit:
+
+ if (fsp) {
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+
+ TALLOC_FREE(frame);
+ return werr;
+}
+
+/***********************************************************************************
+ It may be that we want to limit users to creating shares on certain areas of the UNIX file area.
+ We could define areas by mapping Windows style disks to points on the UNIX directory hierarchy.
+ These disks would the disks listed by this function.
+ Users could then create shares relative to these disks. Watch out for moving these disks around.
+ "Nigel Williams" <nigel@veritas.com>.
+***********************************************************************************/
+
+static const char *server_disks[] = {"C:"};
+
+static uint32_t get_server_disk_count(void)
+{
+ return sizeof(server_disks)/sizeof(server_disks[0]);
+}
+
+static uint32_t init_server_disk_enum(uint32_t *resume)
+{
+ uint32_t server_disk_count = get_server_disk_count();
+
+ /*resume can be an offset into the list for now*/
+
+ if(*resume & 0x80000000)
+ *resume = 0;
+
+ if(*resume > server_disk_count)
+ *resume = server_disk_count;
+
+ return server_disk_count - *resume;
+}
+
+static const char *next_server_disk_enum(uint32_t *resume)
+{
+ const char *disk;
+
+ if(init_server_disk_enum(resume) == 0)
+ return NULL;
+
+ disk = server_disks[*resume];
+
+ (*resume)++;
+
+ DEBUG(10, ("next_server_disk_enum: reporting disk %s. resume handle %d.\n", disk, *resume));
+
+ return disk;
+}
+
+/********************************************************************
+ _srvsvc_NetDiskEnum
+********************************************************************/
+
+WERROR _srvsvc_NetDiskEnum(struct pipes_struct *p,
+ struct srvsvc_NetDiskEnum *r)
+{
+ uint32_t i;
+ const char *disk_name;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ WERROR werr;
+ uint32_t resume = r->in.resume_handle ? *r->in.resume_handle : 0;
+
+ werr = WERR_OK;
+
+ *r->out.totalentries = init_server_disk_enum(&resume);
+
+ r->out.info->disks = talloc_zero_array(ctx, struct srvsvc_NetDiskInfo0,
+ MAX_SERVER_DISK_ENTRIES);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks);
+
+ /*allow one struct srvsvc_NetDiskInfo0 for null terminator*/
+
+ r->out.info->count = 0;
+
+ for(i = 0; i < MAX_SERVER_DISK_ENTRIES -1 && (disk_name = next_server_disk_enum(&resume)); i++) {
+
+ r->out.info->count++;
+
+ /*copy disk name into a unicode string*/
+
+ r->out.info->disks[i].disk = talloc_strdup(ctx, disk_name);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks[i].disk);
+ }
+
+ /* add a terminating null string. Is this there if there is more data to come? */
+
+ r->out.info->count++;
+
+ r->out.info->disks[i].disk = talloc_strdup(ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks[i].disk);
+
+ if (r->out.resume_handle) {
+ *r->out.resume_handle = resume;
+ }
+
+ return werr;
+}
+
+/********************************************************************
+ _srvsvc_NetNameValidate
+********************************************************************/
+
+WERROR _srvsvc_NetNameValidate(struct pipes_struct *p,
+ struct srvsvc_NetNameValidate *r)
+{
+ switch (r->in.name_type) {
+ case 0x9:
+ if (!validate_net_name(r->in.name, INVALID_SHARENAME_CHARS,
+ strlen_m(r->in.name)))
+ {
+ DEBUG(5,("_srvsvc_NetNameValidate: Bad sharename \"%s\"\n",
+ r->in.name));
+ return WERR_INVALID_NAME;
+ }
+ break;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+********************************************************************/
+
+struct enum_file_close_state {
+ struct srvsvc_NetFileClose *r;
+ struct messaging_context *msg_ctx;
+};
+
+static int enum_file_close_fn(struct file_id id,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ void *private_data)
+{
+ char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+ struct enum_file_close_state *state =
+ (struct enum_file_close_state *)private_data;
+ uint32_t fid = (((uint32_t)(procid_to_pid(&e->pid))<<16) | e->share_file_id);
+
+ if (fid != state->r->in.fid) {
+ return 0; /* Not this file. */
+ }
+
+ if (!process_exists(e->pid) ) {
+ return 0;
+ }
+
+ /* Ok - send the close message. */
+ DBG_DEBUG("request to close file %s, %s\n", d->servicepath,
+ share_mode_str(talloc_tos(), 0, &id, e));
+
+ share_mode_entry_to_message(msg, &id, e);
+
+ state->r->out.result = ntstatus_to_werror(
+ messaging_send_buf(state->msg_ctx,
+ e->pid, MSG_SMB_CLOSE_FILE,
+ (uint8_t *)msg, sizeof(msg)));
+
+ return 0;
+}
+
+/********************************************************************
+ Close a file given a 32-bit file id.
+********************************************************************/
+
+WERROR _srvsvc_NetFileClose(struct pipes_struct *p,
+ struct srvsvc_NetFileClose *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct enum_file_close_state state;
+ bool is_disk_op;
+
+ DEBUG(5,("_srvsvc_NetFileClose: %d\n", __LINE__));
+
+ is_disk_op = security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_DISK_OPERATOR);
+
+ if (session_info->unix_token->uid != sec_initial_uid() && !is_disk_op) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* enum_file_close_fn sends the close message to
+ * the relevant smbd process. */
+
+ r->out.result = WERR_FILE_NOT_FOUND;
+ state.r = r;
+ state.msg_ctx = p->msg_ctx;
+ share_entry_forall(enum_file_close_fn, &state);
+ return r->out.result;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _srvsvc_NetCharDevEnum(struct pipes_struct *p,
+ struct srvsvc_NetCharDevEnum *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevGetInfo(struct pipes_struct *p,
+ struct srvsvc_NetCharDevGetInfo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevControl(struct pipes_struct *p,
+ struct srvsvc_NetCharDevControl *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQEnum(struct pipes_struct *p,
+ struct srvsvc_NetCharDevQEnum *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQGetInfo(struct pipes_struct *p,
+ struct srvsvc_NetCharDevQGetInfo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQSetInfo(struct pipes_struct *p,
+ struct srvsvc_NetCharDevQSetInfo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQPurge(struct pipes_struct *p,
+ struct srvsvc_NetCharDevQPurge *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQPurgeSelf(struct pipes_struct *p,
+ struct srvsvc_NetCharDevQPurgeSelf *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetFileGetInfo(struct pipes_struct *p,
+ struct srvsvc_NetFileGetInfo *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetShareCheck(struct pipes_struct *p,
+ struct srvsvc_NetShareCheck *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetServerStatisticsGet(struct pipes_struct *p,
+ struct srvsvc_NetServerStatisticsGet *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetTransportAdd(struct pipes_struct *p,
+ struct srvsvc_NetTransportAdd *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetTransportEnum(struct pipes_struct *p,
+ struct srvsvc_NetTransportEnum *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetTransportDel(struct pipes_struct *p,
+ struct srvsvc_NetTransportDel *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetSetServiceBits(struct pipes_struct *p,
+ struct srvsvc_NetSetServiceBits *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPathType(struct pipes_struct *p,
+ struct srvsvc_NetPathType *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPathCanonicalize(struct pipes_struct *p,
+ struct srvsvc_NetPathCanonicalize *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPathCompare(struct pipes_struct *p,
+ struct srvsvc_NetPathCompare *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRPRNAMECANONICALIZE(struct pipes_struct *p,
+ struct srvsvc_NETRPRNAMECANONICALIZE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPRNameCompare(struct pipes_struct *p,
+ struct srvsvc_NetPRNameCompare *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetShareDelStart(struct pipes_struct *p,
+ struct srvsvc_NetShareDelStart *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetShareDelCommit(struct pipes_struct *p,
+ struct srvsvc_NetShareDelCommit *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetServerTransportAddEx(struct pipes_struct *p,
+ struct srvsvc_NetServerTransportAddEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetServerSetServiceBitsEx(struct pipes_struct *p,
+ struct srvsvc_NetServerSetServiceBitsEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSGETVERSION(struct pipes_struct *p,
+ struct srvsvc_NETRDFSGETVERSION *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSCREATELOCALPARTITION(struct pipes_struct *p,
+ struct srvsvc_NETRDFSCREATELOCALPARTITION *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSDELETELOCALPARTITION(struct pipes_struct *p,
+ struct srvsvc_NETRDFSDELETELOCALPARTITION *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSSETLOCALVOLUMESTATE(struct pipes_struct *p,
+ struct srvsvc_NETRDFSSETLOCALVOLUMESTATE *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSSETSERVERINFO(struct pipes_struct *p,
+ struct srvsvc_NETRDFSSETSERVERINFO *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSCREATEEXITPOINT(struct pipes_struct *p,
+ struct srvsvc_NETRDFSCREATEEXITPOINT *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSDELETEEXITPOINT(struct pipes_struct *p,
+ struct srvsvc_NETRDFSDELETEEXITPOINT *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSMODIFYPREFIX(struct pipes_struct *p,
+ struct srvsvc_NETRDFSMODIFYPREFIX *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSFIXLOCALVOLUME(struct pipes_struct *p,
+ struct srvsvc_NETRDFSFIXLOCALVOLUME *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSMANAGERREPORTSITEINFO(struct pipes_struct *p,
+ struct srvsvc_NETRDFSMANAGERREPORTSITEINFO *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRSERVERTRANSPORTDELEX(struct pipes_struct *p,
+ struct srvsvc_NETRSERVERTRANSPORTDELEX *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_srvsvc_scompat.c"
diff --git a/source3/rpc_server/svcctl/srv_svcctl_nt.c b/source3/rpc_server/svcctl/srv_svcctl_nt.c
new file mode 100644
index 0000000..c1df2f6
--- /dev/null
+++ b/source3/rpc_server/svcctl/srv_svcctl_nt.c
@@ -0,0 +1,1497 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005.
+ *
+ * Largely Rewritten (Again) by:
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ * Copyright (C) Guenther Deschner 2008,2009.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/passwd.h" /* uid_wrapper */
+#include "ntdomain.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "services/services.h"
+#include "services/svc_winreg_glue.h"
+#include "auth.h"
+#include "rpc_server/svcctl/srv_svcctl_nt.h"
+
+#include "rpc_server/rpc_server.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_svcctl.h"
+#include "librpc/gen_ndr/ndr_svcctl_scompat.h"
+#include "srv_svcctl_reg.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+struct service_control_op {
+ const char *name;
+ SERVICE_CONTROL_OPS *ops;
+};
+
+/* handle external services */
+extern SERVICE_CONTROL_OPS rcinit_svc_ops;
+
+/* builtin services (see service_db.c and services/svc_*.c */
+extern SERVICE_CONTROL_OPS spoolss_svc_ops;
+extern SERVICE_CONTROL_OPS netlogon_svc_ops;
+extern SERVICE_CONTROL_OPS winreg_svc_ops;
+extern SERVICE_CONTROL_OPS wins_svc_ops;
+
+/* make sure this number patches the number of builtin
+ SERVICE_CONTROL_OPS structure listed above */
+
+#define SVCCTL_NUM_INTERNAL_SERVICES 4
+
+struct service_control_op *svcctl_ops;
+
+static const struct generic_mapping scm_generic_map =
+ { SC_MANAGER_READ_ACCESS, SC_MANAGER_WRITE_ACCESS, SC_MANAGER_EXECUTE_ACCESS, SC_MANAGER_ALL_ACCESS };
+static const struct generic_mapping svc_generic_map =
+ { SERVICE_READ_ACCESS, SERVICE_WRITE_ACCESS, SERVICE_EXECUTE_ACCESS, SERVICE_ALL_ACCESS };
+
+
+/********************************************************************
+********************************************************************/
+
+bool init_service_op_table( void )
+{
+ const char **service_list = lp_svcctl_list();
+ int num_services = SVCCTL_NUM_INTERNAL_SERVICES + str_list_length( service_list );
+ int i;
+
+ if ( !(svcctl_ops = talloc_array( NULL, struct service_control_op, num_services+1)) ) {
+ DEBUG(0,("init_service_op_table: talloc() failed!\n"));
+ return False;
+ }
+
+ /* services listed in smb.conf get the rc.init interface */
+
+ for ( i=0; service_list && service_list[i]; i++ ) {
+ svcctl_ops[i].name = talloc_strdup( svcctl_ops, service_list[i] );
+ svcctl_ops[i].ops = &rcinit_svc_ops;
+ }
+
+ /* add builtin services */
+
+ svcctl_ops[i].name = talloc_strdup( svcctl_ops, "Spooler" );
+ svcctl_ops[i].ops = &spoolss_svc_ops;
+ i++;
+
+ svcctl_ops[i].name = talloc_strdup( svcctl_ops, "NETLOGON" );
+ svcctl_ops[i].ops = &netlogon_svc_ops;
+ i++;
+
+ svcctl_ops[i].name = talloc_strdup( svcctl_ops, "RemoteRegistry" );
+ svcctl_ops[i].ops = &winreg_svc_ops;
+ i++;
+
+ svcctl_ops[i].name = talloc_strdup( svcctl_ops, "WINS" );
+ svcctl_ops[i].ops = &wins_svc_ops;
+ i++;
+
+ /* NULL terminate the array */
+
+ svcctl_ops[i].name = NULL;
+ svcctl_ops[i].ops = NULL;
+
+ return True;
+}
+
+bool shutdown_service_op_table(void)
+{
+ TALLOC_FREE(svcctl_ops);
+
+ return true;
+}
+
+/********************************************************************
+********************************************************************/
+
+static struct service_control_op* find_service_by_name( const char *name )
+{
+ int i;
+
+ for ( i=0; svcctl_ops[i].name; i++ ) {
+ if ( strequal( name, svcctl_ops[i].name ) )
+ return &svcctl_ops[i];
+ }
+
+ return NULL;
+}
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS svcctl_access_check( struct security_descriptor *sec_desc, struct security_token *token,
+ uint32_t access_desired, uint32_t *access_granted )
+{
+ NTSTATUS status;
+ if ( geteuid() == sec_initial_uid() ) {
+ DEBUG(5,("svcctl_access_check: using root's token\n"));
+ status = get_root_nt_token(&token);
+ if(!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return se_access_check( sec_desc, token, access_desired, access_granted);
+}
+
+/********************************************************************
+********************************************************************/
+
+static struct security_descriptor* construct_scm_sd( TALLOC_CTX *ctx )
+{
+ struct security_ace ace[2];
+ size_t i = 0;
+ struct security_descriptor *sd;
+ struct security_acl *theacl;
+ size_t sd_size;
+
+ /* basic access for Everyone */
+
+ init_sec_ace(&ace[i++], &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, SC_MANAGER_READ_ACCESS, 0);
+
+ /* Full Access 'BUILTIN\Administrators' */
+
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, SC_MANAGER_ALL_ACCESS, 0);
+
+
+ /* create the security descriptor */
+
+ if ( !(theacl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) )
+ return NULL;
+
+ if ( !(sd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL,
+ theacl, &sd_size)) )
+ return NULL;
+
+ return sd;
+}
+
+/******************************************************************
+ Find a registry key handle and return a SERVICE_INFO
+ *****************************************************************/
+
+static SERVICE_INFO *find_service_info_by_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd)
+{
+ SERVICE_INFO *service_info = NULL;
+ NTSTATUS status;
+
+ service_info = find_policy_by_hnd(p,
+ hnd,
+ DCESRV_HANDLE_ANY,
+ SERVICE_INFO,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("find_service_info_by_hnd: handle not found\n"));
+ return NULL;
+ }
+
+ return service_info;
+}
+
+/******************************************************************
+ *****************************************************************/
+
+static WERROR create_open_service_handle(struct pipes_struct *p,
+ struct policy_handle *handle,
+ uint32_t type,
+ const char *service,
+ uint32_t access_granted)
+{
+ SERVICE_INFO *info = NULL;
+ WERROR result = WERR_OK;
+ struct service_control_op *s_op;
+
+ if ( !(info = talloc_zero( NULL, SERVICE_INFO )) )
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ /* the Service Manager has a NULL name */
+
+ info->type = SVC_HANDLE_IS_SCM;
+
+ switch ( type ) {
+ case SVC_HANDLE_IS_SCM:
+ info->type = SVC_HANDLE_IS_SCM;
+ break;
+
+ case SVC_HANDLE_IS_DBLOCK:
+ info->type = SVC_HANDLE_IS_DBLOCK;
+ break;
+
+ case SVC_HANDLE_IS_SERVICE:
+ info->type = SVC_HANDLE_IS_SERVICE;
+
+ /* lookup the SERVICE_CONTROL_OPS */
+
+ if ( !(s_op = find_service_by_name( service )) ) {
+ result = WERR_SERVICE_DOES_NOT_EXIST;
+ goto done;
+ }
+
+ info->ops = s_op->ops;
+
+ if ( !(info->name = talloc_strdup( info, s_op->name )) ) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ break;
+
+ default:
+ result = WERR_SERVICE_DOES_NOT_EXIST;
+ goto done;
+ }
+
+ info->access_granted = access_granted;
+
+ /* store the SERVICE_INFO and create an open handle */
+
+ if ( !create_policy_hnd( p, handle, 0, info ) ) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+done:
+ if ( !W_ERROR_IS_OK(result) )
+ TALLOC_FREE(info);
+
+ return result;
+}
+
+/********************************************************************
+ _svcctl_OpenSCManagerW
+********************************************************************/
+
+WERROR _svcctl_OpenSCManagerW(struct pipes_struct *p,
+ struct svcctl_OpenSCManagerW *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *sec_desc;
+ uint32_t access_granted = 0;
+ NTSTATUS status;
+
+ /* perform access checks */
+
+ if ( !(sec_desc = construct_scm_sd( p->mem_ctx )) )
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ se_map_generic( &r->in.access_mask, &scm_generic_map );
+ status = svcctl_access_check( sec_desc, session_info->security_token,
+ r->in.access_mask, &access_granted );
+ if ( !NT_STATUS_IS_OK(status) )
+ return ntstatus_to_werror( status );
+
+ return create_open_service_handle( p, r->out.handle, SVC_HANDLE_IS_SCM, NULL, access_granted );
+}
+
+/********************************************************************
+ _svcctl_OpenServiceW
+********************************************************************/
+
+WERROR _svcctl_OpenServiceW(struct pipes_struct *p,
+ struct svcctl_OpenServiceW *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct security_descriptor *sec_desc;
+ uint32_t access_granted = 0;
+ NTSTATUS status;
+ const char *service = NULL;
+ WERROR err;
+
+ service = r->in.ServiceName;
+ if (!service) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ DEBUG(5, ("_svcctl_OpenServiceW: Attempting to open Service [%s], \n", service));
+
+ /* based on my tests you can open a service if you have a valid scm handle */
+
+ if ( !find_service_info_by_hnd( p, r->in.scmanager_handle) )
+ return WERR_INVALID_HANDLE;
+
+ /*
+ * Perform access checks. Use the system session_info in order to ensure
+ * that we retrieve the security descriptor
+ */
+ err = svcctl_get_secdesc(p->msg_ctx,
+ get_session_info_system(),
+ service,
+ p->mem_ctx,
+ &sec_desc);
+ if (W_ERROR_EQUAL(err, WERR_FILE_NOT_FOUND)) {
+ DBG_NOTICE("service %s does not exist\n", service);
+ return WERR_SERVICE_DOES_NOT_EXIST;
+ }
+ if (!W_ERROR_IS_OK(err)) {
+ DBG_NOTICE("Failed to get a valid secdesc for %s: %s\n",
+ service, win_errstr(err));
+ return err;
+ }
+
+ se_map_generic( &r->in.access_mask, &svc_generic_map );
+ status = svcctl_access_check( sec_desc, session_info->security_token,
+ r->in.access_mask, &access_granted );
+ if ( !NT_STATUS_IS_OK(status) )
+ return ntstatus_to_werror( status );
+
+ return create_open_service_handle( p, r->out.handle, SVC_HANDLE_IS_SERVICE, service, access_granted );
+}
+
+/********************************************************************
+ _svcctl_CloseServiceHandle
+********************************************************************/
+
+WERROR _svcctl_CloseServiceHandle(struct pipes_struct *p,
+ struct svcctl_CloseServiceHandle *r)
+{
+ if ( !close_policy_hnd( p, r->in.handle ) )
+ return WERR_INVALID_HANDLE;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_GetServiceDisplayNameW
+********************************************************************/
+
+WERROR _svcctl_GetServiceDisplayNameW(struct pipes_struct *p,
+ struct svcctl_GetServiceDisplayNameW *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const char *service;
+ const char *display_name;
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+
+ /* can only use an SCM handle here */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SCM) )
+ return WERR_INVALID_HANDLE;
+
+ service = r->in.service_name;
+
+ display_name = svcctl_lookup_dispname(p->mem_ctx,
+ p->msg_ctx,
+ session_info,
+ service);
+ if (!display_name) {
+ display_name = "";
+ }
+
+ *r->out.display_name = display_name;
+ *r->out.display_name_length = strlen(display_name);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_QueryServiceStatus
+********************************************************************/
+
+WERROR _svcctl_QueryServiceStatus(struct pipes_struct *p,
+ struct svcctl_QueryServiceStatus *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_STATUS) )
+ return WERR_ACCESS_DENIED;
+
+ /* try the service specific status call */
+
+ return info->ops->service_status( info->name, r->out.service_status );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int enumerate_status(TALLOC_CTX *ctx,
+ struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ struct ENUM_SERVICE_STATUSW **status)
+{
+ int num_services = 0;
+ int i;
+ struct ENUM_SERVICE_STATUSW *st;
+ const char *display_name;
+
+ /* just count */
+ while ( svcctl_ops[num_services].name )
+ num_services++;
+
+ if ( !(st = talloc_array( ctx, struct ENUM_SERVICE_STATUSW, num_services )) ) {
+ DEBUG(0,("enumerate_status: talloc() failed!\n"));
+ return -1;
+ }
+
+ for ( i=0; i<num_services; i++ ) {
+ st[i].service_name = talloc_strdup(st, svcctl_ops[i].name );
+
+ display_name = svcctl_lookup_dispname(ctx,
+ msg_ctx,
+ session_info,
+ svcctl_ops[i].name);
+ st[i].display_name = talloc_strdup(st, display_name ? display_name : "");
+
+ svcctl_ops[i].ops->service_status( svcctl_ops[i].name, &st[i].status );
+ }
+
+ *status = st;
+
+ return num_services;
+}
+
+/********************************************************************
+ _svcctl_EnumServicesStatusW
+********************************************************************/
+
+WERROR _svcctl_EnumServicesStatusW(struct pipes_struct *p,
+ struct svcctl_EnumServicesStatusW *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct ENUM_SERVICE_STATUSW *services = NULL;
+ int num_services;
+ int i = 0;
+ size_t buffer_size = 0;
+ WERROR result = WERR_OK;
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ DATA_BLOB blob = data_blob_null;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SCM) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_MGR_ENUMERATE_SERVICE) ) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ num_services = enumerate_status(p->mem_ctx,
+ p->msg_ctx,
+ session_info,
+ &services);
+ if (num_services == -1 ) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for ( i=0; i<num_services; i++ ) {
+ buffer_size += ndr_size_ENUM_SERVICE_STATUSW(&services[i], 0);
+ }
+
+ buffer_size += buffer_size % 4;
+
+ if (buffer_size > r->in.offered) {
+ num_services = 0;
+ result = WERR_MORE_DATA;
+ }
+
+ if ( W_ERROR_IS_OK(result) ) {
+
+ enum ndr_err_code ndr_err;
+ struct ndr_push *ndr;
+
+ ndr = ndr_push_init_ctx(p->mem_ctx);
+ if (ndr == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ndr_err = ndr_push_ENUM_SERVICE_STATUSW_array(
+ ndr, num_services, services);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ntstatus_to_werror(ndr_map_error2ntstatus(ndr_err));
+ }
+ blob = ndr_push_blob(ndr);
+ memcpy(r->out.service, blob.data, MIN(blob.length, r->in.offered));
+ }
+
+ *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered;
+ *r->out.services_returned = (uint32_t)num_services;
+ if (r->out.resume_handle) {
+ *r->out.resume_handle = 0;
+ }
+
+ return result;
+}
+
+/********************************************************************
+ _svcctl_StartServiceW
+********************************************************************/
+
+WERROR _svcctl_StartServiceW(struct pipes_struct *p,
+ struct svcctl_StartServiceW *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_START) )
+ return WERR_ACCESS_DENIED;
+
+ return info->ops->start_service( info->name );
+}
+
+/********************************************************************
+ _svcctl_ControlService
+********************************************************************/
+
+WERROR _svcctl_ControlService(struct pipes_struct *p,
+ struct svcctl_ControlService *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ switch ( r->in.control ) {
+ case SVCCTL_CONTROL_STOP:
+ if ( !(info->access_granted & SC_RIGHT_SVC_STOP) )
+ return WERR_ACCESS_DENIED;
+
+ return info->ops->stop_service( info->name,
+ r->out.service_status );
+
+ case SVCCTL_CONTROL_INTERROGATE:
+ if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_STATUS) )
+ return WERR_ACCESS_DENIED;
+
+ return info->ops->service_status( info->name,
+ r->out.service_status );
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+}
+
+/********************************************************************
+ _svcctl_EnumDependentServicesW
+********************************************************************/
+
+WERROR _svcctl_EnumDependentServicesW(struct pipes_struct *p,
+ struct svcctl_EnumDependentServicesW *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.service );
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_ENUMERATE_DEPENDENTS) )
+ return WERR_ACCESS_DENIED;
+
+ switch (r->in.state) {
+ case SERVICE_STATE_ACTIVE:
+ case SERVICE_STATE_INACTIVE:
+ case SERVICE_STATE_ALL:
+ break;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* we have to set the outgoing buffer size to the same as the
+ incoming buffer size (even in the case of failure */
+ /* this is done in the autogenerated server already - gd */
+
+ *r->out.needed = r->in.offered;
+
+ /* no dependent services...basically a stub function */
+ *r->out.services_returned = 0;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_QueryServiceStatusEx
+********************************************************************/
+
+WERROR _svcctl_QueryServiceStatusEx(struct pipes_struct *p,
+ struct svcctl_QueryServiceStatusEx *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ uint32_t buffer_size;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_STATUS) )
+ return WERR_ACCESS_DENIED;
+
+ /* we have to set the outgoing buffer size to the same as the
+ incoming buffer size (even in the case of failure) */
+ *r->out.needed = r->in.offered;
+
+ switch ( r->in.info_level ) {
+ case SVC_STATUS_PROCESS_INFO:
+ {
+ struct SERVICE_STATUS_PROCESS svc_stat_proc;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+
+ /* Get the status of the service.. */
+ info->ops->service_status( info->name, &svc_stat_proc.status );
+ svc_stat_proc.process_id = getpid();
+ svc_stat_proc.service_flags = 0x0;
+
+ ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &svc_stat_proc,
+ (ndr_push_flags_fn_t)ndr_push_SERVICE_STATUS_PROCESS);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ r->out.buffer = blob.data;
+ buffer_size = sizeof(struct SERVICE_STATUS_PROCESS);
+ break;
+ }
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+
+ buffer_size += buffer_size % 4;
+ *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered;
+
+ if (buffer_size > r->in.offered ) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR fill_svc_config(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ struct auth_session_info *session_info,
+ const char *name,
+ struct QUERY_SERVICE_CONFIG *config)
+{
+ const char *result = NULL;
+
+ /* now fill in the individual values */
+
+ ZERO_STRUCTP(config);
+
+ config->displayname = svcctl_lookup_dispname(mem_ctx,
+ msg_ctx,
+ session_info,
+ name);
+
+ result = svcctl_get_string_value(mem_ctx,
+ msg_ctx,
+ session_info,
+ name,
+ "ObjectName");
+ if (result != NULL) {
+ config->startname = result;
+ }
+
+ result = svcctl_get_string_value(mem_ctx,
+ msg_ctx,
+ session_info,
+ name,
+ "ImagePath");
+ if (result != NULL) {
+ config->executablepath = result;
+ }
+
+ /* a few hard coded values */
+ /* loadordergroup and dependencies are empty */
+
+ config->tag_id = 0x00000000; /* unassigned loadorder group */
+ config->service_type = SERVICE_TYPE_WIN32_OWN_PROCESS;
+ config->error_control = SVCCTL_SVC_ERROR_NORMAL;
+
+ /* set the start type. NetLogon and WINS are disabled to prevent
+ the client from showing the "Start" button (if of course the services
+ are not running */
+
+ if ( strequal( name, "NETLOGON" ) && ( lp_servicenumber(name) == -1 ) )
+ config->start_type = SVCCTL_DISABLED;
+ else if ( strequal( name, "WINS" ) && ( !lp_we_are_a_wins_server() ))
+ config->start_type = SVCCTL_DISABLED;
+ else
+ config->start_type = SVCCTL_DEMAND_START;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_QueryServiceConfigW
+********************************************************************/
+
+WERROR _svcctl_QueryServiceConfigW(struct pipes_struct *p,
+ struct svcctl_QueryServiceConfigW *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ uint32_t buffer_size;
+ WERROR wresult;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_CONFIG) )
+ return WERR_ACCESS_DENIED;
+
+ /* we have to set the outgoing buffer size to the same as the
+ incoming buffer size (even in the case of failure */
+
+ *r->out.needed = r->in.offered;
+
+ wresult = fill_svc_config(p->mem_ctx,
+ p->msg_ctx,
+ session_info,
+ info->name,
+ r->out.query);
+ if ( !W_ERROR_IS_OK(wresult) )
+ return wresult;
+
+ buffer_size = ndr_size_QUERY_SERVICE_CONFIG(r->out.query, 0);
+ *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered;
+
+ if (buffer_size > r->in.offered ) {
+ ZERO_STRUCTP(r->out.query);
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_QueryServiceConfig2W
+********************************************************************/
+
+WERROR _svcctl_QueryServiceConfig2W(struct pipes_struct *p,
+ struct svcctl_QueryServiceConfig2W *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ uint32_t buffer_size;
+ DATA_BLOB blob = data_blob_null;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_CONFIG) )
+ return WERR_ACCESS_DENIED;
+
+ /* we have to set the outgoing buffer size to the same as the
+ incoming buffer size (even in the case of failure */
+ *r->out.needed = r->in.offered;
+
+ switch ( r->in.info_level ) {
+ case SERVICE_CONFIG_DESCRIPTION:
+ {
+ struct SERVICE_DESCRIPTION desc_buf;
+ const char *description;
+ enum ndr_err_code ndr_err;
+
+ description = svcctl_lookup_description(p->mem_ctx,
+ p->msg_ctx,
+ session_info,
+ info->name);
+
+ desc_buf.description = description;
+
+ ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &desc_buf,
+ (ndr_push_flags_fn_t)ndr_push_SERVICE_DESCRIPTION);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+ break;
+ case SERVICE_CONFIG_FAILURE_ACTIONS:
+ {
+ struct SERVICE_FAILURE_ACTIONSW actions;
+ enum ndr_err_code ndr_err;
+
+ /* nothing to say...just service the request */
+
+ ZERO_STRUCT( actions );
+
+ ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &actions,
+ (ndr_push_flags_fn_t)ndr_push_SERVICE_FAILURE_ACTIONSW);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+ break;
+
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ buffer_size = blob.length;
+ buffer_size += buffer_size % 4;
+ *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered;
+
+ if (buffer_size > r->in.offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ memcpy(r->out.buffer, blob.data, blob.length);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_LockServiceDatabase
+********************************************************************/
+
+WERROR _svcctl_LockServiceDatabase(struct pipes_struct *p,
+ struct svcctl_LockServiceDatabase *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SCM) )
+ return WERR_INVALID_HANDLE;
+
+ if ( !(info->access_granted & SC_RIGHT_MGR_LOCK) )
+ return WERR_ACCESS_DENIED;
+
+ /* Just open a handle. Doesn't actually lock anything */
+
+ return create_open_service_handle( p, r->out.lock, SVC_HANDLE_IS_DBLOCK, NULL, 0 );
+}
+
+/********************************************************************
+ _svcctl_UnlockServiceDatabase
+********************************************************************/
+
+WERROR _svcctl_UnlockServiceDatabase(struct pipes_struct *p,
+ struct svcctl_UnlockServiceDatabase *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.lock );
+
+
+ if ( !info || (info->type != SVC_HANDLE_IS_DBLOCK) )
+ return WERR_INVALID_HANDLE;
+
+ return close_policy_hnd( p, r->out.lock) ? WERR_OK : WERR_INVALID_HANDLE;
+}
+
+/********************************************************************
+ _svcctl_QueryServiceObjectSecurity
+********************************************************************/
+
+WERROR _svcctl_QueryServiceObjectSecurity(struct pipes_struct *p,
+ struct svcctl_QueryServiceObjectSecurity *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ struct security_descriptor *sec_desc;
+ NTSTATUS status;
+ uint8_t *buffer = NULL;
+ size_t len = 0;
+ WERROR err;
+
+
+ /* only support the SCM and individual services */
+
+ if ( !info || !(info->type & (SVC_HANDLE_IS_SERVICE|SVC_HANDLE_IS_SCM)) )
+ return WERR_INVALID_HANDLE;
+
+ /* check access reights (according to MSDN) */
+
+ if ( !(info->access_granted & SEC_STD_READ_CONTROL) )
+ return WERR_ACCESS_DENIED;
+
+ /* TODO: handle something besides SECINFO_DACL */
+
+ if ( (r->in.security_flags & SECINFO_DACL) != SECINFO_DACL )
+ return WERR_INVALID_PARAMETER;
+
+ /* Lookup the security descriptor and marshall it up for a reply */
+ err = svcctl_get_secdesc(p->msg_ctx,
+ get_session_info_system(),
+ info->name,
+ p->mem_ctx,
+ &sec_desc);
+ if (W_ERROR_EQUAL(err, WERR_FILE_NOT_FOUND)) {
+ DBG_NOTICE("service %s does not exist\n", info->name);
+ return WERR_SERVICE_DOES_NOT_EXIST;
+ }
+ if (!W_ERROR_IS_OK(err)) {
+ DBG_NOTICE("Failed to get a valid secdesc for %s: %s\n",
+ info->name, win_errstr(err));
+ return err;
+ }
+
+ *r->out.needed = ndr_size_security_descriptor(sec_desc, 0);
+
+ if ( *r->out.needed > r->in.offered) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ status = marshall_sec_desc(p->mem_ctx, sec_desc, &buffer, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ *r->out.needed = len;
+ memcpy(r->out.buffer, buffer, len);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_SetServiceObjectSecurity
+********************************************************************/
+
+WERROR _svcctl_SetServiceObjectSecurity(struct pipes_struct *p,
+ struct svcctl_SetServiceObjectSecurity *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ struct security_descriptor *sec_desc = NULL;
+ uint32_t required_access;
+ NTSTATUS status;
+
+ if ( !info || !(info->type & (SVC_HANDLE_IS_SERVICE|SVC_HANDLE_IS_SCM)) )
+ return WERR_INVALID_HANDLE;
+
+ /* can't set the security de4scriptor on the ServiceControlManager */
+
+ if ( info->type == SVC_HANDLE_IS_SCM )
+ return WERR_ACCESS_DENIED;
+
+ /* check the access on the open handle */
+
+ switch ( r->in.security_flags ) {
+ case SECINFO_DACL:
+ required_access = SEC_STD_WRITE_DAC;
+ break;
+
+ case SECINFO_OWNER:
+ case SECINFO_GROUP:
+ required_access = SEC_STD_WRITE_OWNER;
+ break;
+
+ case SECINFO_SACL:
+ return WERR_INVALID_PARAMETER;
+ default:
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if ( !(info->access_granted & required_access) )
+ return WERR_ACCESS_DENIED;
+
+ /* read the security descfriptor */
+
+ status = unmarshall_sec_desc(p->mem_ctx,
+ r->in.buffer,
+ r->in.offered,
+ &sec_desc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ /* store the new SD */
+
+ if (!svcctl_set_secdesc(p->msg_ctx, session_info, info->name, sec_desc))
+ return WERR_ACCESS_DENIED;
+
+ return WERR_OK;
+}
+
+
+WERROR _svcctl_DeleteService(struct pipes_struct *p,
+ struct svcctl_DeleteService *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SetServiceStatus(struct pipes_struct *p,
+ struct svcctl_SetServiceStatus *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_NotifyBootConfigStatus(struct pipes_struct *p,
+ struct svcctl_NotifyBootConfigStatus *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SCSetServiceBitsW(struct pipes_struct *p,
+ struct svcctl_SCSetServiceBitsW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfigW(struct pipes_struct *p,
+ struct svcctl_ChangeServiceConfigW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_CreateServiceW(struct pipes_struct *p,
+ struct svcctl_CreateServiceW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceLockStatusW(struct pipes_struct *p,
+ struct svcctl_QueryServiceLockStatusW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetServiceKeyNameW(struct pipes_struct *p,
+ struct svcctl_GetServiceKeyNameW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SCSetServiceBitsA(struct pipes_struct *p,
+ struct svcctl_SCSetServiceBitsA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfigA(struct pipes_struct *p,
+ struct svcctl_ChangeServiceConfigA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_CreateServiceA(struct pipes_struct *p,
+ struct svcctl_CreateServiceA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumDependentServicesA(struct pipes_struct *p,
+ struct svcctl_EnumDependentServicesA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServicesStatusA(struct pipes_struct *p,
+ struct svcctl_EnumServicesStatusA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_OpenSCManagerA(struct pipes_struct *p,
+ struct svcctl_OpenSCManagerA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_OpenServiceA(struct pipes_struct *p,
+ struct svcctl_OpenServiceA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceConfigA(struct pipes_struct *p,
+ struct svcctl_QueryServiceConfigA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceLockStatusA(struct pipes_struct *p,
+ struct svcctl_QueryServiceLockStatusA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_StartServiceA(struct pipes_struct *p,
+ struct svcctl_StartServiceA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetServiceDisplayNameA(struct pipes_struct *p,
+ struct svcctl_GetServiceDisplayNameA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetServiceKeyNameA(struct pipes_struct *p,
+ struct svcctl_GetServiceKeyNameA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetCurrentGroupeStateW(struct pipes_struct *p,
+ struct svcctl_GetCurrentGroupeStateW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServiceGroupW(struct pipes_struct *p,
+ struct svcctl_EnumServiceGroupW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfig2A(struct pipes_struct *p,
+ struct svcctl_ChangeServiceConfig2A *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfig2W(struct pipes_struct *p,
+ struct svcctl_ChangeServiceConfig2W *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceConfig2A(struct pipes_struct *p,
+ struct svcctl_QueryServiceConfig2A *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServicesStatusExA(struct pipes_struct *p,
+ struct svcctl_EnumServicesStatusExA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServicesStatusExW(struct pipes_struct *p,
+ struct svcctl_EnumServicesStatusExW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SCSendTSMessage(struct pipes_struct *p,
+ struct svcctl_SCSendTSMessage *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+ _svcctl_CreateServiceWOW64A
+****************************************************************/
+
+WERROR _svcctl_CreateServiceWOW64A(struct pipes_struct *p,
+ struct svcctl_CreateServiceWOW64A *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _svcctl_CreateServiceWOW64W
+****************************************************************/
+
+WERROR _svcctl_CreateServiceWOW64W(struct pipes_struct *p,
+ struct svcctl_CreateServiceWOW64W *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _Opnum46NotUsedOnWire
+****************************************************************/
+
+void _Opnum46NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum46NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _svcctl_NotifyServiceStatusChange
+****************************************************************/
+
+WERROR _svcctl_NotifyServiceStatusChange(struct pipes_struct *p,
+ struct svcctl_NotifyServiceStatusChange *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _svcctl_GetNotifyResults
+****************************************************************/
+
+WERROR _svcctl_GetNotifyResults(struct pipes_struct *p,
+ struct svcctl_GetNotifyResults *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _svcctl_CloseNotifyHandle
+****************************************************************/
+
+WERROR _svcctl_CloseNotifyHandle(struct pipes_struct *p,
+ struct svcctl_CloseNotifyHandle *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _svcctl_ControlServiceExA
+****************************************************************/
+
+WERROR _svcctl_ControlServiceExA(struct pipes_struct *p,
+ struct svcctl_ControlServiceExA *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _svcctl_ControlServiceExW
+****************************************************************/
+
+WERROR _svcctl_ControlServiceExW(struct pipes_struct *p,
+ struct svcctl_ControlServiceExW *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _Opnum52NotUsedOnWire
+****************************************************************/
+
+void _Opnum52NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum52NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _Opnum53NotUsedOnWire
+****************************************************************/
+
+void _Opnum53NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum53NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _Opnum54NotUsedOnWire
+****************************************************************/
+
+void _Opnum54NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum54NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _Opnum55NotUsedOnWire
+****************************************************************/
+
+void _Opnum55NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum55NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _svcctl_QueryServiceConfigEx
+****************************************************************/
+
+WERROR _svcctl_QueryServiceConfigEx(struct pipes_struct *p,
+ struct svcctl_QueryServiceConfigEx *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _Opnum57NotUsedOnWire
+****************************************************************/
+
+void _Opnum57NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum57NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _Opnum58NotUsedOnWire
+****************************************************************/
+
+void _Opnum58NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum58NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _Opnum59NotUsedOnWire
+****************************************************************/
+
+void _Opnum59NotUsedOnWire(struct pipes_struct *p,
+ struct Opnum59NotUsedOnWire *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+}
+
+
+/****************************************************************
+ _svcctl_CreateWowService
+****************************************************************/
+
+WERROR _svcctl_CreateWowService(struct pipes_struct *p,
+ struct svcctl_CreateWowService *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/****************************************************************
+ _svcctl_OpenSCManager2
+****************************************************************/
+
+WERROR _svcctl_OpenSCManager2(struct pipes_struct *p,
+ struct svcctl_OpenSCManager2 *r)
+{
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+static NTSTATUS svcctl__op_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+static NTSTATUS svcctl__op_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server);
+
+#define DCESRV_INTERFACE_SVCCTL_INIT_SERVER \
+ svcctl_init_server
+
+#define DCESRV_INTERFACE_SVCCTL_SHUTDOWN_SERVER \
+ svcctl_shutdown_server
+
+static NTSTATUS svcctl_init_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ bool ok;
+
+ /* initialize the control hooks */
+ init_service_op_table();
+
+ ok = svcctl_init_winreg(msg_ctx);
+ if (!ok) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return svcctl__op_init_server(dce_ctx, ep_server);
+}
+
+static NTSTATUS svcctl_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ shutdown_service_op_table();
+
+ return svcctl__op_shutdown_server(dce_ctx, ep_server);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_svcctl_scompat.c"
diff --git a/source3/rpc_server/svcctl/srv_svcctl_nt.h b/source3/rpc_server/svcctl/srv_svcctl_nt.h
new file mode 100644
index 0000000..dd04927
--- /dev/null
+++ b/source3/rpc_server/svcctl/srv_svcctl_nt.h
@@ -0,0 +1,33 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005.
+ *
+ * Largely Rewritten (Again) by:
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ * Copyright (C) Guenther Deschner 2008,2009.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RPC_SERVER_SVCCTL_SRV_SVCCTL_NT_H_
+#define _RPC_SERVER_SVCCTL_SRV_SVCCTL_NT_H_
+
+/* The following definitions come from rpc_server/srv_svcctl_nt.c */
+
+bool init_service_op_table( void );
+bool shutdown_service_op_table(void);
+
+#endif /* _RPC_SERVER_SVCCTL_SRV_SVCCTL_NT_H_ */
diff --git a/source3/rpc_server/svcctl/srv_svcctl_reg.c b/source3/rpc_server/svcctl/srv_svcctl_reg.c
new file mode 100644
index 0000000..78f6096
--- /dev/null
+++ b/source3/rpc_server/svcctl/srv_svcctl_reg.c
@@ -0,0 +1,678 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SVCCTL RPC server keys initialization
+ *
+ * Copyright (c) 2005 Marcin Krzysztof Porwit
+ * Copyright (c) 2005 Gerald (Jerry) Carter
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "services/services.h"
+#include "services/svc_winreg_glue.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "rpc_client/cli_winreg_int.h"
+#include "rpc_client/cli_winreg.h"
+#include "rpc_server/svcctl/srv_svcctl_reg.h"
+#include "auth.h"
+#include "registry/reg_backend_db.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+#define TOP_LEVEL_SERVICES_KEY "SYSTEM\\CurrentControlSet\\Services"
+
+struct rcinit_file_information {
+ char *description;
+};
+
+struct service_display_info {
+ const char *servicename;
+ const char *daemon;
+ const char *dispname;
+ const char *description;
+};
+
+static struct service_display_info builtin_svcs[] = {
+ {
+ "Spooler",
+ "smbd",
+ "Print Spooler",
+ "Internal service for spooling files to print devices"
+ },
+ {
+ "NETLOGON",
+ "smbd",
+ "Net Logon",
+ "File service providing access to policy and profile data (not"
+ "remotely manageable)"
+ },
+ {
+ "RemoteRegistry",
+ "smbd",
+ "Remote Registry Service",
+ "Internal service providing remote access to the Samba registry"
+ },
+ {
+ "WINS",
+ "nmbd",
+ "Windows Internet Name Service (WINS)",
+ "Internal service providing a NetBIOS point-to-point name server"
+ "(not remotely manageable)"
+ },
+ { NULL, NULL, NULL, NULL }
+};
+
+static struct service_display_info common_unix_svcs[] = {
+ { "cups", NULL, "Common Unix Printing System","Provides unified printing support for all operating systems" },
+ { "postfix", NULL, "Internet Mail Service", "Provides support for sending and receiving electonic mail" },
+ { "sendmail", NULL, "Internet Mail Service", "Provides support for sending and receiving electonic mail" },
+ { "portmap", NULL, "TCP Port to RPC PortMapper",NULL },
+ { "xinetd", NULL, "Internet Meta-Daemon", NULL },
+ { "inet", NULL, "Internet Meta-Daemon", NULL },
+ { "xntpd", NULL, "Network Time Service", NULL },
+ { "ntpd", NULL, "Network Time Service", NULL },
+ { "lpd", NULL, "BSD Print Spooler", NULL },
+ { "nfsserver", NULL, "Network File Service", NULL },
+ { "cron", NULL, "Scheduling Service", NULL },
+ { "at", NULL, "Scheduling Service", NULL },
+ { "nscd", NULL, "Name Service Cache Daemon", NULL },
+ { "slapd", NULL, "LDAP Directory Service", NULL },
+ { "ldap", NULL, "LDAP DIrectory Service", NULL },
+ { "ypbind", NULL, "NIS Directory Service", NULL },
+ { "courier-imap", NULL, "IMAP4 Mail Service", NULL },
+ { "courier-pop3", NULL, "POP3 Mail Service", NULL },
+ { "named", NULL, "Domain Name Service", NULL },
+ { "bind", NULL, "Domain Name Service", NULL },
+ { "httpd", NULL, "HTTP Server", NULL },
+ { "apache", NULL, "HTTP Server", "Provides s highly scalable and flexible web server "
+ "capable of implementing various protocols including "
+ "but not limited to HTTP" },
+ { "autofs", NULL, "Automounter", NULL },
+ { "squid", NULL, "Web Cache Proxy ", NULL },
+ { "perfcountd", NULL, "Performance Monitoring Daemon", NULL },
+ { "pgsql", NULL, "PgSQL Database Server", "Provides service for SQL database from Postgresql.org" },
+ { "arpwatch", NULL, "ARP Tables watcher", "Provides service for monitoring ARP tables for changes" },
+ { "dhcpd", NULL, "DHCP Server", "Provides service for dynamic host configuration and IP assignment" },
+ { "nwserv", NULL, "NetWare Server Emulator", "Provides service for emulating Novell NetWare 3.12 server" },
+ { "proftpd", NULL, "Professional FTP Server", "Provides high configurable service for FTP connection and "
+ "file transferring" },
+ { "ssh2", NULL, "SSH Secure Shell", "Provides service for secure connection for remote administration" },
+ { "sshd", NULL, "SSH Secure Shell", "Provides service for secure connection for remote administration" },
+ { NULL, NULL, NULL, NULL }
+};
+
+/********************************************************************
+ This is where we do the dirty work of filling in things like the
+ Display name, Description, etc...
+********************************************************************/
+static char *svcctl_get_common_service_dispname(TALLOC_CTX *mem_ctx,
+ const char *servicename)
+{
+ uint32_t i;
+
+ for (i = 0; common_unix_svcs[i].servicename; i++) {
+ if (strequal(servicename, common_unix_svcs[i].servicename)) {
+ char *dispname;
+ dispname = talloc_asprintf(mem_ctx, "%s (%s)",
+ common_unix_svcs[i].dispname,
+ common_unix_svcs[i].servicename);
+ if (dispname == NULL) {
+ return NULL;
+ }
+ return dispname;
+ }
+ }
+
+ return talloc_strdup(mem_ctx, servicename);
+}
+
+/********************************************************************
+********************************************************************/
+static char *svcctl_cleanup_string(TALLOC_CTX *mem_ctx,
+ const char *string)
+{
+ char *clean = NULL;
+ char *begin, *end;
+
+ clean = talloc_strdup(mem_ctx, string);
+ if (clean == NULL) {
+ return NULL;
+ }
+ begin = clean;
+
+ /* trim any beginning whilespace */
+ while (isspace(*begin)) {
+ begin++;
+ }
+
+ if (*begin == '\0') {
+ return NULL;
+ }
+
+ /* trim any trailing whitespace or carriage returns.
+ Start at the end and move backwards */
+
+ end = begin + strlen(begin) - 1;
+
+ while (isspace(*end) || *end=='\n' || *end=='\r') {
+ *end = '\0';
+ end--;
+ }
+
+ return begin;
+}
+
+/********************************************************************
+********************************************************************/
+static bool read_init_file(TALLOC_CTX *mem_ctx,
+ const char *servicename,
+ struct rcinit_file_information **service_info)
+{
+ struct rcinit_file_information *info = NULL;
+ char *filepath = NULL;
+ char str[1024];
+ FILE *f = NULL;
+ char *p = NULL;
+
+ info = talloc_zero(mem_ctx, struct rcinit_file_information);
+ if (info == NULL) {
+ return false;
+ }
+
+ /* attempt the file open */
+
+ filepath = talloc_asprintf(mem_ctx,
+ "%s/%s/%s",
+ get_dyn_MODULESDIR(),
+ SVCCTL_SCRIPT_DIR,
+ servicename);
+ if (filepath == NULL) {
+ return false;
+ }
+ f = fopen( filepath, "r" );
+ if (f == NULL) {
+ DEBUG(0,("read_init_file: failed to open [%s]\n", filepath));
+ return false;
+ }
+
+ while ((fgets(str, sizeof(str) - 1, f)) != NULL) {
+ /* ignore everything that is not a full line
+ comment starting with a '#' */
+
+ if (str[0] != '#') {
+ continue;
+ }
+
+ /* Look for a line like '^#.*Description:' */
+
+ p = strstr(str, "Description:");
+ if (p != NULL) {
+ char *desc;
+ size_t len = strlen(p);
+
+ if (len <= 12) {
+ break;
+ }
+
+ desc = svcctl_cleanup_string(mem_ctx, p + 12);
+ if (desc != NULL) {
+ info->description = talloc_strdup(info, desc);
+ }
+ }
+ }
+
+ fclose(f);
+
+ if (info->description == NULL) {
+ info->description = talloc_strdup(info,
+ "External Unix Service");
+ if (info->description == NULL) {
+ return false;
+ }
+ }
+
+ *service_info = info;
+
+ return true;
+}
+
+static bool svcctl_add_service(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *hive_hnd,
+ const char *key,
+ uint32_t access_mask,
+ const char *name)
+{
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+ struct security_descriptor *sd = NULL;
+ struct policy_handle key_hnd;
+ struct winreg_String wkey;
+ struct winreg_String wkeyclass;
+ char *description = NULL;
+ char *dname = NULL;
+ char *ipath = NULL;
+ bool ok = false;
+ uint32_t i;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+
+ ZERO_STRUCT(key_hnd);
+
+ ZERO_STRUCT(wkey);
+ wkey.name = talloc_asprintf(mem_ctx, "%s\\%s", key, name);
+ if (wkey.name == NULL) {
+ goto done;
+ }
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(h,
+ mem_ctx,
+ hive_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ &key_hnd,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, win_errstr(result)));
+ goto done;
+ }
+
+ /* These values are hardcoded in all QueryServiceConfig() replies.
+ I'm just storing them here for cosmetic purposes */
+ status = dcerpc_winreg_set_dword(mem_ctx,
+ h,
+ &key_hnd,
+ "Start",
+ SVCCTL_AUTO_START,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(mem_ctx,
+ h,
+ &key_hnd,
+ "Type",
+ SERVICE_TYPE_WIN32_OWN_PROCESS,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_dword(mem_ctx,
+ h,
+ &key_hnd,
+ "ErrorControl",
+ SVCCTL_SVC_ERROR_NORMAL,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(mem_ctx,
+ h,
+ &key_hnd,
+ "ObjectName",
+ "LocalSystem",
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ /*
+ * Special considerations for internal services and the DisplayName
+ * value.
+ */
+ for (i = 0; builtin_svcs[i].servicename; i++) {
+ if (strequal(name, builtin_svcs[i].servicename)) {
+ ipath = talloc_asprintf(mem_ctx,
+ "%s/%s/%s",
+ get_dyn_MODULESDIR(),
+ SVCCTL_SCRIPT_DIR,
+ builtin_svcs[i].daemon);
+ description = talloc_strdup(mem_ctx, builtin_svcs[i].description);
+ dname = talloc_strdup(mem_ctx, builtin_svcs[i].dispname);
+ break;
+ }
+ }
+
+ /* Default to an external service if we haven't found a match */
+ if (builtin_svcs[i].servicename == NULL) {
+ struct rcinit_file_information *init_info = NULL;
+ char *dispname = NULL;
+
+ ipath = talloc_asprintf(mem_ctx,
+ "%s/%s/%s",
+ get_dyn_MODULESDIR(),
+ SVCCTL_SCRIPT_DIR,
+ name);
+
+ /* lookup common unix display names */
+ dispname = svcctl_get_common_service_dispname(mem_ctx, name);
+ dname = talloc_strdup(mem_ctx, dispname ? dispname : "");
+
+ /* get info from init file itself */
+ if (read_init_file(mem_ctx, name, &init_info)) {
+ description = talloc_strdup(mem_ctx,
+ init_info->description);
+ } else {
+ description = talloc_strdup(mem_ctx,
+ "External Unix Service");
+ }
+ }
+
+ if (ipath == NULL || dname == NULL || description == NULL) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(mem_ctx,
+ h,
+ &key_hnd,
+ "DisplayName",
+ dname,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(mem_ctx,
+ h,
+ &key_hnd,
+ "ImagePath",
+ ipath,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sz(mem_ctx,
+ h,
+ &key_hnd,
+ "Description",
+ description,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ sd = svcctl_gen_service_sd(mem_ctx);
+ if (sd == NULL) {
+ DEBUG(0, ("add_new_svc_name: Failed to create default "
+ "sec_desc!\n"));
+ goto done;
+ }
+
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
+ }
+ ZERO_STRUCT(key_hnd);
+
+ ZERO_STRUCT(wkey);
+ wkey.name = talloc_asprintf(mem_ctx, "%s\\%s\\Security", key, name);
+ if (wkey.name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(h,
+ mem_ctx,
+ hive_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ access_mask,
+ NULL,
+ &key_hnd,
+ &action,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
+ wkey.name, win_errstr(result)));
+ goto done;
+ }
+
+ status = dcerpc_winreg_set_sd(mem_ctx,
+ h,
+ &key_hnd,
+ "Security",
+ sd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
+ win_errstr(result)));
+ goto done;
+ }
+
+ ok = true;
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
+ }
+
+ return ok;
+}
+
+bool svcctl_init_winreg(struct messaging_context *msg_ctx)
+{
+ struct dcerpc_binding_handle *h = NULL;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ struct policy_handle hive_hnd, key_hnd;
+ const char **service_list = lp_svcctl_list();
+ const char **subkeys = NULL;
+ uint32_t num_subkeys = 0;
+ char *key = NULL;
+ uint32_t i;
+ NTSTATUS status;
+ WERROR result = WERR_OK;
+ bool ok = false;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return false;
+ }
+
+ DEBUG(3, ("Initialise the svcctl registry keys if needed.\n"));
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ key = talloc_strdup(tmp_ctx, TOP_LEVEL_SERVICES_KEY);
+ if (key == NULL) {
+ goto done;
+ }
+
+ status = dcerpc_winreg_int_hklm_openkey(tmp_ctx,
+ get_session_info_system(),
+ msg_ctx,
+ &h,
+ key,
+ false,
+ access_mask,
+ &hive_hnd,
+ &key_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
+ key, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ /* get all subkeys */
+ status = dcerpc_winreg_enum_keys(tmp_ctx,
+ h,
+ &key_hnd,
+ &num_subkeys,
+ &subkeys,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("svcctl_init_winreg: Could not enum keys at %s - %s\n",
+ key, nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("svcctl_init_winreg: Could not enum keys at %s - %s\n",
+ key, win_errstr(result)));
+ goto done;
+ }
+
+ for (i = 0; builtin_svcs[i].servicename != NULL; i++) {
+ uint32_t j;
+ bool skip = false;
+
+ for (j = 0; j < num_subkeys; j++) {
+ if (strequal(subkeys[i], builtin_svcs[i].servicename)) {
+ skip = true;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ ok = svcctl_add_service(tmp_ctx,
+ h,
+ &hive_hnd,
+ key,
+ access_mask,
+ builtin_svcs[i].servicename);
+ if (!ok) {
+ goto done;
+ }
+ }
+
+ for (i = 0; service_list && service_list[i]; i++) {
+ uint32_t j;
+ bool skip = false;
+
+ for (j = 0; j < num_subkeys; j++) {
+ if (strequal(subkeys[i], service_list[i])) {
+ skip = true;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ ok = svcctl_add_service(tmp_ctx,
+ h,
+ &hive_hnd,
+ key,
+ access_mask,
+ service_list[i]);
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
+ }
+ ZERO_STRUCT(key_hnd);
+
+ if (!ok) {
+ goto done;
+ }
+ }
+
+done:
+ if (is_valid_policy_hnd(&key_hnd)) {
+ dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
+ }
+
+ talloc_free(tmp_ctx);
+ return ok;
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/svcctl/srv_svcctl_reg.h b/source3/rpc_server/svcctl/srv_svcctl_reg.h
new file mode 100644
index 0000000..ab12a03
--- /dev/null
+++ b/source3/rpc_server/svcctl/srv_svcctl_reg.h
@@ -0,0 +1,29 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SVCCTL RPC server keys initialization
+ *
+ * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SRV_SERVICES_REG_H
+#define SRV_SERVICES_REG_H
+
+bool svcctl_init_winreg(struct messaging_context *msg_ctx);
+
+#endif /* SRV_SERVICES_REG_H */
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source3/rpc_server/winreg/srv_winreg_nt.c b/source3/rpc_server/winreg/srv_winreg_nt.c
new file mode 100644
index 0000000..132213a
--- /dev/null
+++ b/source3/rpc_server/winreg/srv_winreg_nt.c
@@ -0,0 +1,1126 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Gerald Carter 2002-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/>.
+ */
+
+/* Implementation of registry functions. */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "librpc/gen_ndr/ndr_winreg_scompat.h"
+#include "registry.h"
+#include "registry/reg_api.h"
+#include "registry/reg_perfcount.h"
+#include "rpc_misc.h"
+#include "auth.h"
+#include "lib/privileges.h"
+#include "libcli/security/secdesc.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+enum handle_types { HTYPE_REGVAL, HTYPE_REGKEY };
+
+/******************************************************************
+ Find a registry key handle and return a struct registry_key *
+ *****************************************************************/
+
+static struct registry_key *find_regkey_by_hnd(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ enum handle_types type)
+{
+ struct registry_key *regkey = NULL;
+ NTSTATUS status;
+
+ regkey = find_policy_by_hnd(p,
+ hnd,
+ type,
+ struct registry_key,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: %s\n",
+ nt_errstr(status)));
+ return NULL;
+ }
+
+ return regkey;
+}
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle
+ Note that P should be valid & hnd should already have space
+
+ When we open a key, we store the full path to the key as
+ HK[LM|U]\<key>\<key>\...
+ *******************************************************************/
+
+static WERROR open_registry_key(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ struct registry_key *parent,
+ const char *subkeyname,
+ uint32_t access_desired)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ WERROR result = WERR_OK;
+ struct registry_key *key;
+
+ if (parent == NULL) {
+ result = reg_openhive(p->mem_ctx, subkeyname, access_desired,
+ session_info->security_token, &key);
+ }
+ else {
+ result = reg_openkey(p->mem_ctx, parent, subkeyname,
+ access_desired, &key);
+ }
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ return result;
+ }
+
+ if ( !create_policy_hnd( p, hnd, HTYPE_REGKEY, key ) ) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Function for open a new registry handle and creating a handle
+ Note that P should be valid & hnd should already have space
+ *******************************************************************/
+
+static bool close_registry_key(struct pipes_struct *p,
+ struct policy_handle *hnd,
+ enum handle_types type)
+{
+ struct registry_key *regkey = find_regkey_by_hnd(p, hnd, type);
+
+ if ( !regkey ) {
+ DEBUG(2,("close_registry_key: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(hnd)));
+ return False;
+ }
+
+ close_policy_hnd(p, hnd);
+
+ return True;
+}
+
+/********************************************************************
+ _winreg_CloseKey
+ ********************************************************************/
+
+WERROR _winreg_CloseKey(struct pipes_struct *p,
+ struct winreg_CloseKey *r)
+{
+ bool ok;
+
+ /* close the policy handle */
+
+ ok = close_registry_key(p, r->in.handle, HTYPE_REGKEY);
+ if (!ok) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _winreg_OpenHKLM
+ ********************************************************************/
+
+WERROR _winreg_OpenHKLM(struct pipes_struct *p,
+ struct winreg_OpenHKLM *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKLM, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKPD
+ ********************************************************************/
+
+WERROR _winreg_OpenHKPD(struct pipes_struct *p,
+ struct winreg_OpenHKPD *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKPD, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKPT
+ ********************************************************************/
+
+WERROR _winreg_OpenHKPT(struct pipes_struct *p,
+ struct winreg_OpenHKPT *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKPT, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKCR
+ ********************************************************************/
+
+WERROR _winreg_OpenHKCR(struct pipes_struct *p,
+ struct winreg_OpenHKCR *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKCR, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKU
+ ********************************************************************/
+
+WERROR _winreg_OpenHKU(struct pipes_struct *p,
+ struct winreg_OpenHKU *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKU, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKCU
+ ********************************************************************/
+
+WERROR _winreg_OpenHKCU(struct pipes_struct *p,
+ struct winreg_OpenHKCU *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKCU, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKCC
+ ********************************************************************/
+
+WERROR _winreg_OpenHKCC(struct pipes_struct *p,
+ struct winreg_OpenHKCC *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKCC, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKDD
+ ********************************************************************/
+
+WERROR _winreg_OpenHKDD(struct pipes_struct *p,
+ struct winreg_OpenHKDD *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKDD, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenHKPN
+ ********************************************************************/
+
+WERROR _winreg_OpenHKPN(struct pipes_struct *p,
+ struct winreg_OpenHKPN *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKPN, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_OpenKey
+ ********************************************************************/
+
+WERROR _winreg_OpenKey(struct pipes_struct *p,
+ struct winreg_OpenKey *r)
+{
+ struct registry_key *parent = find_regkey_by_hnd(p,
+ r->in.parent_handle,
+ HTYPE_REGKEY);
+
+ if ( !parent )
+ return WERR_INVALID_HANDLE;
+
+ return open_registry_key(p, r->out.handle, parent, r->in.keyname.name, r->in.access_mask);
+}
+
+/*******************************************************************
+ _winreg_QueryValue
+ ********************************************************************/
+
+WERROR _winreg_QueryValue(struct pipes_struct *p,
+ struct winreg_QueryValue *r)
+{
+ WERROR status = WERR_FILE_NOT_FOUND;
+ struct registry_key *regkey = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ prs_struct prs_hkpd;
+
+ uint8_t *outbuf = NULL;
+ uint32_t outbuf_size = 0;
+
+ bool free_buf = False;
+ bool free_prs = False;
+
+ if ( !regkey )
+ return WERR_INVALID_HANDLE;
+
+ if (r->in.value_name->name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if ((r->out.data_length == NULL) || (r->out.type == NULL) || (r->out.data_size == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ DEBUG(7,("_winreg_QueryValue: policy key name = [%s]\n", regkey->key->name));
+ DEBUG(7,("_winreg_QueryValue: policy key type = [%08x]\n", regkey->key->type));
+
+ /* Handle QueryValue calls on HKEY_PERFORMANCE_DATA */
+ if(regkey->key->type == REG_KEY_HKPD)
+ {
+ if (strequal(r->in.value_name->name, "Global")) {
+ if (!prs_init(&prs_hkpd, *r->in.data_size, p->mem_ctx, MARSHALL))
+ return WERR_NOT_ENOUGH_MEMORY;
+ status = reg_perfcount_get_hkpd(
+ &prs_hkpd, *r->in.data_size, &outbuf_size, NULL);
+ outbuf = (uint8_t *)prs_hkpd.data_p;
+ free_prs = True;
+ }
+ else if (strequal(r->in.value_name->name, "Counter 009")) {
+ outbuf_size = reg_perfcount_get_counter_names(
+ reg_perfcount_get_base_index(),
+ (char **)(void *)&outbuf);
+ free_buf = True;
+ }
+ else if (strequal(r->in.value_name->name, "Explain 009")) {
+ outbuf_size = reg_perfcount_get_counter_help(
+ reg_perfcount_get_base_index(),
+ (char **)(void *)&outbuf);
+ free_buf = True;
+ }
+ else if (isdigit(r->in.value_name->name[0])) {
+ /* we probably have a request for a specific object
+ * here */
+ if (!prs_init(&prs_hkpd, *r->in.data_size, p->mem_ctx, MARSHALL))
+ return WERR_NOT_ENOUGH_MEMORY;
+ status = reg_perfcount_get_hkpd(
+ &prs_hkpd, *r->in.data_size, &outbuf_size,
+ r->in.value_name->name);
+ outbuf = (uint8_t *)prs_hkpd.data_p;
+ free_prs = True;
+ }
+ else {
+ DEBUG(3,("Unsupported key name [%s] for HKPD.\n",
+ r->in.value_name->name));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ *r->out.type = REG_BINARY;
+ }
+ else {
+ struct registry_value *val;
+
+ status = reg_queryvalue(p->mem_ctx, regkey, r->in.value_name->name,
+ &val);
+ if (!W_ERROR_IS_OK(status)) {
+
+ DEBUG(10,("_winreg_QueryValue: reg_queryvalue failed with: %s\n",
+ win_errstr(status)));
+
+ if (r->out.data_size) {
+ *r->out.data_size = 0;
+ }
+ if (r->out.data_length) {
+ *r->out.data_length = 0;
+ }
+ return status;
+ }
+
+ outbuf = val->data.data;
+ outbuf_size = val->data.length;
+ *r->out.type = val->type;
+ }
+
+ status = WERR_FILE_NOT_FOUND;
+
+ if (*r->in.data_size < outbuf_size) {
+ *r->out.data_size = outbuf_size;
+ status = r->in.data ? WERR_MORE_DATA : WERR_OK;
+ } else {
+ *r->out.data_length = outbuf_size;
+ *r->out.data_size = outbuf_size;
+ if (r->out.data) {
+ memcpy(r->out.data, outbuf, outbuf_size);
+ }
+ status = WERR_OK;
+ }
+
+ if (free_prs) prs_mem_free(&prs_hkpd);
+ if (free_buf) SAFE_FREE(outbuf);
+
+ return status;
+}
+
+/*****************************************************************************
+ _winreg_QueryInfoKey
+ ****************************************************************************/
+
+WERROR _winreg_QueryInfoKey(struct pipes_struct *p,
+ struct winreg_QueryInfoKey *r)
+{
+ WERROR status = WERR_OK;
+ struct registry_key *regkey = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+
+ if ( !regkey )
+ return WERR_INVALID_HANDLE;
+
+ r->out.classname->name = NULL;
+
+ status = reg_queryinfokey(regkey, r->out.num_subkeys, r->out.max_subkeylen,
+ r->out.max_classlen, r->out.num_values, r->out.max_valnamelen,
+ r->out.max_valbufsize, r->out.secdescsize,
+ r->out.last_changed_time);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * These calculations account for the registry buffers being
+ * UTF-16. They are inexact at best, but so far they worked.
+ */
+
+ *r->out.max_subkeylen *= 2;
+
+ *r->out.max_valnamelen += 1;
+ *r->out.max_valnamelen *= 2;
+
+ return WERR_OK;
+}
+
+
+/*****************************************************************************
+ _winreg_GetVersion
+ ****************************************************************************/
+
+WERROR _winreg_GetVersion(struct pipes_struct *p,
+ struct winreg_GetVersion *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+
+ if ( !regkey )
+ return WERR_INVALID_HANDLE;
+
+ return reg_getversion(r->out.version);
+}
+
+
+/*****************************************************************************
+ _winreg_EnumKey
+ ****************************************************************************/
+
+WERROR _winreg_EnumKey(struct pipes_struct *p,
+ struct winreg_EnumKey *r)
+{
+ WERROR err = WERR_OK;
+ struct registry_key *key = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ char *name;
+
+ if ( !key )
+ return WERR_INVALID_HANDLE;
+
+ if ( !r->in.name || !r->in.keyclass )
+ return WERR_INVALID_PARAMETER;
+
+ DEBUG(8,("_winreg_EnumKey: enumerating key [%s]\n", key->key->name));
+
+ err = reg_enumkey(p->mem_ctx, key, r->in.enum_index, &name,
+ r->out.last_changed_time);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+ r->out.name->name = name;
+ r->out.keyclass->name = "";
+ return WERR_OK;
+}
+
+/*****************************************************************************
+ _winreg_EnumValue
+ ****************************************************************************/
+
+WERROR _winreg_EnumValue(struct pipes_struct *p,
+ struct winreg_EnumValue *r)
+{
+ WERROR err = WERR_OK;
+ struct registry_key *key = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ char *valname = NULL;
+ struct registry_value *val = NULL;
+
+ if ( !key )
+ return WERR_INVALID_HANDLE;
+
+ if ( !r->in.name )
+ return WERR_INVALID_PARAMETER;
+
+ DEBUG(8,("_winreg_EnumValue: enumerating values for key [%s]\n",
+ key->key->name));
+
+ err = reg_enumvalue(p->mem_ctx, key, r->in.enum_index, &valname, &val);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ if (r->out.name != NULL) {
+ r->out.name->name = valname;
+ }
+
+ if (r->out.type != NULL) {
+ *r->out.type = val->type;
+ }
+
+ if (r->out.value != NULL) {
+ if ((r->out.size == NULL) || (r->out.length == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (val->data.length > *r->out.size) {
+ *r->out.size = val->data.length;
+ return WERR_MORE_DATA;
+ }
+
+ memcpy( r->out.value, val->data.data, val->data.length );
+ }
+
+ if (r->out.length != NULL) {
+ *r->out.length = val->data.length;
+ }
+ if (r->out.size != NULL) {
+ *r->out.size = val->data.length;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _winreg_InitiateSystemShutdown
+ ********************************************************************/
+
+WERROR _winreg_InitiateSystemShutdown(struct pipes_struct *p,
+ struct winreg_InitiateSystemShutdown *r)
+{
+ struct winreg_InitiateSystemShutdownEx s;
+
+ s.in.hostname = r->in.hostname;
+ s.in.message = r->in.message;
+ s.in.timeout = r->in.timeout;
+ s.in.force_apps = r->in.force_apps;
+ s.in.do_reboot = r->in.do_reboot;
+ s.in.reason = 0;
+
+ /* thunk down to _winreg_InitiateSystemShutdownEx()
+ (just returns a status) */
+
+ return _winreg_InitiateSystemShutdownEx( p, &s );
+}
+
+/*******************************************************************
+ _winreg_InitiateSystemShutdownEx
+ ********************************************************************/
+
+#define SHUTDOWN_R_STRING "-r"
+#define SHUTDOWN_F_STRING "-f"
+
+
+WERROR _winreg_InitiateSystemShutdownEx(struct pipes_struct *p,
+ struct winreg_InitiateSystemShutdownEx *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *shutdown_script = NULL;
+ char *chkmsg = NULL;
+ fstring str_timeout;
+ fstring str_reason;
+ fstring do_reboot;
+ fstring f;
+ int ret = -1;
+ bool can_shutdown = false;
+
+ shutdown_script = lp_shutdown_script(p->mem_ctx, lp_sub);
+ if (!shutdown_script) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (!*shutdown_script) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* pull the message string and perform necessary sanity checks on it */
+
+ if ( r->in.message && r->in.message->string ) {
+ chkmsg = talloc_alpha_strcpy(p->mem_ctx,
+ r->in.message->string,
+ NULL);
+ if (chkmsg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ fstr_sprintf(str_timeout, "%d", r->in.timeout);
+ fstr_sprintf(do_reboot, r->in.do_reboot ? SHUTDOWN_R_STRING : "");
+ fstr_sprintf(f, r->in.force_apps ? SHUTDOWN_F_STRING : "");
+ fstr_sprintf(str_reason, "%d", r->in.reason );
+
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%z", chkmsg ? chkmsg : "");
+ if (!shutdown_script) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%t", str_timeout);
+ if (!shutdown_script) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%r", do_reboot);
+ if (!shutdown_script) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%f", f);
+ if (!shutdown_script) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%x", str_reason);
+ if (!shutdown_script) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ can_shutdown = security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_REMOTE_SHUTDOWN);
+
+ /* IF someone has privs, run the shutdown script as root. OTHERWISE run it as not root
+ Take the error return from the script and provide it as the Windows return code. */
+
+ /********** BEGIN SeRemoteShutdownPrivilege BLOCK **********/
+
+ if ( can_shutdown )
+ become_root();
+
+ ret = smbrun(shutdown_script, NULL, NULL);
+
+ if ( can_shutdown )
+ unbecome_root();
+
+ /********** END SeRemoteShutdownPrivilege BLOCK **********/
+
+ DEBUG(3,("_reg_shutdown_ex: Running the command `%s' gave %d\n",
+ shutdown_script, ret));
+
+ return (ret == 0) ? WERR_OK : WERR_ACCESS_DENIED;
+}
+
+/*******************************************************************
+ _winreg_AbortSystemShutdown
+ ********************************************************************/
+
+WERROR _winreg_AbortSystemShutdown(struct pipes_struct *p,
+ struct winreg_AbortSystemShutdown *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ const char *abort_shutdown_script = NULL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int ret = -1;
+ bool can_shutdown = false;
+
+ abort_shutdown_script = lp_abort_shutdown_script(talloc_tos(), lp_sub);
+ if (!*abort_shutdown_script)
+ return WERR_ACCESS_DENIED;
+
+ can_shutdown = security_token_has_privilege(
+ session_info->security_token, SEC_PRIV_REMOTE_SHUTDOWN);
+
+ /********** BEGIN SeRemoteShutdownPrivilege BLOCK **********/
+
+ if ( can_shutdown )
+ become_root();
+
+ ret = smbrun(abort_shutdown_script, NULL, NULL);
+
+ if ( can_shutdown )
+ unbecome_root();
+
+ /********** END SeRemoteShutdownPrivilege BLOCK **********/
+
+ DEBUG(3,("_winreg_AbortSystemShutdown: Running the command `%s' gave %d\n",
+ abort_shutdown_script, ret));
+
+ return (ret == 0) ? WERR_OK : WERR_ACCESS_DENIED;
+}
+
+/*******************************************************************
+ _winreg_RestoreKey
+ ********************************************************************/
+
+WERROR _winreg_RestoreKey(struct pipes_struct *p,
+ struct winreg_RestoreKey *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+
+ if ( !regkey ) {
+ return WERR_INVALID_HANDLE;
+ }
+ return WERR_BAD_PATHNAME;
+}
+
+/*******************************************************************
+ _winreg_SaveKey
+ ********************************************************************/
+
+WERROR _winreg_SaveKey(struct pipes_struct *p,
+ struct winreg_SaveKey *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+
+ if ( !regkey ) {
+ return WERR_INVALID_HANDLE;
+ }
+ return WERR_BAD_PATHNAME;
+}
+
+/*******************************************************************
+ _winreg_SaveKeyEx
+ ********************************************************************/
+
+WERROR _winreg_SaveKeyEx(struct pipes_struct *p,
+ struct winreg_SaveKeyEx *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ _winreg_CreateKey
+ ********************************************************************/
+
+WERROR _winreg_CreateKey(struct pipes_struct *p,
+ struct winreg_CreateKey *r)
+{
+ struct registry_key *parent = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ struct registry_key *new_key = NULL;
+ WERROR result = WERR_OK;
+
+ if ( !parent )
+ return WERR_INVALID_HANDLE;
+
+ DEBUG(10, ("_winreg_CreateKey called with parent key '%s' and "
+ "subkey name '%s'\n", parent->key->name, r->in.name.name));
+
+ result = reg_createkey(NULL, parent, r->in.name.name, r->in.access_mask,
+ &new_key, r->out.action_taken);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ if (!create_policy_hnd(p, r->out.new_handle, HTYPE_REGKEY, new_key)) {
+ TALLOC_FREE(new_key);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _winreg_SetValue
+ ********************************************************************/
+
+WERROR _winreg_SetValue(struct pipes_struct *p,
+ struct winreg_SetValue *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ struct registry_value *val = NULL;
+
+ if ( !key )
+ return WERR_INVALID_HANDLE;
+
+ DEBUG(8,("_winreg_SetValue: Setting value for [%s:%s]\n",
+ key->key->name, r->in.name.name));
+
+ val = talloc_zero(p->mem_ctx, struct registry_value);
+ if (val == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ val->type = r->in.type;
+ val->data = data_blob_talloc(p->mem_ctx, r->in.data, r->in.size);
+
+ return reg_setvalue(key, r->in.name.name, val);
+}
+
+/*******************************************************************
+ _winreg_DeleteKey
+ ********************************************************************/
+
+WERROR _winreg_DeleteKey(struct pipes_struct *p,
+ struct winreg_DeleteKey *r)
+{
+ struct registry_key *parent = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+
+ if ( !parent )
+ return WERR_INVALID_HANDLE;
+
+ return reg_deletekey(parent, r->in.key.name);
+}
+
+
+/*******************************************************************
+ _winreg_DeleteValue
+ ********************************************************************/
+
+WERROR _winreg_DeleteValue(struct pipes_struct *p,
+ struct winreg_DeleteValue *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+
+ if ( !key )
+ return WERR_INVALID_HANDLE;
+
+ return reg_deletevalue(key, r->in.value.name);
+}
+
+/*******************************************************************
+ _winreg_GetKeySecurity
+ ********************************************************************/
+
+WERROR _winreg_GetKeySecurity(struct pipes_struct *p,
+ struct winreg_GetKeySecurity *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ WERROR err = WERR_OK;
+ struct security_descriptor *secdesc = NULL;
+ uint8_t *data = NULL;
+ size_t len = 0;
+
+ if ( !key )
+ return WERR_INVALID_HANDLE;
+
+ /* access checks first */
+
+ if ( !(key->key->access_granted & SEC_STD_READ_CONTROL) )
+ return WERR_ACCESS_DENIED;
+
+ err = reg_getkeysecurity(p->mem_ctx, key, &secdesc);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ err = ntstatus_to_werror(marshall_sec_desc(p->mem_ctx, secdesc,
+ &data, &len));
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ if (len > r->out.sd->size) {
+ r->out.sd->size = len;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ r->out.sd->size = len;
+ r->out.sd->len = len;
+ r->out.sd->data = data;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _winreg_SetKeySecurity
+ ********************************************************************/
+
+WERROR _winreg_SetKeySecurity(struct pipes_struct *p,
+ struct winreg_SetKeySecurity *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p,
+ r->in.handle,
+ HTYPE_REGKEY);
+ struct security_descriptor *secdesc = NULL;
+ WERROR err = WERR_OK;
+
+ if ( !key )
+ return WERR_INVALID_HANDLE;
+
+ /* access checks first */
+
+ if ( !(key->key->access_granted & SEC_STD_WRITE_DAC) )
+ return WERR_ACCESS_DENIED;
+
+ err = ntstatus_to_werror(unmarshall_sec_desc(p->mem_ctx, r->in.sd->data,
+ r->in.sd->len, &secdesc));
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ return reg_setkeysecurity(key, secdesc);
+}
+
+/*******************************************************************
+ _winreg_FlushKey
+ ********************************************************************/
+
+WERROR _winreg_FlushKey(struct pipes_struct *p,
+ struct winreg_FlushKey *r)
+{
+ /* I'm just replying OK because there's not a lot
+ here I see to do i --jerry */
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _winreg_UnLoadKey
+ ********************************************************************/
+
+WERROR _winreg_UnLoadKey(struct pipes_struct *p,
+ struct winreg_UnLoadKey *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ _winreg_ReplaceKey
+ ********************************************************************/
+
+WERROR _winreg_ReplaceKey(struct pipes_struct *p,
+ struct winreg_ReplaceKey *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ _winreg_LoadKey
+ ********************************************************************/
+
+WERROR _winreg_LoadKey(struct pipes_struct *p,
+ struct winreg_LoadKey *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ _winreg_NotifyChangeKeyValue
+ ********************************************************************/
+
+WERROR _winreg_NotifyChangeKeyValue(struct pipes_struct *p,
+ struct winreg_NotifyChangeKeyValue *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ _winreg_QueryMultipleValues
+ ********************************************************************/
+
+WERROR _winreg_QueryMultipleValues(struct pipes_struct *p,
+ struct winreg_QueryMultipleValues *r)
+{
+ struct winreg_QueryMultipleValues2 r2;
+ uint32_t needed = 0;
+
+ r2.in.key_handle = r->in.key_handle;
+ r2.in.values_in = r->in.values_in;
+ r2.in.num_values = r->in.num_values;
+ r2.in.offered = r->in.buffer_size;
+ r2.in.buffer = r->in.buffer;
+ r2.out.values_out = r->out.values_out;
+ r2.out.needed = &needed;
+ r2.out.buffer = r->out.buffer;
+
+ return _winreg_QueryMultipleValues2(p, &r2);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static WERROR construct_multiple_entry(TALLOC_CTX *mem_ctx,
+ const char *valuename,
+ uint32_t value_length,
+ uint32_t offset,
+ enum winreg_Type type,
+ struct QueryMultipleValue *r)
+{
+ r->ve_valuename = talloc_zero(mem_ctx, struct winreg_ValNameBuf);
+ if (r->ve_valuename == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ r->ve_valuename->name = talloc_strdup(r->ve_valuename, valuename ? valuename : "");
+ if (r->ve_valuename->name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ r->ve_valuename->size = strlen_m_term(r->ve_valuename->name)*2;
+ r->ve_valuelen = value_length;
+ r->ve_valueptr = offset;
+ r->ve_type = type;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _winreg_QueryMultipleValues2
+ ********************************************************************/
+
+WERROR _winreg_QueryMultipleValues2(struct pipes_struct *p,
+ struct winreg_QueryMultipleValues2 *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd(p,
+ r->in.key_handle,
+ HTYPE_REGKEY);
+ struct registry_value *vals = NULL;
+ const char **names = NULL;
+ uint32_t offset = 0, num_vals = 0;
+ DATA_BLOB result = data_blob_null;
+ uint32_t i = 0;
+ WERROR err = WERR_OK;
+
+ if (!regkey) {
+ return WERR_INVALID_HANDLE;
+ }
+
+ names = talloc_zero_array(p->mem_ctx, const char *, r->in.num_values);
+ if (names == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (i=0; i < r->in.num_values; i++) {
+ if (r->in.values_in[i].ve_valuename &&
+ r->in.values_in[i].ve_valuename->name) {
+ names[i] = talloc_strdup(names,
+ r->in.values_in[i].ve_valuename->name);
+ if (names[i] == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ }
+
+ err = reg_querymultiplevalues(p->mem_ctx, regkey,
+ r->in.num_values, names,
+ &num_vals, &vals);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ result = data_blob_talloc(p->mem_ctx, NULL, 0);
+
+ for (i=0; i < r->in.num_values; i++) {
+ const char *valuename = NULL;
+
+ if (vals[i].data.length > 0) {
+ if (!data_blob_append(p->mem_ctx, &result,
+ vals[i].data.data,
+ vals[i].data.length)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if (r->in.values_in[i].ve_valuename &&
+ r->in.values_in[i].ve_valuename->name) {
+ valuename = r->in.values_in[i].ve_valuename->name;
+ }
+
+ err = construct_multiple_entry(r->out.values_out,
+ valuename,
+ vals[i].data.length,
+ offset,
+ vals[i].type,
+ &r->out.values_out[i]);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ offset += vals[i].data.length;
+ }
+
+ *r->out.needed = result.length;
+
+ if (r->in.num_values != num_vals) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (*r->in.offered >= *r->out.needed) {
+ if (r->out.buffer) {
+ memcpy(r->out.buffer, result.data, MIN(result.length, *r->in.offered));
+ }
+ return WERR_OK;
+ } else {
+ return WERR_MORE_DATA;
+ }
+}
+
+/*******************************************************************
+ _winreg_DeleteKeyEx
+ ********************************************************************/
+
+WERROR _winreg_DeleteKeyEx(struct pipes_struct *p,
+ struct winreg_DeleteKeyEx *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_winreg_scompat.c"
diff --git a/source3/rpc_server/witness/srv_witness_nt.c b/source3/rpc_server/witness/srv_witness_nt.c
new file mode 100644
index 0000000..1d5f720
--- /dev/null
+++ b/source3/rpc_server/witness/srv_witness_nt.c
@@ -0,0 +1,2465 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2012,2023 Stefan Metzmacher
+ * Copyright (C) 2015 Guenther Deschner
+ * Copyright (C) 2018 Samuel Cabrero
+ *
+ * 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 "ctdbd_conn.h"
+#include "ctdb/protocol/protocol.h"
+#include "ctdb_srvids.h"
+#include "messages.h"
+#include "lib/messages_ctdb.h"
+#include "lib/global_contexts.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/util_str_escape.h"
+#include "source3/include/util_tdb.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "lib/param/param.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/tsocket/tsocket.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/auth.h"
+#include "librpc/gen_ndr/ndr_witness.h"
+#include "librpc/gen_ndr/ndr_witness_scompat.h"
+#include "librpc/gen_ndr/ndr_rpcd_witness.h"
+#include "rpc_server/rpc_server.h"
+
+#define SWN_SERVICE_CONTEXT_HANDLE_REGISTRATION 0x01
+
+struct swn_service_interface;
+struct swn_service_registration;
+struct swn_service_async_notify_state;
+
+struct swn_service_globals {
+ struct dcesrv_context *dce_ctx;
+ struct db_context *dce_conn_register;
+
+ const char *server_global_name;
+ uint32_t local_vnn;
+
+ struct {
+ bool valid;
+ uint64_t generation;
+ struct swn_service_interface *list;
+ } interfaces;
+
+ struct {
+ uint32_t unused_timeout_secs;
+ struct swn_service_registration *list;
+ struct db_context *db;
+ } registrations;
+};
+
+struct swn_service_interface {
+ struct swn_service_interface *prev, *next;
+
+ const char *group_name;
+ struct samba_sockaddr addr;
+ enum witness_interfaceInfo_state state;
+ bool local_iface;
+ uint32_t current_vnn;
+ uint64_t change_generation;
+ uint64_t check_generation;
+};
+
+struct swn_service_registration {
+ struct swn_service_registration *prev, *next;
+
+ struct swn_service_globals *swn;
+
+ struct {
+ struct tevent_context *ev_ctx;
+ struct messaging_context *msg_ctx;
+ struct tevent_req *subreq;
+ } msg;
+
+ struct {
+ struct policy_handle handle;
+ void *ptr;
+ } key;
+
+ struct {
+ enum witness_version version;
+ const char *computer_name;
+ } client;
+
+ const char *net_name;
+ const char *share_name;
+ struct samba_sockaddr ip_address;
+
+ struct {
+ bool triggered;
+ struct witness_notifyResponse *response;
+ WERROR result;
+ } forced_response;
+
+ struct {
+ bool triggered;
+ /*
+ * We only do ip based RESOURCE_CHANGE notifications for now
+ * and it means we do just one notification at a time
+ * and don't need to queue pending notifications.
+ */
+ enum witness_interfaceInfo_state last_ip_state;
+ } change_notification;
+
+ struct {
+ bool triggered;
+ uint32_t new_node;
+ struct samba_sockaddr new_ip;
+ } move_notification;
+
+ struct {
+ bool required;
+ bool triggered;
+ uint32_t new_node;
+ struct samba_sockaddr new_ip;
+ } share_notification;
+
+ struct {
+ bool required;
+ bool triggered;
+ /*
+ * TODO: find how this works on windows and implement
+ * Windows Server 2022 as client doesn't use it...
+ */
+ } ip_notification;
+
+ struct {
+ struct timeval create_time;
+ struct timeval last_time;
+ uint32_t unused_timeout_secs;
+ struct timeval expire_time;
+ struct tevent_timer *timer;
+ } usage;
+
+ struct {
+ /*
+ * In order to let a Windows server 2022
+ * correctly re-register after moving
+ * to a new connection, we force an
+ * unregistration after 5 seconds.
+ *
+ * It means the client gets WERR_NOT_FOUND
+ * from a pending AsyncNotify() and calls
+ * Unregister() (which also gets WERR_NOT_FOUND).
+ * Then the client calls GetInterfaceList()
+ * and RegisterEx() again.
+ */
+ struct tevent_timer *timer;
+ } forced_unregister;
+
+ struct {
+ uint32_t timeout_secs;
+ struct tevent_queue *queue;
+ struct swn_service_async_notify_state *list;
+ } async_notify;
+};
+
+static struct swn_service_globals *swn_globals = NULL;
+
+static int swn_service_globals_destructor(struct swn_service_globals *swn)
+{
+ SMB_ASSERT(swn == swn_globals);
+ swn_globals = NULL;
+
+ while (swn->registrations.list != NULL) {
+ /*
+ * NO TALLOC_FREE() because of DLIST_REMOVE()
+ * in swn_service_registration_destructor()
+ */
+ talloc_free(swn->registrations.list);
+ }
+
+ return 0;
+}
+
+static void swn_service_async_notify_reg_destroyed(struct swn_service_async_notify_state *state);
+
+static int swn_service_registration_destructor(struct swn_service_registration *reg)
+{
+ struct swn_service_globals *swn = reg->swn;
+ struct GUID_txt_buf key_buf;
+ const char *key_str = GUID_buf_string(&reg->key.handle.uuid, &key_buf);
+ DATA_BLOB key_blob = data_blob_string_const(key_str);
+ TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length);
+ NTSTATUS status;
+
+ tevent_queue_stop(reg->async_notify.queue);
+ while (reg->async_notify.list != NULL) {
+ swn_service_async_notify_reg_destroyed(reg->async_notify.list);
+ }
+
+ status = dbwrap_delete(reg->swn->registrations.db, key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("rpcd_witness_registration: key '%s' delete - %s\n",
+ tdb_data_dbg(key),
+ nt_errstr(status));
+ } else if (DEBUGLVL(DBGLVL_DEBUG)) {
+ DBG_DEBUG("rpcd_witness_registration: key '%s' deleted\n",
+ tdb_data_dbg(key));
+ }
+
+ DLIST_REMOVE(swn->registrations.list, reg);
+ reg->swn = NULL;
+
+ /*
+ * make sure to drop the policy/context handle from
+ * the assoc_group
+ */
+ TALLOC_FREE(reg->key.ptr);
+
+ return 0;
+}
+
+static void swn_service_registration_update_usage(struct swn_service_registration *reg,
+ struct timeval now)
+{
+ uint64_t expire_timeout_secs = 0;
+ uint64_t max_expire_timeout_secs = TIME_T_MAX;
+
+ reg->usage.last_time = now;
+
+ if (max_expire_timeout_secs > reg->usage.last_time.tv_sec) {
+ max_expire_timeout_secs -= reg->usage.last_time.tv_sec;
+ } else {
+ /*
+ * This should never happen unless
+ * a 32 bit system hits its limit
+ */
+ max_expire_timeout_secs = 0;
+ }
+
+ if (tevent_queue_length(reg->async_notify.queue) != 0) {
+ expire_timeout_secs += reg->async_notify.timeout_secs;
+ }
+
+ expire_timeout_secs += reg->usage.unused_timeout_secs;
+ expire_timeout_secs = MIN(expire_timeout_secs, max_expire_timeout_secs);
+
+ reg->usage.expire_time = timeval_add(&reg->usage.last_time,
+ expire_timeout_secs, 0);
+
+ if (expire_timeout_secs == 0) {
+ /*
+ * No timer needed, witness v1
+ * or max_expire_timeout_secs = 0
+ */
+ TALLOC_FREE(reg->usage.timer);
+ }
+
+ if (reg->usage.timer == NULL) {
+ /* no timer to update */
+ reg->usage.expire_time = (struct timeval) { .tv_sec = TIME_T_MAX, };
+ return;
+ }
+
+ tevent_update_timer(reg->usage.timer, reg->usage.expire_time);
+}
+
+static void swn_service_registration_unused(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct swn_service_registration *reg =
+ talloc_get_type_abort(private_data,
+ struct swn_service_registration);
+
+ reg->usage.timer = NULL;
+
+ TALLOC_FREE(reg);
+}
+
+static void swn_service_registration_force_unregister(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct swn_service_registration *reg =
+ talloc_get_type_abort(private_data,
+ struct swn_service_registration);
+
+ reg->forced_unregister.timer = NULL;
+
+ TALLOC_FREE(reg);
+}
+
+static int swn_service_ctdb_ipreallocated(struct tevent_context *ev,
+ uint32_t src_vnn, uint32_t dst_vnn,
+ uint64_t dst_srvid,
+ const uint8_t *msg, size_t msglen,
+ void *private_data);
+
+static NTSTATUS swn_service_init_globals(struct dcesrv_context *dce_ctx)
+{
+ struct swn_service_globals *swn = NULL;
+ char *global_path = NULL;
+ const char *realm = NULL;
+ const char *nbname = NULL;
+ int ret;
+ bool ok;
+
+ if (swn_globals != NULL) {
+ SMB_ASSERT(swn_globals->dce_ctx == dce_ctx);
+ return NT_STATUS_OK;
+ }
+
+ swn = talloc_zero(dce_ctx, struct swn_service_globals);
+ if (swn == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ swn->dce_ctx = dce_ctx;
+
+ swn->dce_conn_register = db_open_rbt(swn);
+ if (swn->dce_conn_register == NULL) {
+ TALLOC_FREE(swn);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This contains secret information like client keys!
+ */
+ global_path = lock_path(swn, "rpcd_witness_registration.tdb");
+ if (global_path == NULL) {
+ TALLOC_FREE(swn);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ swn->registrations.db = db_open(swn, global_path,
+ 0, /* hash_size */
+ TDB_DEFAULT |
+ TDB_CLEAR_IF_FIRST |
+ TDB_INCOMPATIBLE_HASH,
+ O_RDWR | O_CREAT, 0600,
+ DBWRAP_LOCK_ORDER_1,
+ DBWRAP_FLAG_NONE);
+ if (swn->registrations.db == NULL) {
+ NTSTATUS status;
+
+ status = map_nt_error_from_unix_common(errno);
+ TALLOC_FREE(swn);
+
+ return status;
+ }
+ TALLOC_FREE(global_path);
+
+ nbname = lpcfg_netbios_name(dce_ctx->lp_ctx);
+ realm = lpcfg_realm(dce_ctx->lp_ctx);
+ if (realm != NULL && realm[0] != '\0') {
+ char *name = NULL;
+
+ name = talloc_asprintf(swn, "%s.%s", nbname, realm);
+ if (name == NULL) {
+ TALLOC_FREE(swn);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ok = strlower_m(name);
+ if (!ok) {
+ TALLOC_FREE(swn);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ swn->server_global_name = name;
+ } else {
+ swn->server_global_name = talloc_strdup(swn, nbname);
+ if (swn->server_global_name == NULL) {
+ TALLOC_FREE(swn);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ swn->local_vnn = get_my_vnn();
+
+ ret = register_with_ctdbd(messaging_ctdb_connection(),
+ CTDB_SRVID_IPREALLOCATED,
+ swn_service_ctdb_ipreallocated,
+ swn);
+ if (ret != 0) {
+ TALLOC_FREE(swn);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ swn->registrations.unused_timeout_secs = 30;
+
+ talloc_set_destructor(swn, swn_service_globals_destructor);
+ swn_globals = swn;
+ return NT_STATUS_OK;
+}
+
+static struct swn_service_interface *swn_service_interface_by_addr(
+ struct swn_service_globals *swn,
+ const struct samba_sockaddr *addr)
+{
+ struct swn_service_interface *iface = NULL;
+
+ for (iface = swn->interfaces.list; iface != NULL; iface = iface->next) {
+ bool ok;
+
+ ok = sockaddr_equal(&iface->addr.u.sa, &addr->u.sa);
+ if (ok) {
+ return iface;
+ }
+ }
+
+ return NULL;
+}
+
+static void swn_service_interface_changed(struct swn_service_globals *swn,
+ struct swn_service_interface *iface)
+{
+ struct swn_service_registration *reg = NULL;
+ char addr[INET6_ADDRSTRLEN] = { 0, };
+
+ print_sockaddr(addr, sizeof(addr), &iface->addr.u.ss);
+ DBG_NOTICE("addr[%s] state[%u] local_iface[%u] "
+ "current_vnn[%"PRIu32"] generation[%"PRIu64"][%"PRIu64"]\n",
+ addr,
+ iface->state,
+ iface->local_iface,
+ iface->current_vnn,
+ iface->change_generation,
+ iface->check_generation);
+
+ for (reg = swn->registrations.list; reg != NULL; reg = reg->next) {
+ bool match;
+
+ /*
+ * We only check the ip address,
+ * we do not make real use of the group name.
+ */
+
+ match = sockaddr_equal(&reg->ip_address.u.sa,
+ &iface->addr.u.sa);
+ if (!match) {
+ continue;
+ }
+
+ if (reg->change_notification.last_ip_state
+ != WITNESS_STATE_UNAVAILABLE)
+ {
+ /*
+ * Remember the current state unless we already
+ * hit WITNESS_STATE_UNAVAILAVLE before we notified
+ * the client
+ */
+ reg->change_notification.last_ip_state = iface->state;
+ }
+
+ reg->change_notification.triggered = true;
+
+ tevent_queue_start(reg->async_notify.queue);
+ }
+
+ return;
+}
+
+static NTSTATUS swn_service_add_or_update_interface(struct swn_service_globals *swn,
+ const char *group_name,
+ const struct samba_sockaddr *addr,
+ enum witness_interfaceInfo_state state,
+ bool local_iface,
+ uint32_t current_vnn)
+{
+ struct swn_service_interface *iface = NULL;
+ bool changed = false;
+ bool force_unavailable = false;
+ bool filter;
+
+ if (addr->u.sa.sa_family != AF_INET &&
+ addr->u.sa.sa_family != AF_INET6)
+ {
+ /*
+ * We only support ipv4 and ipv6
+ */
+ return NT_STATUS_OK;
+ }
+
+ filter = is_loopback_addr(&addr->u.sa);
+ if (filter) {
+ return NT_STATUS_OK;
+ }
+ filter = is_linklocal_addr(&addr->u.ss);
+ if (filter) {
+ return NT_STATUS_OK;
+ }
+
+ for (iface = swn->interfaces.list; iface != NULL; iface = iface->next) {
+ bool match;
+
+ match = strequal(group_name, iface->group_name);
+ if (!match) {
+ continue;
+ }
+
+ match = sockaddr_equal(&addr->u.sa, &iface->addr.u.sa);
+ if (!match) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (iface == NULL) {
+ iface = talloc_zero(swn, struct swn_service_interface);
+ if (iface == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ iface->group_name = talloc_strdup(iface, group_name);
+ if (iface->group_name == NULL) {
+ TALLOC_FREE(iface);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ iface->addr = *addr;
+
+ iface->state = WITNESS_STATE_UNKNOWN;
+ iface->current_vnn = NONCLUSTER_VNN;
+ DLIST_ADD_END(swn->interfaces.list, iface);
+
+ iface->change_generation = swn->interfaces.generation;
+ }
+
+ if (iface->state != state) {
+ changed = true;
+ iface->state = state;
+ }
+
+ if (iface->current_vnn != current_vnn) {
+ changed = true;
+ if (iface->current_vnn != NONCLUSTER_VNN) {
+ force_unavailable = true;
+ }
+ iface->current_vnn = current_vnn;
+ }
+
+ if (iface->local_iface != local_iface) {
+ changed = true;
+ force_unavailable = true;
+ iface->local_iface = local_iface;
+ }
+
+ iface->check_generation = swn->interfaces.generation;
+
+ if (!changed) {
+ return NT_STATUS_OK;
+ }
+
+ iface->change_generation = swn->interfaces.generation;
+
+ if (force_unavailable) {
+ iface->state = WITNESS_STATE_UNAVAILABLE;
+ }
+
+ swn_service_interface_changed(swn, iface);
+
+ if (force_unavailable) {
+ iface->state = state;
+ }
+
+ return NT_STATUS_OK;
+};
+
+static int swn_service_ctdb_all_ip_cb(uint32_t total_ip_count,
+ const struct sockaddr_storage *ip,
+ uint32_t pinned_vnn,
+ uint32_t current_vnn,
+ void *private_data)
+{
+ struct swn_service_globals *swn =
+ talloc_get_type_abort(private_data,
+ struct swn_service_globals);
+ enum witness_interfaceInfo_state state = WITNESS_STATE_UNKNOWN;
+ struct samba_sockaddr addr = {
+ .u = {
+ .ss = *ip,
+ },
+ };
+ NTSTATUS status;
+ bool local_iface = false;
+
+ SMB_ASSERT(swn->local_vnn != NONCLUSTER_VNN);
+
+ if (current_vnn == NONCLUSTER_VNN) {
+ /*
+ * No node hosts this address
+ */
+ state = WITNESS_STATE_UNAVAILABLE;
+ } else {
+ state = WITNESS_STATE_AVAILABLE;
+ }
+
+ if (current_vnn == swn->local_vnn || pinned_vnn == swn->local_vnn) {
+ local_iface = true;
+ }
+
+ status = swn_service_add_or_update_interface(swn,
+ swn->server_global_name,
+ &addr,
+ state,
+ local_iface,
+ current_vnn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("swn_service_add_or_update_interface() failed: %s\n",
+ nt_errstr(status));
+ return map_errno_from_nt_status(status);
+ }
+
+ return 0;
+}
+
+static NTSTATUS swn_service_reload_interfaces(struct dcesrv_context *dce_ctx)
+{
+ struct swn_service_interface *iface = NULL;
+ struct swn_service_interface *next = NULL;
+ bool include_node_ips = false;
+ bool include_public_ips = true;
+ int ret;
+ NTSTATUS status;
+
+ status = swn_service_init_globals(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (swn_globals->interfaces.valid) {
+ return NT_STATUS_OK;
+ }
+
+ swn_globals->interfaces.generation += 1;
+
+ include_node_ips = lpcfg_parm_bool(dce_ctx->lp_ctx,
+ NULL,
+ "rpcd witness",
+ "include node ips",
+ include_node_ips);
+ include_public_ips = lpcfg_parm_bool(dce_ctx->lp_ctx,
+ NULL,
+ "rpcd witness",
+ "include public ips",
+ include_public_ips);
+
+ ret = ctdbd_all_ip_foreach(messaging_ctdb_connection(),
+ include_node_ips,
+ include_public_ips,
+ swn_service_ctdb_all_ip_cb,
+ swn_globals);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ for (iface = swn_globals->interfaces.list; iface != NULL; iface = next) {
+ next = iface->next;
+
+ if (iface->check_generation == swn_globals->interfaces.generation) {
+ continue;
+ }
+
+ status = swn_service_add_or_update_interface(swn_globals,
+ iface->group_name,
+ &iface->addr,
+ WITNESS_STATE_UNAVAILABLE,
+ iface->local_iface,
+ iface->current_vnn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DLIST_REMOVE(swn_globals->interfaces.list, iface);
+ TALLOC_FREE(iface);
+ }
+
+ /* CTDB_SRVID_IPREALLOCATED is still registered */
+
+ swn_globals->interfaces.valid = true;
+ return NT_STATUS_OK;
+}
+
+static int swn_service_ctdb_ipreallocated(struct tevent_context *ev,
+ uint32_t src_vnn, uint32_t dst_vnn,
+ uint64_t dst_srvid,
+ const uint8_t *msg, size_t msglen,
+ void *private_data)
+{
+ struct swn_service_globals *swn =
+ talloc_get_type_abort(private_data,
+ struct swn_service_globals);
+ NTSTATUS status;
+
+ DBG_DEBUG("PID[%d] swn[%p] IPREALLOCATED\n", getpid(), swn);
+
+ swn->interfaces.valid = false;
+ status = swn_service_reload_interfaces(swn->dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ swn->interfaces.valid = false;
+ return 0;
+ }
+
+ return 0;
+}
+
+struct swn_dcesrv_connection {
+ struct db_context *rbt;
+ struct dcesrv_connection *conn;
+ struct samba_sockaddr cli_addr;
+ struct samba_sockaddr srv_addr;
+ char addr[INET6_ADDRSTRLEN];
+};
+
+static int swn_dcesrv_connection_release_ip(struct tevent_context *ev,
+ uint32_t src_vnn,
+ uint32_t dst_vnn,
+ uint64_t dst_srvid,
+ const uint8_t *msg,
+ size_t msglen,
+ void *private_data)
+{
+ struct swn_dcesrv_connection *sc =
+ talloc_get_type_abort(private_data,
+ struct swn_dcesrv_connection);
+ struct dcesrv_connection *conn = sc->conn;
+ const char *ip = NULL;
+ const char *addr = sc->addr;
+ const char *p = addr;
+
+ if (conn->terminate != NULL) {
+ /* avoid recursion */
+ return 0;
+ }
+
+ if (msglen == 0) {
+ return 0;
+ }
+ if (msg[msglen-1] != '\0') {
+ return 0;
+ }
+
+ ip = (const char *)msg;
+
+ if (strncmp("::ffff:", addr, 7) == 0) {
+ p = addr + 7;
+ }
+
+ DBG_DEBUG("Got release IP message for %s, our address is %s\n", ip, p);
+
+ if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
+ DBG_NOTICE("Got release IP message for our IP %s - exiting immediately\n",
+ ip);
+ talloc_free(sc);
+ dcesrv_terminate_connection(conn, "CTDB_SRVID_RELEASE_IP");
+ return EADDRNOTAVAIL;
+ }
+
+ return 0;
+
+}
+
+static int swn_dcesrv_connection_destructor(struct swn_dcesrv_connection *sc)
+{
+ struct ctdbd_connection *cconn = messaging_ctdb_connection();
+ struct dcesrv_connection *conn = sc->conn;
+ uintptr_t conn_ptr = (uintptr_t)conn;
+ NTSTATUS status;
+ TDB_DATA key;
+
+ key = make_tdb_data((uint8_t *)&conn_ptr, sizeof(conn_ptr));
+
+ status = dbwrap_delete(sc->rbt, key);
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+
+ if (cconn == NULL) {
+ return 0;
+ }
+
+ ctdbd_unregister_ips(cconn,
+ &sc->srv_addr.u.ss,
+ &sc->cli_addr.u.ss,
+ swn_dcesrv_connection_release_ip,
+ sc);
+
+ return 0;
+}
+
+static NTSTATUS dcesrv_interface_witness_register_ips(struct dcesrv_connection *conn)
+{
+ struct ctdbd_connection *cconn = messaging_ctdb_connection();
+ struct dcesrv_context *dce_ctx = conn->dce_ctx;
+ const struct tsocket_address *client_address =
+ dcesrv_connection_get_remote_address(conn);
+ const struct tsocket_address *server_address =
+ dcesrv_connection_get_local_address(conn);
+ NTSTATUS status;
+ uintptr_t conn_ptr = (uintptr_t)conn;
+ struct swn_dcesrv_connection *sc = NULL;
+ uintptr_t sc_ptr;
+ const char *addr = NULL;
+ TDB_DATA key;
+ TDB_DATA val;
+ bool exists;
+ int ret;
+
+ status = swn_service_init_globals(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("swn_service_init_globals() failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ key = make_tdb_data((uint8_t *)&conn_ptr, sizeof(conn_ptr));
+
+ exists = dbwrap_exists(swn_globals->dce_conn_register, key);
+ if (exists) {
+ /* Already registered */
+ return NT_STATUS_OK;
+ }
+
+ sc = talloc_zero(conn, struct swn_dcesrv_connection);
+ if (sc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sc->rbt = swn_globals->dce_conn_register;
+ sc->conn = conn;
+
+ if (tsocket_address_is_inet(client_address, "ip")) {
+ ssize_t sret;
+
+ sret = tsocket_address_bsd_sockaddr(client_address,
+ &sc->cli_addr.u.sa,
+ sizeof(sc->cli_addr.u.ss));
+ if (sret == -1) {
+ TALLOC_FREE(sc);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ sc->cli_addr.sa_socklen = sret;
+ }
+
+ if (tsocket_address_is_inet(server_address, "ip")) {
+ ssize_t sret;
+
+ sret = tsocket_address_bsd_sockaddr(server_address,
+ &sc->srv_addr.u.sa,
+ sizeof(sc->srv_addr.u.ss));
+ if (sret == -1) {
+ TALLOC_FREE(sc);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ sc->srv_addr.sa_socklen = sret;
+ }
+
+ addr = print_sockaddr(sc->addr, sizeof(sc->addr), &sc->srv_addr.u.ss);
+ if (addr == NULL) {
+ TALLOC_FREE(sc);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sc_ptr = (uintptr_t)sc;
+ val = make_tdb_data((uint8_t *)&sc_ptr, sizeof(sc_ptr));
+
+ status = dbwrap_store(sc->rbt, key, val, TDB_INSERT);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(sc);
+ return status;
+ }
+
+ talloc_set_destructor(sc, swn_dcesrv_connection_destructor);
+
+
+ ret = ctdbd_register_ips(cconn,
+ &sc->srv_addr.u.ss,
+ &sc->cli_addr.u.ss,
+ swn_dcesrv_connection_release_ip,
+ sc);
+ if (ret != 0) {
+ TALLOC_FREE(sc);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_WITNESS_BIND(context, iface) \
+ dcesrv_interface_witness_bind(context, iface)
+static NTSTATUS dcesrv_interface_witness_bind(struct dcesrv_connection_context *context,
+ const struct dcesrv_interface *iface)
+{
+ NTSTATUS status;
+
+ status = dcesrv_interface_witness_register_ips(context->conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * This is not really critical, so we just print
+ * as warning...
+ */
+ DBG_WARNING("dcesrv_interface_witness_register_ips() failed: %s\n",
+ nt_errstr(status));
+ }
+
+ /*
+ * [MS-SWN] Section 7. If the authentication level is not
+ * integrity or privacy level, Windows servers will fail the call
+ * with access denied
+ */
+ return dcesrv_interface_bind_require_integrity(context, iface);
+}
+
+/****************************************************************
+ _witness_GetInterfaceList
+****************************************************************/
+
+WERROR _witness_GetInterfaceList(struct pipes_struct *p,
+ struct witness_GetInterfaceList *r)
+{
+ struct dcesrv_context *dce_ctx = p->dce_call->conn->dce_ctx;
+ struct swn_service_interface *iface = NULL;
+ struct witness_interfaceList *list = NULL;
+ size_t num_interfaces = 0;
+ NTSTATUS status;
+
+ status = swn_service_reload_interfaces(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ for (iface = swn_globals->interfaces.list; iface != NULL; iface = iface->next) {
+ num_interfaces += 1;
+ }
+
+ list = talloc_zero(p->mem_ctx, struct witness_interfaceList);
+ if (list == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ list->interfaces = talloc_zero_array(list,
+ struct witness_interfaceInfo,
+ num_interfaces);
+ if (list->interfaces == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (iface = swn_globals->interfaces.list; iface != NULL; iface = iface->next) {
+ struct witness_interfaceInfo *info =
+ &list->interfaces[list->num_interfaces++];
+ char addr[INET6_ADDRSTRLEN] = { 0, };
+ const char *ipv4 = "0.0.0.0";
+ const char *ipv6 = "::";
+ uint32_t flags = 0;
+
+ print_sockaddr(addr, sizeof(addr), &iface->addr.u.ss);
+ if (iface->addr.u.sa.sa_family == AF_INET) {
+ flags |= WITNESS_INFO_IPv4_VALID;
+ ipv4 = addr;
+ } else if (iface->addr.u.sa.sa_family == AF_INET6) {
+ flags |= WITNESS_INFO_IPv6_VALID;
+ ipv6 = addr;
+ }
+
+ if (!iface->local_iface) {
+ /*
+ * If it's not a local interface
+ * it is able to serve as
+ * witness server
+ */
+ flags |= WITNESS_INFO_WITNESS_IF;
+ }
+
+ info->group_name = talloc_strdup(list, iface->group_name);
+ if (info->group_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ info->version = WITNESS_V2; /* WitnessServiceVersion; */
+ info->state = iface->state;
+ info->ipv4 = talloc_strdup(list, ipv4);
+ if (info->ipv4 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ info->ipv6 = talloc_strdup(list, ipv6);
+ if (info->ipv6 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ info->flags = flags;
+ }
+
+ *r->out.interface_list = list;
+ return WERR_OK;
+}
+
+static bool swn_server_registration_message_filter(struct messaging_rec *rec, void *private_data)
+{
+ struct swn_service_registration *reg = NULL;
+ struct policy_handle context_handle;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ bool match;
+
+ if (rec->msg_type != MSG_RPCD_WITNESS_REGISTRATION_UPDATE) {
+ return false;
+ }
+
+ if (rec->num_fds != 0) {
+ return false;
+ }
+
+ if (rec->buf.length < 20) {
+ return false;
+ }
+
+ reg = talloc_get_type_abort(private_data, struct swn_service_registration);
+
+ blob = data_blob_const(rec->buf.data, 20);
+ ndr_err = ndr_pull_struct_blob_all_noalloc(&blob, &context_handle,
+ (ndr_pull_flags_fn_t)ndr_pull_policy_handle);
+ SMB_ASSERT(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
+
+ match = ndr_policy_handle_equal(&context_handle, &reg->key.handle);
+ if (!match) {
+ return false;
+ }
+
+ return true;
+}
+
+static void swn_server_registration_client_move_to_node(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_move_to_node *move)
+{
+ reg->move_notification.triggered = true;
+ reg->move_notification.new_node = move->new_node;
+ reg->move_notification.new_ip = (struct samba_sockaddr) {
+ .sa_socklen = 0,
+ };
+
+ tevent_queue_start(reg->async_notify.queue);
+}
+
+static void swn_server_registration_client_move_to_ip(
+ struct swn_service_registration *reg,
+ const char *new_ip_str)
+{
+ struct samba_sockaddr new_ip = {
+ .sa_socklen = 0,
+ };
+ bool ok;
+
+ ok = is_ipaddress(new_ip_str);
+ if (!ok) {
+ return;
+ }
+ ok = interpret_string_addr(&new_ip.u.ss,
+ new_ip_str,
+ AI_PASSIVE|AI_NUMERICHOST);
+ if (!ok) {
+ return;
+ }
+
+ reg->move_notification.triggered = true;
+ reg->move_notification.new_node = NONCLUSTER_VNN;
+ reg->move_notification.new_ip = new_ip;
+
+ tevent_queue_start(reg->async_notify.queue);
+}
+
+static void swn_server_registration_client_move_to_ipv4(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_move_to_ipv4 *move)
+{
+ swn_server_registration_client_move_to_ip(reg, move->new_ipv4);
+}
+
+static void swn_server_registration_client_move_to_ipv6(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_move_to_ipv6 *move)
+{
+ swn_server_registration_client_move_to_ip(reg, move->new_ipv6);
+}
+
+static void swn_server_registration_share_move_to_node(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_move_to_node *move)
+{
+ if (!reg->share_notification.required) {
+ return;
+ }
+
+ reg->share_notification.triggered = true;
+ reg->share_notification.new_node = move->new_node;
+ reg->share_notification.new_ip = (struct samba_sockaddr) {
+ .sa_socklen = 0,
+ };
+
+ tevent_queue_start(reg->async_notify.queue);
+}
+
+static void swn_server_registration_share_move_to_ip(
+ struct swn_service_registration *reg,
+ const char *new_ip_str)
+{
+ struct samba_sockaddr new_ip = {
+ .sa_socklen = 0,
+ };
+ bool ok;
+
+ ok = is_ipaddress(new_ip_str);
+ if (!ok) {
+ return;
+ }
+ ok = interpret_string_addr(&new_ip.u.ss,
+ new_ip_str,
+ AI_PASSIVE|AI_NUMERICHOST);
+ if (!ok) {
+ return;
+ }
+
+ if (!reg->share_notification.required) {
+ return;
+ }
+
+ reg->share_notification.triggered = true;
+ reg->share_notification.new_node = NONCLUSTER_VNN;
+ reg->share_notification.new_ip = new_ip;
+
+ tevent_queue_start(reg->async_notify.queue);
+}
+
+static void swn_server_registration_share_move_to_ipv4(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_move_to_ipv4 *move)
+{
+ swn_server_registration_share_move_to_ip(reg, move->new_ipv4);
+}
+
+static void swn_server_registration_share_move_to_ipv6(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_move_to_ipv6 *move)
+{
+ swn_server_registration_share_move_to_ip(reg, move->new_ipv6);
+}
+
+static void swn_server_registration_force_response(
+ struct swn_service_registration *reg,
+ struct rpcd_witness_registration_update_force_response *response)
+{
+ reg->forced_response.triggered = true;
+ reg->forced_response.response = talloc_move(reg, &response->response);
+ reg->forced_response.result = response->result;
+
+ tevent_queue_start(reg->async_notify.queue);
+}
+
+static void swn_server_registration_message_done(struct tevent_req *subreq)
+{
+ struct swn_service_registration *reg =
+ tevent_req_callback_data(subreq,
+ struct swn_service_registration);
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct messaging_rec *rec = NULL;
+ struct rpcd_witness_registration_updateB update_blob;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ int ret;
+
+ SMB_ASSERT(reg->msg.subreq == subreq);
+ reg->msg.subreq = NULL;
+
+ ret = messaging_filtered_read_recv(subreq, frame, &rec);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ status = map_nt_error_from_unix_common(ret);
+ DBG_ERR("messaging_filtered_read_recv() - %s\n",
+ nt_errstr(status));
+ goto wait_for_next;
+ }
+
+ DBG_DEBUG("MSG_RPCD_WITNESS_REGISTRATION_UPDATE: received...\n");
+
+ ndr_err = ndr_pull_struct_blob(&rec->buf, frame, &update_blob,
+ (ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration_updateB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_ERR("ndr_pull_struct_blob - %s\n", nt_errstr(status));
+ goto wait_for_next;
+ }
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update_blob);
+ }
+
+ switch (update_blob.type) {
+ case RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE:
+ swn_server_registration_client_move_to_node(reg,
+ &update_blob.update.client_move_to_node);
+ break;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV4:
+ swn_server_registration_client_move_to_ipv4(reg,
+ &update_blob.update.client_move_to_ipv4);
+ break;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV6:
+ swn_server_registration_client_move_to_ipv6(reg,
+ &update_blob.update.client_move_to_ipv6);
+ break;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE:
+ swn_server_registration_share_move_to_node(reg,
+ &update_blob.update.share_move_to_node);
+ break;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV4:
+ swn_server_registration_share_move_to_ipv4(reg,
+ &update_blob.update.share_move_to_ipv4);
+ break;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV6:
+ swn_server_registration_share_move_to_ipv6(reg,
+ &update_blob.update.share_move_to_ipv6);
+ break;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_UNREGISTER:
+ TALLOC_FREE(reg);
+ TALLOC_FREE(frame);
+ return;
+ case RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_RESPONSE:
+ swn_server_registration_force_response(reg,
+ &update_blob.update.force_response);
+ break;
+ }
+
+wait_for_next:
+ TALLOC_FREE(frame);
+ reg->msg.subreq = messaging_filtered_read_send(reg,
+ reg->msg.ev_ctx,
+ reg->msg.msg_ctx,
+ swn_server_registration_message_filter,
+ reg);
+ if (reg->msg.subreq == NULL) {
+ DBG_ERR("messaging_filtered_read_send() failed\n");
+ return;
+ }
+ tevent_req_set_callback(reg->msg.subreq,
+ swn_server_registration_message_done,
+ reg);
+}
+
+static WERROR swn_server_registration_create(struct swn_service_globals *swn,
+ struct pipes_struct *p,
+ const struct witness_RegisterEx *r,
+ const struct swn_service_interface *iface,
+ struct swn_service_registration **preg)
+{
+ struct swn_service_registration *reg = NULL;
+ const struct tsocket_address *client_address =
+ dcesrv_connection_get_remote_address(p->dce_call->conn);
+ const struct tsocket_address *server_address =
+ dcesrv_connection_get_local_address(p->dce_call->conn);
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(p->dce_call);
+ struct rpcd_witness_registration rg = { .version = 0, };
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ struct GUID_txt_buf key_buf = {};
+ const char *key_str = NULL;
+ DATA_BLOB key_blob = { .length = 0, };
+ TDB_DATA key = { .dsize = 0, };
+ DATA_BLOB val_blob = { .length = 0, };
+ TDB_DATA val = { .dsize = 0, };
+
+ /*
+ * [MS-SWN] 3.1.4.5
+ * The server MUST create a WitnessRegistration entry as follows and
+ * insert it into the WitnessRegistrationList.
+ */
+ reg = talloc_zero(p->mem_ctx, struct swn_service_registration);
+ if (reg == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ reg->swn = swn;
+
+ reg->client.version = r->in.version;
+
+ /*
+ * all other string values are checked against
+ * well known expected values.
+ *
+ * So we better escape the client_computer_name
+ * if it contains strange things...
+ */
+ reg->client.computer_name = log_escape(reg, r->in.client_computer_name);
+ if (reg->client.computer_name == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ reg->net_name = talloc_strdup(reg, r->in.net_name);
+ if (reg->net_name == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ reg->ip_address = iface->addr;
+
+ if (r->in.share_name != NULL) {
+ reg->share_name = talloc_strdup(reg, r->in.share_name);
+ if (reg->share_name == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ reg->share_notification.required = true;
+ }
+
+ reg->async_notify.timeout_secs = r->in.timeout;
+ reg->async_notify.queue = tevent_queue_create(reg, "async_notify");
+ if (reg->async_notify.queue == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tevent_queue_stop(reg->async_notify.queue);
+
+ reg->ip_notification.required = (r->in.flags &
+ WITNESS_REGISTER_IP_NOTIFICATION);
+
+ reg->usage.create_time = p->dce_call->time;
+ reg->usage.unused_timeout_secs =
+ swn_globals->registrations.unused_timeout_secs;
+ /*
+ * swn_service_registration_update_usage() below
+ * will update the timer to its real expire time!
+ */
+ reg->usage.expire_time = (struct timeval) { .tv_sec = TIME_T_MAX, };
+ reg->usage.timer = tevent_add_timer(p->dce_call->event_ctx,
+ reg,
+ reg->usage.expire_time,
+ swn_service_registration_unused,
+ reg);
+ if (reg->usage.timer == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ swn_service_registration_update_usage(reg, reg->usage.create_time);
+
+ reg->msg.ev_ctx = p->dce_call->event_ctx;
+ reg->msg.msg_ctx = p->msg_ctx;
+ reg->msg.subreq = messaging_filtered_read_send(reg,
+ reg->msg.ev_ctx,
+ reg->msg.msg_ctx,
+ swn_server_registration_message_filter,
+ reg);
+ if (reg->msg.subreq == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tevent_req_set_callback(reg->msg.subreq,
+ swn_server_registration_message_done,
+ reg);
+
+ reg->key.ptr = create_policy_hnd(p, &reg->key.handle,
+ SWN_SERVICE_CONTEXT_HANDLE_REGISTRATION,
+ reg);
+ if (reg->key.ptr == NULL) {
+ TALLOC_FREE(reg);
+ return WERR_NO_SYSTEM_RESOURCES;
+ }
+
+ DLIST_ADD_END(swn_globals->registrations.list, reg);
+ talloc_set_destructor(reg, swn_service_registration_destructor);
+
+ key_str = GUID_buf_string(&reg->key.handle.uuid, &key_buf);
+ key_blob = data_blob_string_const(key_str);
+ key = make_tdb_data(key_blob.data, key_blob.length);
+
+ rg = (struct rpcd_witness_registration) {
+ .version = r->in.version,
+ .net_name = r->in.net_name,
+ .share_name = r->in.share_name,
+ .ip_address = r->in.ip_address,
+ .client_computer_name = reg->client.computer_name,
+ .flags = r->in.flags,
+ .timeout = r->in.timeout,
+ .context_handle = reg->key.handle,
+ .server_id = messaging_server_id(p->msg_ctx),
+ .account_name = session_info->info->account_name,
+ .domain_name = session_info->info->domain_name,
+ .account_sid = session_info->security_token->sids[PRIMARY_USER_SID_INDEX],
+ .local_address = tsocket_address_string(server_address, p->mem_ctx),
+ .remote_address = tsocket_address_string(client_address, p->mem_ctx),
+ .registration_time = timeval_to_nttime(&p->dce_call->time),
+ };
+
+ ndr_err = ndr_push_struct_blob(&val_blob, p->mem_ctx, &rg,
+ (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_WARNING("rpcd_witness_registration: key '%s' ndr_push - %s\n",
+ tdb_data_dbg(key),
+ nt_errstr(status));
+ TALLOC_FREE(reg);
+ return WERR_NO_SYSTEM_RESOURCES;
+ }
+ val = make_tdb_data(val_blob.data, val_blob.length);
+
+ status = dbwrap_store(reg->swn->registrations.db, key, val, TDB_INSERT);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("rpcd_witness_registration: key '%s' store - %s\n",
+ tdb_data_dbg(key),
+ nt_errstr(status));
+ TALLOC_FREE(reg);
+ return WERR_NO_SYSTEM_RESOURCES;
+ }
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ DBG_DEBUG("rpcd_witness_registration: key '%s' stored\n",
+ tdb_data_dbg(key));
+ NDR_PRINT_DEBUG(rpcd_witness_registration, &rg);
+ }
+
+ *preg = reg;
+ return WERR_OK;
+}
+
+static WERROR swn_server_check_net_name(struct swn_service_globals *swn,
+ const char *net_name)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *stripped_net_name = NULL;
+ char *p = NULL;
+ bool ok;
+
+ ok = strequal(swn->server_global_name, net_name);
+ if (ok) {
+ TALLOC_FREE(frame);
+ return WERR_OK;
+ }
+
+ stripped_net_name = talloc_strdup(frame, net_name);
+ if (stripped_net_name == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ p = strchr(stripped_net_name, '.');
+ if (p != NULL) {
+ *p = '\0';
+ }
+
+ ok = is_myname(stripped_net_name);
+ if (ok) {
+ TALLOC_FREE(frame);
+ return WERR_OK;
+ }
+
+ TALLOC_FREE(frame);
+ return WERR_INVALID_PARAMETER;
+}
+
+/****************************************************************
+ _witness_Register
+****************************************************************/
+
+WERROR _witness_Register(struct pipes_struct *p,
+ struct witness_Register *r)
+{
+ struct dcesrv_context *dce_ctx = p->dce_call->conn->dce_ctx;
+ struct swn_service_registration *reg = NULL;
+ struct samba_sockaddr addr = { .sa_socklen = 0, };
+ struct swn_service_interface *iface = NULL;
+ const struct witness_RegisterEx rex = {
+ .in = {
+ .version = r->in.version,
+ .net_name = r->in.net_name,
+ .ip_address = r->in.ip_address,
+ .client_computer_name = r->in.client_computer_name,
+ },
+ };
+ NTSTATUS status;
+ WERROR werr;
+ bool ok;
+
+ /*
+ * [MS-SWN] 3.1.4.2
+ * If the Version field of the request is not 0x00010001, the server
+ * MUST stop processing the request and return the error code
+ * ERROR_REVISION_MISMATCH
+ */
+ if (r->in.version != WITNESS_V1) {
+ return WERR_REVISION_MISMATCH;
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.2
+ * If NetName, IpAddress or ClientComputerName is NULL, the server
+ * MUST fail the request and return the error code
+ * ERROR_INVALID_PARAMETER
+ */
+ if (r->in.net_name == NULL ||
+ r->in.ip_address == NULL ||
+ r->in.client_computer_name == NULL)
+ {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ status = swn_service_reload_interfaces(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.2
+ * If the NetName parameter is not equal to ServerGlobalName, the
+ * server MUST fail the request and return the error code
+ * ERROR_INVALID_PARAMETER
+ */
+ werr = swn_server_check_net_name(swn_globals, r->in.net_name);
+ if (!W_ERROR_IS_OK(werr)) {
+ DBG_INFO("Invalid net_name[%s], "
+ "server_global_name[%s]: %s\n",
+ log_escape(p->mem_ctx, r->in.net_name),
+ swn_globals->server_global_name,
+ win_errstr(werr));
+ return werr;
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.2
+ * The server MUST enumerate the shares by calling NetrShareEnum as
+ * specified in [MS-SRVS] section 3.1.4.8. In the enumerated list,
+ * if any of the shares has shi*_type set to STYPE_CLUSTER_SOFS, as
+ * specified in [MS-SRVS] section 2.2.2.4, the server MUST search for
+ * an Interface in InterfaceList, where Interface.IPv4Address or
+ * Interface.IPv6Address matches the IpAddress parameter based on its
+ * format. If no matching entry is found, the server MUST fail the
+ * request and return the error code ERROR_INVALID_STATE.
+ *
+ * After clarifying this point with dochelp:
+ * A server only sets the CLUSTER_SOFS, CLUSTER_FS, or CLUSTER_DFS bit
+ * flags in NetrShareEnum when the call is local and never will be set
+ * by remote calls. This point only serves the purpose of identifying
+ * the SOFS shares.
+ * The server returns the error code ERROR_INVALID_STATE if the share
+ * enumeration of SMB share resources fails with any error other than
+ * STATUS_SUCCESS. It’s not the absence of SOFS shares, or just the
+ * call to ShareEnum. When the server enumerates the shares by calling
+ * NetrShareEnum locally, it tries to filter out only shares with
+ * STYPE_CLUSTER_SOFS. The scope of 'If no matching entry is found'
+ * is broader. Even if shares have STYPE_CLUSTER_SOFS, but no match
+ * could be found with the IpAddress, ERROR_INVALID_STATE will be
+ * returned too.
+ *
+ * In a CTDB cluster, all shares in the clustered filesystem are
+ * scale-out. We can skip this check and proceed to find the matching
+ * IP address.
+ */
+ ok = is_ipaddress(r->in.ip_address);
+ if (!ok) {
+ DBG_INFO("Invalid ip_address[%s]\n",
+ log_escape(p->mem_ctx, r->in.ip_address));
+ return WERR_INVALID_STATE;
+ }
+ ok = interpret_string_addr(&addr.u.ss,
+ r->in.ip_address,
+ AI_PASSIVE|AI_NUMERICHOST);
+ if (!ok) {
+ DBG_INFO("Invalid ip_address[%s]\n",
+ log_escape(p->mem_ctx, r->in.ip_address));
+ return WERR_INVALID_STATE;
+ }
+ iface = swn_service_interface_by_addr(swn_globals, &addr);
+ if (iface == NULL) {
+ DBG_INFO("Invalid ip_address[%s]\n",
+ log_escape(p->mem_ctx, r->in.ip_address));
+ return WERR_INVALID_STATE;
+ }
+
+ werr = swn_server_registration_create(swn_globals, p, &rex, iface, &reg);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ *r->out.context_handle = reg->key.handle;
+ return WERR_OK;
+}
+
+
+/****************************************************************
+ _witness_UnRegister
+****************************************************************/
+
+WERROR _witness_UnRegister(struct pipes_struct *p,
+ struct witness_UnRegister *r)
+{
+ bool ok;
+
+ /*
+ * [MS-SWN] 3.1.4.3
+ * The server MUST search for the WitnessRegistration in
+ * WitnessRegistrationList, where WitnessRegistration.RegistrationKey
+ * matches the pContext parameter. If no matching entry is found,
+ * the server SHOULD<4> stop processing the request and return the
+ * error code ERROR_NOT_FOUND.
+ */
+ ok = close_policy_hnd(p, &r->in.context_handle);
+ if (!ok) {
+ if (p->fault_state != 0) {
+ p->fault_state = 0;
+ }
+ return WERR_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************
+ _witness_AsyncNotify
+****************************************************************/
+
+struct swn_service_async_notify_state {
+ struct swn_service_async_notify_state *prev, *next;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ TALLOC_CTX *r_mem_ctx;
+ struct witness_AsyncNotify *r;
+ struct swn_service_registration *reg;
+ struct tevent_queue_entry *qe;
+};
+
+static void swn_service_async_notify_trigger(struct tevent_req *req,
+ void *private_data);
+
+static void swn_service_async_notify_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct swn_service_async_notify_state *state =
+ tevent_req_data(req,
+ struct swn_service_async_notify_state);
+
+ TALLOC_FREE(state->qe);
+
+ if (state->reg != NULL) {
+ DLIST_REMOVE(state->reg->async_notify.list, state);
+ state->reg = NULL;
+ }
+}
+
+static void swn_service_async_notify_reg_destroyed(struct swn_service_async_notify_state *state)
+{
+ swn_service_async_notify_cleanup(state->req, TEVENT_REQ_USER_ERROR);
+ swn_service_async_notify_trigger(state->req, NULL);
+}
+
+static bool swn_service_async_notify_cancel(struct tevent_req *req)
+{
+ return false;
+}
+
+static struct tevent_req *swn_service_async_notify_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ TALLOC_CTX *r_mem_ctx,
+ struct witness_AsyncNotify *r,
+ struct swn_service_registration *reg)
+{
+ struct tevent_req *req = NULL;
+ struct swn_service_async_notify_state *state = NULL;
+ struct timeval now = timeval_current();
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct swn_service_async_notify_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->req = req;
+ state->r_mem_ctx = r_mem_ctx;
+ state->r = r;
+ state->reg = reg;
+
+ /*
+ * triggered changes likely wakeup
+ * more than one waiter, so we better
+ * let all individual waiters go through
+ * a tevent_immediate round.
+ */
+ tevent_req_defer_callback(req, ev);
+
+ tevent_req_set_cleanup_fn(req, swn_service_async_notify_cleanup);
+ tevent_req_set_cancel_fn(req, swn_service_async_notify_cancel);
+
+ if (!reg->forced_response.triggered &&
+ !reg->change_notification.triggered &&
+ !reg->move_notification.triggered &&
+ !reg->share_notification.triggered &&
+ !reg->ip_notification.triggered)
+ {
+ tevent_queue_stop(reg->async_notify.queue);
+ }
+
+ DLIST_ADD_END(reg->async_notify.list, state);
+
+ state->qe = tevent_queue_add_entry(reg->async_notify.queue, ev, req,
+ swn_service_async_notify_trigger,
+ NULL)
+ if (tevent_req_nomem(state->qe, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (reg->async_notify.timeout_secs != 0) {
+ struct timeval endtime;
+ bool ok;
+
+ endtime = timeval_add(&now, reg->async_notify.timeout_secs, 0);
+ ok = tevent_req_set_endtime(req, ev, endtime);
+ if (!ok) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ /*
+ * Once we added the queue entry
+ * swn_service_registration_update_usage()
+ * will adjust the registration expire time...
+ */
+ swn_service_registration_update_usage(state->reg, now);
+
+ /*
+ * Wait for trigger or timeout...
+ */
+ return req;
+}
+
+static void swn_service_async_notify_trigger(struct tevent_req *req,
+ void *private_data)
+{
+ struct swn_service_async_notify_state *state =
+ tevent_req_data(req,
+ struct swn_service_async_notify_state);
+ struct swn_service_registration *reg = state->reg;
+ struct witness_notifyResponse *resp = NULL;
+ WERROR forced_result = WERR_OK;
+ bool defer_forced_unregister = false;
+
+ if (reg == NULL) {
+ tevent_req_werror(req, WERR_NOT_FOUND);
+ return;
+ }
+
+ if (reg->forced_response.triggered) {
+ resp = talloc_move(state, &reg->forced_response.response);
+ forced_result = reg->forced_response.result;
+
+ reg->forced_response.triggered = false;
+ reg->forced_response.result = WERR_OK;
+ goto finished;
+ }
+
+ if (reg->change_notification.triggered) {
+ struct swn_service_globals *swn = reg->swn;
+ const struct swn_service_interface *iface = NULL;
+ union witness_notifyResponse_message *msgs = NULL;
+ char reg_ip[INET6_ADDRSTRLEN] = { 0, };
+ struct witness_ResourceChange *rc = NULL;
+ enum witness_interfaceInfo_state cur_state;
+
+ print_sockaddr(reg_ip, sizeof(reg_ip), &reg->ip_address.u.ss);
+
+ iface = swn_service_interface_by_addr(swn, &reg->ip_address);
+ if (iface != NULL) {
+ cur_state = iface->state;
+ } else {
+ /*
+ * If the interface is no longer in our list
+ * it must be unavailable
+ */
+ cur_state = WITNESS_STATE_UNAVAILABLE;
+ }
+ if (cur_state != WITNESS_STATE_AVAILABLE) {
+ reg->change_notification.last_ip_state = cur_state;
+ }
+
+ resp = talloc_zero(state, struct witness_notifyResponse);
+ if (tevent_req_nomem(resp, req)) {
+ return;
+ }
+
+ msgs = talloc_zero_array(resp,
+ union witness_notifyResponse_message,
+ 1);
+ if (tevent_req_nomem(msgs, req)) {
+ return;
+ }
+
+ resp->type = WITNESS_NOTIFY_RESOURCE_CHANGE;
+ resp->num = 0;
+ resp->messages = msgs;
+
+ rc = &msgs[resp->num].resource_change;
+
+ switch (reg->change_notification.last_ip_state) {
+ case WITNESS_STATE_AVAILABLE:
+ rc->type = WITNESS_RESOURCE_STATE_AVAILABLE;
+ break;
+ case WITNESS_STATE_UNAVAILABLE:
+ rc->type = WITNESS_RESOURCE_STATE_UNAVAILABLE;
+ break;
+ case WITNESS_STATE_UNKNOWN:
+ rc->type = WITNESS_RESOURCE_STATE_UNKNOWN;
+ break;
+ }
+
+ /*
+ * We use the ip address as resource name
+ */
+ rc->name = talloc_strdup(msgs, reg_ip);
+ if (tevent_req_nomem(rc->name, req)) {
+ return;
+ }
+
+ resp->num += 1;
+
+ if (rc->type != WITNESS_RESOURCE_STATE_AVAILABLE) {
+ /*
+ * In order to let a Windows server 2022
+ * correctly re-register after moving
+ * to a new connection, we force an
+ * unregistration after 5 seconds.
+ *
+ * It means the client gets WERR_NOT_FOUND
+ * from a pending AsyncNotify() and calls
+ * Unregister() (which also gets WERR_NOT_FOUND).
+ * Then the client calls GetInterfaceList()
+ * and RegisterEx() again.
+ */
+ defer_forced_unregister = true;
+ }
+
+ if (reg->change_notification.last_ip_state != cur_state) {
+ /*
+ * This means the last_ip_state was *not* available,
+ * and the current_state *is* available.
+ *
+ * keep the queue running and return the available
+ * message in the next run
+ */
+ reg->change_notification.last_ip_state = cur_state;
+ goto finished;
+ }
+
+ reg->change_notification.triggered = false;
+ reg->change_notification.last_ip_state = WITNESS_STATE_UNKNOWN;
+ goto finished;
+ }
+
+ if (reg->move_notification.triggered) {
+ struct swn_service_globals *swn = reg->swn;
+ struct swn_service_interface *iface = NULL;
+ union witness_notifyResponse_message *msgs = NULL;
+ struct witness_IPaddrInfoList *list = NULL;
+ uint32_t num_ips = 0;
+ const uint32_t *new_node = NULL;
+ const struct samba_sockaddr *new_ip = NULL;
+
+ if (reg->move_notification.new_node != NONCLUSTER_VNN) {
+ new_node = &reg->move_notification.new_node;
+ }
+ if (!is_zero_addr(&reg->move_notification.new_ip.u.ss)) {
+ new_ip = &reg->move_notification.new_ip;
+ }
+
+ for (iface = swn->interfaces.list;
+ iface != NULL;
+ iface = iface->next)
+ {
+ if (new_node != NULL &&
+ iface->current_vnn != *new_node)
+ {
+ continue;
+ }
+
+ if (new_ip != NULL &&
+ !sockaddr_equal(&new_ip->u.sa, &iface->addr.u.sa))
+ {
+ continue;
+ }
+
+ num_ips += 1;
+ }
+
+ if (num_ips == 0) {
+ goto no_moves;
+ }
+
+ resp = talloc_zero(state, struct witness_notifyResponse);
+ if (tevent_req_nomem(resp, req)) {
+ return;
+ }
+
+ msgs = talloc_zero_array(resp,
+ union witness_notifyResponse_message,
+ 1);
+ if (tevent_req_nomem(msgs, req)) {
+ return;
+ }
+
+ list = &msgs[0].client_move;
+ list->addr = talloc_zero_array(msgs,
+ struct witness_IPaddrInfo,
+ num_ips);
+ if (tevent_req_nomem(list->addr, req)) {
+ return;
+ }
+
+ for (iface = swn->interfaces.list;
+ iface != NULL;
+ iface = iface->next)
+ {
+ struct witness_IPaddrInfo *info = &list->addr[list->num];
+ char addr[INET6_ADDRSTRLEN] = { 0, };
+ const char *ipv4 = "0.0.0.0";
+ const char *ipv6 = "::";
+ uint32_t flags = 0;
+ bool is_reg_ip = false;
+
+ if (new_node != NULL &&
+ iface->current_vnn != *new_node)
+ {
+ continue;
+ }
+
+ if (new_ip != NULL &&
+ !sockaddr_equal(&new_ip->u.sa, &iface->addr.u.sa))
+ {
+ continue;
+ }
+
+ switch (iface->state) {
+ case WITNESS_STATE_AVAILABLE:
+ flags |= WITNESS_IPADDR_ONLINE;
+ break;
+ case WITNESS_STATE_UNAVAILABLE:
+ flags |= WITNESS_IPADDR_OFFLINE;
+ break;
+ case WITNESS_STATE_UNKNOWN:
+ /* We map unknown also to offline */
+ flags |= WITNESS_IPADDR_OFFLINE;
+ break;
+ }
+
+ print_sockaddr(addr, sizeof(addr), &iface->addr.u.ss);
+ if (iface->addr.u.sa.sa_family == AF_INET) {
+ flags |= WITNESS_IPADDR_V4;
+ ipv4 = addr;
+ } else if (iface->addr.u.sa.sa_family == AF_INET6) {
+ flags |= WITNESS_IPADDR_V6;
+ ipv6 = addr;
+ }
+
+ info->ipv4 = talloc_strdup(list, ipv4);
+ if (tevent_req_nomem(info->ipv4, req)) {
+ return;
+ }
+ info->ipv6 = talloc_strdup(list, ipv6);
+ if (tevent_req_nomem(info->ipv6, req)) {
+ return;
+ }
+ info->flags = flags;
+ list->num += 1;
+
+ is_reg_ip = sockaddr_equal(&reg->ip_address.u.sa,
+ &iface->addr.u.sa);
+ if (!is_reg_ip) {
+ /*
+ * In order to let a Windows server 2022
+ * correctly re-register after moving
+ * to a new connection, we force an
+ * unregistration after 5 seconds.
+ *
+ * It means the client gets WERR_NOT_FOUND from
+ * a pending AsyncNotify() and calls
+ * Unregister() (which also gets
+ * WERR_NOT_FOUND). Then the client calls
+ * GetInterfaceList() and RegisterEx() again.
+ */
+ defer_forced_unregister = true;
+ }
+ }
+
+ resp->type = WITNESS_NOTIFY_CLIENT_MOVE;
+ resp->num = talloc_array_length(msgs);
+ resp->messages = msgs;
+
+no_moves:
+ reg->move_notification.triggered = false;
+ if (resp != NULL) {
+ goto finished;
+ }
+ }
+
+ if (reg->share_notification.triggered) {
+ struct swn_service_globals *swn = reg->swn;
+ struct swn_service_interface *iface = NULL;
+ union witness_notifyResponse_message *msgs = NULL;
+ struct witness_IPaddrInfoList *list = NULL;
+ uint32_t num_ips = 0;
+ const uint32_t *new_node = NULL;
+ const struct samba_sockaddr *new_ip = NULL;
+
+ if (reg->share_notification.new_node != NONCLUSTER_VNN) {
+ new_node = &reg->share_notification.new_node;
+ }
+ if (!is_zero_addr(&reg->share_notification.new_ip.u.ss)) {
+ new_ip = &reg->share_notification.new_ip;
+ }
+
+ for (iface = swn->interfaces.list;
+ iface != NULL;
+ iface = iface->next)
+ {
+ if (new_node != NULL &&
+ iface->current_vnn != *new_node)
+ {
+ continue;
+ }
+
+ if (new_ip != NULL &&
+ !sockaddr_equal(&new_ip->u.sa, &iface->addr.u.sa))
+ {
+ continue;
+ }
+
+ num_ips += 1;
+ }
+
+ if (num_ips == 0) {
+ goto no_share_moves;
+ }
+
+ resp = talloc_zero(state, struct witness_notifyResponse);
+ if (tevent_req_nomem(resp, req)) {
+ return;
+ }
+
+ msgs = talloc_zero_array(resp,
+ union witness_notifyResponse_message,
+ 1);
+ if (tevent_req_nomem(msgs, req)) {
+ return;
+ }
+
+ list = &msgs[0].client_move;
+ list->addr = talloc_zero_array(msgs,
+ struct witness_IPaddrInfo,
+ num_ips);
+ if (tevent_req_nomem(list->addr, req)) {
+ return;
+ }
+
+ for (iface = swn->interfaces.list;
+ iface != NULL;
+ iface = iface->next)
+ {
+ struct witness_IPaddrInfo *info = &list->addr[list->num];
+ char addr[INET6_ADDRSTRLEN] = { 0, };
+ const char *ipv4 = "0.0.0.0";
+ const char *ipv6 = "::";
+ uint32_t flags = 0;
+ bool is_reg_ip = false;
+
+ if (new_node != NULL &&
+ iface->current_vnn != *new_node)
+ {
+ continue;
+ }
+
+ if (new_ip != NULL &&
+ !sockaddr_equal(&new_ip->u.sa, &iface->addr.u.sa))
+ {
+ continue;
+ }
+
+ switch (iface->state) {
+ case WITNESS_STATE_AVAILABLE:
+ flags |= WITNESS_IPADDR_ONLINE;
+ break;
+ case WITNESS_STATE_UNAVAILABLE:
+ flags |= WITNESS_IPADDR_OFFLINE;
+ break;
+ case WITNESS_STATE_UNKNOWN:
+ /* We map unknown also to offline */
+ flags |= WITNESS_IPADDR_OFFLINE;
+ break;
+ }
+
+ print_sockaddr(addr, sizeof(addr), &iface->addr.u.ss);
+ if (iface->addr.u.sa.sa_family == AF_INET) {
+ flags |= WITNESS_IPADDR_V4;
+ ipv4 = addr;
+ } else if (iface->addr.u.sa.sa_family == AF_INET6) {
+ flags |= WITNESS_IPADDR_V6;
+ ipv6 = addr;
+ }
+
+ info->ipv4 = talloc_strdup(list, ipv4);
+ if (tevent_req_nomem(info->ipv4, req)) {
+ return;
+ }
+ info->ipv6 = talloc_strdup(list, ipv6);
+ if (tevent_req_nomem(info->ipv6, req)) {
+ return;
+ }
+ info->flags = flags;
+ list->num += 1;
+
+ is_reg_ip = sockaddr_equal(&reg->ip_address.u.sa,
+ &iface->addr.u.sa);
+ if (!is_reg_ip) {
+ /*
+ * In order to let a Windows server 2022
+ * correctly re-register after moving
+ * to a new connection, we force an
+ * unregistration after 5 seconds.
+ *
+ * It means the client gets WERR_NOT_FOUND from
+ * a pending AsyncNotify() and calls
+ * Unregister() (which also gets
+ * WERR_NOT_FOUND). Then the client calls
+ * GetInterfaceList() and RegisterEx() again.
+ */
+ defer_forced_unregister = true;
+ }
+ }
+
+ resp->type = WITNESS_NOTIFY_SHARE_MOVE;
+ resp->num = talloc_array_length(msgs);
+ resp->messages = msgs;
+
+no_share_moves:
+ reg->share_notification.triggered = false;
+ if (resp != NULL) {
+ goto finished;
+ }
+ }
+
+finished:
+ if (!reg->forced_response.triggered &&
+ !reg->change_notification.triggered &&
+ !reg->move_notification.triggered &&
+ !reg->share_notification.triggered &&
+ !reg->ip_notification.triggered)
+ {
+ tevent_queue_stop(reg->async_notify.queue);
+ }
+
+ if (defer_forced_unregister) {
+ struct tevent_timer *te = NULL;
+
+ /*
+ * In order to let a Windows server 2022
+ * correctly re-register after moving
+ * to a new connection, we force an
+ * unregistration after 5 seconds.
+ *
+ * It means the client gets WERR_NOT_FOUND
+ * from a pending AsyncNotify() and calls
+ * Unregister() (which also gets WERR_NOT_FOUND).
+ * Then the client calls GetInterfaceList()
+ * and RegisterEx() again.
+ */
+ TALLOC_FREE(reg->forced_unregister.timer);
+ te = tevent_add_timer(state->ev,
+ reg,
+ timeval_current_ofs(5,0),
+ swn_service_registration_force_unregister,
+ reg);
+ if (tevent_req_nomem(te, req)) {
+ return;
+ }
+ reg->forced_unregister.timer = te;
+ }
+
+ *state->r->out.response = talloc_move(state->r_mem_ctx, &resp);
+ state->r->out.result = forced_result;
+ if (!W_ERROR_IS_OK(forced_result)) {
+ tevent_req_werror(req, forced_result);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static WERROR swn_service_async_notify_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_werror(req);
+}
+
+struct _witness_AsyncNotify_state {
+ struct dcesrv_call_state *dce_call;
+ struct witness_AsyncNotify *r;
+ struct swn_service_registration *reg;
+ struct tevent_req *subreq;
+};
+
+static bool _witness_AsyncNotify_cancel(struct tevent_req *req);
+static void _witness_AsyncNotify_done(struct tevent_req *subreq);
+
+WERROR _witness_AsyncNotify(struct pipes_struct *p,
+ struct witness_AsyncNotify *r)
+{
+ struct tevent_req *req = NULL;
+ struct _witness_AsyncNotify_state *state = NULL;
+ struct swn_service_registration *reg = NULL;
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+ /*
+ * [MS-SWN] 3.1.4.4
+ * The server MUST search for the WitnessRegistration in
+ * WitnessRegistrationList, where WitnessRegistration.RegistrationKey
+ * matches the pContext parameter. If no matching entry is found, the
+ * server MUST fail the request and return the error code
+ * ERROR_NOT_FOUND.
+ */
+ reg = find_policy_by_hnd(p, &r->in.context_handle,
+ SWN_SERVICE_CONTEXT_HANDLE_REGISTRATION,
+ struct swn_service_registration,
+ &status);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (p->fault_state != 0) {
+ p->fault_state = 0;
+ }
+ return WERR_NOT_FOUND;
+ }
+
+ swn_service_registration_update_usage(reg, p->dce_call->time);
+
+ req = tevent_req_create(p->mem_ctx, &state,
+ struct _witness_AsyncNotify_state);
+ if (req == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->dce_call = p->dce_call;
+ state->r = r;
+ state->reg = reg;
+
+ tevent_req_set_cancel_fn(req, _witness_AsyncNotify_cancel);
+
+ state->subreq = swn_service_async_notify_send(state,
+ state->dce_call->event_ctx,
+ state->dce_call,
+ state->r,
+ state->reg);
+ if (state->subreq == NULL) {
+ TALLOC_FREE(state);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tevent_req_set_callback(state->subreq,
+ _witness_AsyncNotify_done,
+ req);
+
+ state->dce_call->subreq = req;
+ state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ return WERR_EVENT_PENDING; /* hidden by DCESRV_CALL_STATE_FLAG_ASYNC */
+}
+
+static bool _witness_AsyncNotify_cancel(struct tevent_req *req)
+{
+ struct _witness_AsyncNotify_state *state =
+ tevent_req_data(req,
+ struct _witness_AsyncNotify_state);
+ struct dcesrv_call_state *dce_call = state->dce_call;
+
+ SMB_ASSERT(dce_call->subreq == req);
+ dce_call->subreq = NULL;
+
+ TALLOC_FREE(state->subreq);
+
+ if (dce_call->got_orphaned) {
+ dce_call->fault_code = DCERPC_FAULT_SERVER_UNAVAILABLE;
+ } else {
+ dce_call->fault_code = DCERPC_NCA_S_FAULT_CANCEL;
+ }
+ state->r->out.result = WERR_RPC_S_CALL_CANCELLED;
+
+ dcesrv_async_reply(dce_call);
+ return true;
+}
+
+static void _witness_AsyncNotify_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct _witness_AsyncNotify_state *state =
+ tevent_req_data(req,
+ struct _witness_AsyncNotify_state);
+ struct dcesrv_call_state *dce_call = state->dce_call;
+
+ SMB_ASSERT(dce_call->subreq == req);
+ dce_call->subreq = NULL;
+
+ SMB_ASSERT(state->subreq == subreq);
+ state->subreq = NULL;
+
+ state->r->out.result = swn_service_async_notify_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (W_ERROR_EQUAL(state->r->out.result, WERR_NOT_FOUND)) {
+ state->reg = NULL;
+ }
+
+ if (state->reg != NULL &&
+ tevent_queue_length(state->reg->async_notify.queue) == 0)
+ {
+ struct timeval now = timeval_current();
+ swn_service_registration_update_usage(state->reg, now);
+ }
+
+ dcesrv_async_reply(dce_call);
+}
+
+/****************************************************************
+ _witness_RegisterEx
+****************************************************************/
+
+WERROR _witness_RegisterEx(struct pipes_struct *p,
+ struct witness_RegisterEx *r)
+{
+ struct dcesrv_context *dce_ctx = p->dce_call->conn->dce_ctx;
+ struct swn_service_registration *reg = NULL;
+ struct samba_sockaddr addr = { .sa_socklen = 0, };
+ struct swn_service_interface *iface = NULL;
+ NTSTATUS status;
+ WERROR werr;
+ bool ok;
+
+ /*
+ * [MS-SWN] 3.1.4.5
+ * If the Version field of the request is not 0x00020000, the server
+ * MUST stop processing the request and return the error code
+ * ERROR_REVISION_MISMATCH
+ */
+ if (r->in.version != WITNESS_V2) {
+ return WERR_REVISION_MISMATCH;
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.5
+ * If NetName, IpAddress or ClientComputerName is NULL, the server
+ * MUST fail the request and return the error code
+ * ERROR_INVALID_PARAMETER
+ */
+ if (r->in.net_name == NULL ||
+ r->in.ip_address == NULL ||
+ r->in.client_computer_name == NULL)
+ {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ status = swn_service_reload_interfaces(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.5
+ * If the NetName parameter is not equal to ServerGlobalName, the
+ * server MUST fail the request and return the error code
+ * ERROR_INVALID_PARAMETER
+ */
+ werr = swn_server_check_net_name(swn_globals, r->in.net_name);
+ if (!W_ERROR_IS_OK(werr)) {
+ DBG_INFO("Invalid net_name[%s], "
+ "server_global_name[%s]: %s\n",
+ log_escape(p->mem_ctx, r->in.net_name),
+ swn_globals->server_global_name,
+ win_errstr(werr));
+ return werr;
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.5
+ * If ShareName is not NULL, the server MUST enumerate the shares by
+ * calling NetrShareEnum as specified in [MS-SRVS] section 3.1.4.8.
+ * If the enumeration fails or if no shares are returned, the server
+ * MUST return the error code ERROR_INVALID_STATE.
+ *
+ * If none of the shares in the list has shi*_type set to
+ * STYPE_CLUSTER_SOFS as specified in [MS-SRVS] section 3.1.4.8,
+ * the server MUST ignore ShareName.
+ *
+ * In a CTDB cluster, all shares in the clustered filesystem are
+ * scale-out. Check if the provided share name is in a clustered FS
+ */
+ if (r->in.share_name != NULL) {
+ char *save_share = NULL;
+ int cmp;
+
+ /*
+ * For now we allow all shares...
+ *
+ * The main reason is that windows
+ * clients typically connect as
+ * machine account, so things like %U
+ * wouldn't work anyway.
+ *
+ * And in the end it's just a string,
+ * so we just check it's sane.
+ */
+ save_share = log_escape(p->mem_ctx, r->in.share_name);
+ if (save_share == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ cmp = strcmp(save_share, r->in.share_name);
+ if (cmp != 0) {
+ DBG_INFO("Invalid share_name[%s]\n",
+ save_share);
+ return WERR_INVALID_STATE;
+ }
+ TALLOC_FREE(save_share);
+ }
+
+ /*
+ * [MS-SWN] 3.1.4.5
+ * The server MUST search for an Interface in InterfaceList, where
+ * Interface.IPv4Address or Interface.IPv6Address matches the
+ * IpAddress parameter based on its format. If no matching entry is
+ * found, the server MUST fail the request and return the error code
+ * ERROR_INVALID_STATE.
+ */
+ ok = is_ipaddress(r->in.ip_address);
+ if (!ok) {
+ DBG_INFO("Invalid ip_address[%s]\n",
+ log_escape(p->mem_ctx, r->in.ip_address));
+ return WERR_INVALID_STATE;
+ }
+ ok = interpret_string_addr(&addr.u.ss,
+ r->in.ip_address,
+ AI_PASSIVE|AI_NUMERICHOST);
+ if (!ok) {
+ DBG_INFO("Invalid ip_address[%s]\n",
+ log_escape(p->mem_ctx, r->in.ip_address));
+ return WERR_INVALID_STATE;
+ }
+ iface = swn_service_interface_by_addr(swn_globals, &addr);
+ if (iface == NULL) {
+ DBG_INFO("Invalid ip_address[%s]\n",
+ log_escape(p->mem_ctx, r->in.ip_address));
+ return WERR_INVALID_STATE;
+ }
+
+ werr = swn_server_registration_create(swn_globals, p, r, iface, &reg);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ *r->out.context_handle = reg->key.handle;
+ return WERR_OK;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_witness_scompat.c"
diff --git a/source3/rpc_server/wkssvc/srv_wkssvc_nt.c b/source3/rpc_server/wkssvc/srv_wkssvc_nt.c
new file mode 100644
index 0000000..0724dd0
--- /dev/null
+++ b/source3/rpc_server/wkssvc/srv_wkssvc_nt.c
@@ -0,0 +1,964 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Andrew Tridgell 1992-1997,
+ * Copyright (C) Gerald (Jerry) Carter 2006.
+ * Copyright (C) Guenther Deschner 2007-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/>.
+ */
+
+/* This is the implementation of the wks interface. */
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/libnet_join.h"
+#include "libnet/libnet_join.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "librpc/gen_ndr/ndr_wkssvc_scompat.h"
+#include "../libcli/security/security.h"
+#include "session.h"
+#include "smbd/smbd.h"
+#include "auth.h"
+#include "krb5_env.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+struct dom_usr {
+ char *name;
+ char *domain;
+ time_t login_time;
+};
+
+static int dom_user_cmp(const struct dom_usr *usr1, const struct dom_usr *usr2)
+{
+ /* Called from qsort to compare two domain users in a dom_usr_t array
+ * for sorting by login time. Return >0 if usr1 login time was later
+ * than usr2 login time, <0 if it was earlier */
+ return (usr1->login_time - usr2->login_time);
+}
+
+/*******************************************************************
+ Get a list of the names of all users of this machine who are
+ logged into the domain.
+
+ This should return a list of the users on this machine who are
+ logged into the domain (i.e. have been authenticated by the domain's
+ password server) but that doesn't fit well with the normal Samba
+ scenario where accesses out to the domain are made through smbclient
+ with each such session individually authenticated. So about the best
+ we can do currently is to list sessions of local users connected to
+ this server, which means that to get themself included in the list a
+ local user must create a session to the local samba server by running:
+ smbclient \\\\localhost\\share
+
+ FIXME: find a better way to get local users logged into the domain
+ in this list.
+ ********************************************************************/
+
+static int get_domain_userlist(TALLOC_CTX *mem_ctx, struct dom_usr **pusers)
+{
+ struct sessionid *session_list = NULL;
+ char *machine_name, *p, *nm;
+ const char *sep;
+ struct dom_usr *users, *tmp;
+ int i, num_users, num_sessions;
+
+ sep = lp_winbind_separator();
+ if (!sep) {
+ sep = "\\";
+ }
+
+ num_sessions = list_sessions(mem_ctx, &session_list);
+ if (num_sessions == 0) {
+ *pusers = NULL;
+ return 0;
+ }
+
+ users = talloc_array(mem_ctx, struct dom_usr, num_sessions);
+ if (users == NULL) {
+ TALLOC_FREE(session_list);
+ return ENOMEM;
+ }
+
+ for (i=num_users=0; i<num_sessions; i++) {
+ if (session_list[i].username[0] == '\0' ||
+ session_list[i].remote_machine[0] == '\0') {
+ continue;
+ }
+ p = strpbrk(session_list[i].remote_machine, "./");
+ if (p) {
+ *p = '\0';
+ }
+ machine_name = talloc_asprintf_strupper_m(
+ users, "%s", session_list[i].remote_machine);
+ if (machine_name == NULL) {
+ DEBUG(10, ("talloc_asprintf failed\n"));
+ continue;
+ }
+ if (strcmp(machine_name, lp_netbios_name()) == 0) {
+ p = session_list[i].username;
+ nm = strstr(p, sep);
+ if (nm) {
+ /*
+ * "domain+name" format so split domain and
+ * name components
+ */
+ *nm = '\0';
+ nm += strlen(sep);
+ users[num_users].domain =
+ talloc_asprintf_strupper_m(users,
+ "%s", p);
+ users[num_users].name = talloc_strdup(users,
+ nm);
+ } else {
+ /*
+ * Simple user name so get domain from smb.conf
+ */
+ users[num_users].domain =
+ talloc_strdup(users, lp_workgroup());
+ users[num_users].name = talloc_strdup(users,
+ p);
+ }
+ users[num_users].login_time =
+ session_list[i].connect_start;
+ num_users++;
+ }
+ TALLOC_FREE(machine_name);
+ }
+ TALLOC_FREE(session_list);
+
+ if (num_users == 0) {
+ TALLOC_FREE(users);
+ *pusers = NULL;
+ return 0;
+ }
+
+ tmp = talloc_realloc(mem_ctx, users, struct dom_usr, num_users);
+ if (tmp == NULL) {
+ TALLOC_FREE(users);
+ return ENOMEM;
+ }
+ users = tmp;
+
+ /* Sort the user list by time, oldest first */
+ TYPESAFE_QSORT(users, num_users, dom_user_cmp);
+
+ *pusers = users;
+ return 0;
+}
+
+/*******************************************************************
+ RPC Workstation Service request NetWkstaGetInfo with level 100.
+ Returns to the requester:
+ - The machine name.
+ - The smb version number
+ - The domain name.
+ Returns a filled in wkssvc_NetWkstaInfo100 struct.
+ ********************************************************************/
+
+static struct wkssvc_NetWkstaInfo100 *create_wks_info_100(TALLOC_CTX *mem_ctx)
+{
+ struct wkssvc_NetWkstaInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo100);
+ if (info100 == NULL) {
+ return NULL;
+ }
+
+ info100->platform_id = PLATFORM_ID_NT; /* unknown */
+ info100->version_major = SAMBA_MAJOR_NBT_ANNOUNCE_VERSION;
+ info100->version_minor = SAMBA_MINOR_NBT_ANNOUNCE_VERSION;
+
+ info100->server_name = talloc_asprintf_strupper_m(
+ info100, "%s", lp_netbios_name());
+ info100->domain_name = talloc_asprintf_strupper_m(
+ info100, "%s", lp_workgroup());
+
+ return info100;
+}
+
+/*******************************************************************
+ RPC Workstation Service request NetWkstaGetInfo with level 101.
+ Returns to the requester:
+ - As per NetWkstaGetInfo with level 100, plus:
+ - The LANMAN directory path (not currently supported).
+ Returns a filled in wkssvc_NetWkstaInfo101 struct.
+ ********************************************************************/
+
+static struct wkssvc_NetWkstaInfo101 *create_wks_info_101(TALLOC_CTX *mem_ctx)
+{
+ struct wkssvc_NetWkstaInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo101);
+ if (info101 == NULL) {
+ return NULL;
+ }
+
+ info101->platform_id = PLATFORM_ID_NT; /* unknown */
+ info101->version_major = SAMBA_MAJOR_NBT_ANNOUNCE_VERSION;
+ info101->version_minor = SAMBA_MINOR_NBT_ANNOUNCE_VERSION;
+
+ info101->server_name = talloc_asprintf_strupper_m(
+ info101, "%s", lp_netbios_name());
+ info101->domain_name = talloc_asprintf_strupper_m(
+ info101, "%s", lp_workgroup());
+ info101->lan_root = "";
+
+ return info101;
+}
+
+/*******************************************************************
+ RPC Workstation Service request NetWkstaGetInfo with level 102.
+ Returns to the requester:
+ - As per NetWkstaGetInfo with level 101, plus:
+ - The number of logged in users.
+ Returns a filled in wkssvc_NetWkstaInfo102 struct.
+ ********************************************************************/
+
+static struct wkssvc_NetWkstaInfo102 *create_wks_info_102(TALLOC_CTX *mem_ctx)
+{
+ struct wkssvc_NetWkstaInfo102 *info102;
+
+ info102 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo102);
+ if (info102 == NULL) {
+ return NULL;
+ }
+
+ info102->platform_id = PLATFORM_ID_NT; /* unknown */
+ info102->version_major = SAMBA_MAJOR_NBT_ANNOUNCE_VERSION;
+ info102->version_minor = SAMBA_MINOR_NBT_ANNOUNCE_VERSION;
+
+ info102->server_name = talloc_asprintf_strupper_m(
+ info102, "%s", lp_netbios_name());
+ info102->domain_name = talloc_asprintf_strupper_m(
+ info102, "%s", lp_workgroup());
+ info102->lan_root = "";
+ info102->logged_on_users = 0;
+
+ return info102;
+}
+
+/********************************************************************
+ Handling for RPC Workstation Service request NetWkstaGetInfo
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaGetInfo(struct pipes_struct *p,
+ struct wkssvc_NetWkstaGetInfo *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct dom_sid_buf buf;
+
+ switch (r->in.level) {
+ case 100:
+ /* Level 100 can be allowed from anyone including anonymous
+ * so no access checks are needed for this case */
+ r->out.info->info100 = create_wks_info_100(p->mem_ctx);
+ if (r->out.info->info100 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ case 101:
+ /* Level 101 can be allowed from any logged in user */
+ if (!nt_token_check_sid(&global_sid_Authenticated_Users,
+ session_info->security_token)) {
+ DEBUG(1,("User not allowed for NetWkstaGetInfo level "
+ "101\n"));
+ DEBUGADD(3,(" - does not have sid for Authenticated "
+ "Users %s:\n",
+ dom_sid_str_buf(
+ &global_sid_Authenticated_Users,
+ &buf)));
+ security_token_debug(DBGC_CLASS, 3,
+ session_info->security_token);
+ return WERR_ACCESS_DENIED;
+ }
+ r->out.info->info101 = create_wks_info_101(p->mem_ctx);
+ if (r->out.info->info101 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ case 102:
+ /* Level 102 Should only be allowed from a domain administrator */
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ session_info->security_token)) {
+ DEBUG(1,("User not allowed for NetWkstaGetInfo level "
+ "102\n"));
+ DEBUGADD(3,(" - does not have sid for Administrators "
+ "group %s, sids are:\n",
+ dom_sid_str_buf(
+ &global_sid_Builtin_Administrators,
+ &buf)));
+ security_token_debug(DBGC_CLASS, 3,
+ session_info->security_token);
+ return WERR_ACCESS_DENIED;
+ }
+ r->out.info->info102 = create_wks_info_102(p->mem_ctx);
+ if (r->out.info->info102 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaSetInfo(struct pipes_struct *p,
+ struct wkssvc_NetWkstaSetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ RPC Workstation Service request NetWkstaEnumUsers with level 0:
+ Returns to the requester:
+ - the user names of the logged in users.
+ Returns a filled in wkssvc_NetWkstaEnumUsersCtr0 struct.
+ ********************************************************************/
+
+static struct wkssvc_NetWkstaEnumUsersCtr0 *create_enum_users0(
+ TALLOC_CTX *mem_ctx)
+{
+ struct wkssvc_NetWkstaEnumUsersCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct wkssvc_NetWkstaEnumUsersCtr0);
+ if (ctr0 == NULL) {
+ return NULL;
+ }
+
+ ctr0->entries_read = 0;
+ ctr0->user0 = talloc_array(ctr0, struct wkssvc_NetrWkstaUserInfo0, 0);
+ if (ctr0->user0 == NULL) {
+ TALLOC_FREE(ctr0);
+ return NULL;
+ }
+
+ return ctr0;
+}
+
+/********************************************************************
+ RPC Workstation Service request NetWkstaEnumUsers with level 1.
+ Returns to the requester:
+ - the user names of the logged in users,
+ - the domain or machine each is logged into,
+ - the password server that was used to authenticate each,
+ - other domains each user is logged into (not currently supported).
+ Returns a filled in wkssvc_NetWkstaEnumUsersCtr1 struct.
+ ********************************************************************/
+
+static struct wkssvc_NetWkstaEnumUsersCtr1 *create_enum_users1(
+ TALLOC_CTX *mem_ctx)
+{
+ struct wkssvc_NetWkstaEnumUsersCtr1 *ctr1;
+ struct dom_usr *dom_users;
+ const char *pwd_server;
+ char *pwd_tmp;
+ int i, num_dom_users, ret;
+
+ ctr1 = talloc(mem_ctx, struct wkssvc_NetWkstaEnumUsersCtr1);
+ if (ctr1 == NULL) {
+ return NULL;
+ }
+
+ ret = get_domain_userlist(talloc_tos(), &dom_users);
+ if (ret != 0) {
+ TALLOC_FREE(ctr1);
+ errno = ret;
+ return NULL;
+ }
+ num_dom_users = talloc_array_length(dom_users);
+
+ ctr1->user1 = talloc_array(ctr1, struct wkssvc_NetrWkstaUserInfo1,
+ num_dom_users);
+ if (ctr1->user1 == NULL) {
+ TALLOC_FREE(ctr1);
+ TALLOC_FREE(dom_users);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ pwd_server = "";
+
+ if ((pwd_tmp = talloc_strdup(ctr1->user1, lp_password_server()))) {
+ /* The configured password server is a full DNS name but
+ * for the logon server we need to return just the first
+ * component (machine name) of it in upper-case */
+ char *p = strchr(pwd_tmp, '.');
+ if (p) {
+ *p = '\0';
+ } else {
+ p = pwd_tmp + strlen(pwd_tmp);
+ }
+ while (--p >= pwd_tmp) {
+ *p = toupper(*p);
+ }
+ pwd_server = pwd_tmp;
+ }
+
+ /* Now domain users */
+ for (i=0; i<num_dom_users; i++) {
+ ctr1->user1[i].user_name =
+ talloc_strdup(ctr1->user1, dom_users[i].name);
+ ctr1->user1[i].logon_domain =
+ talloc_strdup(ctr1->user1, dom_users[i].domain);
+ ctr1->user1[i].logon_server = pwd_server;
+
+ ctr1->user1[i++].other_domains = NULL; /* Maybe in future? */
+ }
+
+ ctr1->entries_read = i;
+
+ TALLOC_FREE(dom_users);
+ return ctr1;
+}
+
+/********************************************************************
+ Handling for RPC Workstation Service request NetWkstaEnumUsers
+ (a.k.a Windows NetWkstaUserEnum)
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaEnumUsers(struct pipes_struct *p,
+ struct wkssvc_NetWkstaEnumUsers *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+
+ /* This with any level should only be allowed from a domain administrator */
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ session_info->security_token)) {
+ struct dom_sid_buf buf;
+ DEBUG(1,("User not allowed for NetWkstaEnumUsers\n"));
+ DEBUGADD(3,(" - does not have sid for Administrators group "
+ "%s\n",
+ dom_sid_str_buf(
+ &global_sid_Builtin_Administrators,
+ &buf)));
+ security_token_debug(
+ DBGC_CLASS, 3, session_info->security_token);
+ return WERR_ACCESS_DENIED;
+ }
+
+ switch (r->in.info->level) {
+ case 0:
+ r->out.info->ctr.user0 = create_enum_users0(p->mem_ctx);
+ if (r->out.info->ctr.user0 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ r->out.info->level = r->in.info->level;
+ *r->out.entries_read = r->out.info->ctr.user0->entries_read;
+ if (r->out.resume_handle != NULL) {
+ *r->out.resume_handle = 0;
+ }
+ break;
+ case 1:
+ r->out.info->ctr.user1 = create_enum_users1(p->mem_ctx);
+ if (r->out.info->ctr.user1 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ r->out.info->level = r->in.info->level;
+ *r->out.entries_read = r->out.info->ctr.user1->entries_read;
+ if (r->out.resume_handle != NULL) {
+ *r->out.resume_handle = 0;
+ }
+ break;
+ default:
+ return WERR_INVALID_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaUserGetInfo(struct pipes_struct *p,
+ struct wkssvc_NetrWkstaUserGetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaUserSetInfo(struct pipes_struct *p,
+ struct wkssvc_NetrWkstaUserSetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaTransportEnum(struct pipes_struct *p,
+ struct wkssvc_NetWkstaTransportEnum *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaTransportAdd(struct pipes_struct *p,
+ struct wkssvc_NetrWkstaTransportAdd *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaTransportDel(struct pipes_struct *p,
+ struct wkssvc_NetrWkstaTransportDel *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseAdd(struct pipes_struct *p,
+ struct wkssvc_NetrUseAdd *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseGetInfo(struct pipes_struct *p,
+ struct wkssvc_NetrUseGetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseDel(struct pipes_struct *p,
+ struct wkssvc_NetrUseDel *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseEnum(struct pipes_struct *p,
+ struct wkssvc_NetrUseEnum *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrMessageBufferSend(struct pipes_struct *p,
+ struct wkssvc_NetrMessageBufferSend *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWorkstationStatisticsGet(struct pipes_struct *p,
+ struct wkssvc_NetrWorkstationStatisticsGet *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrLogonDomainNameAdd(struct pipes_struct *p,
+ struct wkssvc_NetrLogonDomainNameAdd *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrLogonDomainNameDel(struct pipes_struct *p,
+ struct wkssvc_NetrLogonDomainNameDel *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrJoinDomain(struct pipes_struct *p,
+ struct wkssvc_NetrJoinDomain *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUnjoinDomain(struct pipes_struct *p,
+ struct wkssvc_NetrUnjoinDomain *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrRenameMachineInDomain(struct pipes_struct *p,
+ struct wkssvc_NetrRenameMachineInDomain *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrValidateName(struct pipes_struct *p,
+ struct wkssvc_NetrValidateName *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrGetJoinInformation(struct pipes_struct *p,
+ struct wkssvc_NetrGetJoinInformation *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrGetJoinableOus(struct pipes_struct *p,
+ struct wkssvc_NetrGetJoinableOus *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ _wkssvc_NetrJoinDomain2
+ ********************************************************************/
+
+WERROR _wkssvc_NetrJoinDomain2(struct pipes_struct *p,
+ struct wkssvc_NetrJoinDomain2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct libnet_JoinCtx *j = NULL;
+ char *cleartext_pwd = NULL;
+ char *admin_domain = NULL;
+ char *admin_account = NULL;
+ WERROR werr;
+ struct security_token *token = session_info->security_token;
+ NTSTATUS status;
+ DATA_BLOB session_key;
+ bool ok;
+
+ if (!r->in.domain_name) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!r->in.admin_account || !r->in.encrypted_password) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!security_token_has_privilege(token, SEC_PRIV_MACHINE_ACCOUNT) &&
+ !nt_token_check_domain_rid(token, DOMAIN_RID_ADMINS) &&
+ !nt_token_check_sid(&global_sid_Builtin_Administrators, token)) {
+ DEBUG(5,("_wkssvc_NetrJoinDomain2: account doesn't have "
+ "sufficient privileges\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if ((r->in.join_flags & WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED) ||
+ (r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ status = session_extract_session_key(session_info,
+ &session_key,
+ KEY_USE_16BYTES);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("_wkssvc_NetrJoinDomain2: no session key %s\n",
+ nt_errstr(status)));
+ return WERR_NO_USER_SESSION_KEY;
+ }
+
+ werr = decode_wkssvc_join_password_buffer(
+ p->mem_ctx, r->in.encrypted_password,
+ &session_key, &cleartext_pwd);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ok = split_domain_user(p->mem_ctx,
+ r->in.admin_account,
+ &admin_domain,
+ &admin_account);
+ if (!ok) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ werr = libnet_init_JoinCtx(p->mem_ctx, &j);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ j->in.domain_name = r->in.domain_name;
+ j->in.account_ou = r->in.account_ou;
+ j->in.join_flags = r->in.join_flags;
+ j->in.admin_account = admin_account;
+ j->in.admin_password = cleartext_pwd;
+ j->in.debug = true;
+ j->in.modify_config = lp_config_backend_is_registry();
+ j->in.msg_ctx = p->msg_ctx;
+
+ become_root();
+ setenv(KRB5_ENV_CCNAME, "MEMORY:_wkssvc_NetrJoinDomain2", 1);
+ werr = libnet_Join(p->mem_ctx, j);
+ unsetenv(KRB5_ENV_CCNAME);
+ unbecome_root();
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(5,("_wkssvc_NetrJoinDomain2: libnet_Join failed with: %s\n",
+ j->out.error_string ? j->out.error_string :
+ win_errstr(werr)));
+ }
+
+ TALLOC_FREE(j);
+ return werr;
+}
+
+/********************************************************************
+ _wkssvc_NetrUnjoinDomain2
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUnjoinDomain2(struct pipes_struct *p,
+ struct wkssvc_NetrUnjoinDomain2 *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct auth_session_info *session_info =
+ dcesrv_call_session_info(dce_call);
+ struct libnet_UnjoinCtx *u = NULL;
+ char *cleartext_pwd = NULL;
+ char *admin_domain = NULL;
+ char *admin_account = NULL;
+ WERROR werr;
+ struct security_token *token = session_info->security_token;
+ NTSTATUS status;
+ DATA_BLOB session_key;
+ bool ok;
+
+ if (!r->in.account || !r->in.encrypted_password) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!security_token_has_privilege(token, SEC_PRIV_MACHINE_ACCOUNT) &&
+ !nt_token_check_domain_rid(token, DOMAIN_RID_ADMINS) &&
+ !nt_token_check_sid(&global_sid_Builtin_Administrators, token)) {
+ DEBUG(5,("_wkssvc_NetrUnjoinDomain2: account doesn't have "
+ "sufficient privileges\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = session_extract_session_key(session_info,
+ &session_key,
+ KEY_USE_16BYTES);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("_wkssvc_NetrUnjoinDomain2: no session key %s\n",
+ nt_errstr(status)));
+ return WERR_NO_USER_SESSION_KEY;
+ }
+
+ werr = decode_wkssvc_join_password_buffer(
+ p->mem_ctx, r->in.encrypted_password,
+ &session_key, &cleartext_pwd);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ok = split_domain_user(p->mem_ctx,
+ r->in.account,
+ &admin_domain,
+ &admin_account);
+ if (!ok) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ werr = libnet_init_UnjoinCtx(p->mem_ctx, &u);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ u->in.domain_name = lp_realm();
+ u->in.unjoin_flags = r->in.unjoin_flags |
+ WKSSVC_JOIN_FLAGS_JOIN_TYPE;
+ u->in.admin_account = admin_account;
+ u->in.admin_password = cleartext_pwd;
+ u->in.debug = true;
+ u->in.modify_config = lp_config_backend_is_registry();
+ u->in.msg_ctx = p->msg_ctx;
+
+ become_root();
+ setenv(KRB5_ENV_CCNAME, "MEMORY:_wkssvc_NetrUnjoinDomain2", 1);
+ werr = libnet_Unjoin(p->mem_ctx, u);
+ unsetenv(KRB5_ENV_CCNAME);
+ unbecome_root();
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(5,("_wkssvc_NetrUnjoinDomain2: libnet_Unjoin failed with: %s\n",
+ u->out.error_string ? u->out.error_string :
+ win_errstr(werr)));
+ }
+
+ TALLOC_FREE(u);
+ return werr;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrRenameMachineInDomain2(struct pipes_struct *p,
+ struct wkssvc_NetrRenameMachineInDomain2 *r)
+{
+ /* for now just return not supported */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrValidateName2(struct pipes_struct *p,
+ struct wkssvc_NetrValidateName2 *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrGetJoinableOus2(struct pipes_struct *p,
+ struct wkssvc_NetrGetJoinableOus2 *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrAddAlternateComputerName(struct pipes_struct *p,
+ struct wkssvc_NetrAddAlternateComputerName *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrRemoveAlternateComputerName(struct pipes_struct *p,
+ struct wkssvc_NetrRemoveAlternateComputerName *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrSetPrimaryComputername(struct pipes_struct *p,
+ struct wkssvc_NetrSetPrimaryComputername *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrEnumerateComputerNames(struct pipes_struct *p,
+ struct wkssvc_NetrEnumerateComputerNames *r)
+{
+ /* FIXME: Add implementation code here */
+ p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
+ return WERR_NOT_SUPPORTED;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_wkssvc_scompat.c"
diff --git a/source3/rpc_server/wscript_build b/source3/rpc_server/wscript_build
new file mode 100644
index 0000000..904311a
--- /dev/null
+++ b/source3/rpc_server/wscript_build
@@ -0,0 +1,308 @@
+#!/usr/bin/env python
+
+### RPC_SERVER
+bld.SAMBA3_SUBSYSTEM('rpc',
+ source='',
+ deps='dcerpc-server-core')
+
+bld.SAMBA_BINARY('samba-dcerpcd',
+ source='rpc_host.c',
+ deps='''
+ samba3core
+ CMDLINE_S3
+ dcerpc-binding
+ npa_tstream
+ AUTH_COMMON
+ RPC_SOCK_HELPER
+ NDR_RPC_HOST
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA_LIBRARY('RPC_WORKER',
+ private_library=True,
+ source='''
+ rpc_worker.c
+ ''',
+ deps='''
+ smbd_base
+ CMDLINE_S3
+ NDR_RPC_HOST
+ RPC_SERVER
+ RPC_NCACN_NP
+ npa_tstream
+ ''')
+
+bld.SAMBA3_BINARY('rpcd_rpcecho',
+ source='rpcd_rpcecho.c',
+ deps='''
+ RPC_WORKER
+ RPC_RPCECHO
+ ''',
+ for_selftest=True,
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_classic',
+ source='rpcd_classic.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_SERVICE
+ RPC_SOCK_HELPER
+ smbd_base
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_lsad',
+ source='rpcd_lsad.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_LSARPC
+ RPC_SAMR
+ RPC_DSSETUP
+ RPC_NETLOGON
+ RPC_SOCK_HELPER
+ smbd_base
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_winreg',
+ source='rpcd_winreg.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_WINREG
+ smbd_base
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_spoolss',
+ source='rpcd_spoolss.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_SPOOLSS
+ smbd_base
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_epmapper',
+ source='rpcd_epmapper.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_EPMAPPER
+ smbd_base
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_fsrvp',
+ source='rpcd_fsrvp.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_FSS_AGENT
+ smbd_base
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_BINARY('rpcd_witness',
+ source='rpcd_witness.c',
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ RPC_WITNESS
+ ''',
+ install_path='${SAMBA_LIBEXECDIR}',
+ enabled=bld.env.with_ctdb)
+
+bld.SAMBA3_SUBSYSTEM('RPC_CONFIG',
+ source='rpc_config.c',
+ deps='talloc')
+
+bld.SAMBA3_SUBSYSTEM('RPC_NCACN_NP',
+ source='rpc_ncacn_np.c rpc_handles.c',
+ deps='auth common_auth npa_tstream')
+
+bld.SAMBA3_LIBRARY('RPC_SERVER_LOOP',
+ private_library=True,
+ source='rpc_server.c',
+ deps='''
+ LIBTSOCKET
+ dcerpc-server-core
+ npa_tstream
+ auth
+ RPC_NCACN_NP
+ samba3-util
+ ''')
+
+bld.SAMBA3_SUBSYSTEM('SRV_ACCESS_CHECK',
+ source='srv_access_check.c',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_SERVER',
+ source='',
+ deps='''
+ dcerpc-server-core
+ RPC_CONFIG
+ RPC_SERVER_LOOP
+ NDR_NAMED_PIPE_AUTH
+ ''')
+
+### RPC_SERVICES
+bld.SAMBA3_SUBSYSTEM('RPC_DSSETUP',
+ source='''dssetup/srv_dssetup_nt.c''',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_EPMAPPER',
+ source='''epmapper/srv_epmapper.c''',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_FSS_STATE',
+ source='''fss/srv_fss_state.c''',
+ deps='samba-util NDR_FSRVP_STATE')
+
+bld.SAMBA3_SUBSYSTEM('RPC_FSS_AGENT',
+ source='''fss/srv_fss_agent.c''',
+ deps='samba-util RPC_FSS_STATE')
+
+bld.SAMBA3_SUBSYSTEM('RPC_EVENTLOG',
+ source='''eventlog/srv_eventlog_nt.c
+ eventlog/srv_eventlog_reg.c''',
+ deps='LIBEVENTLOG LIBCLI_WINREG_INTERNAL')
+
+bld.SAMBA3_SUBSYSTEM('RPC_INITSHUTDOWN',
+ source='''initshutdown/srv_initshutdown_nt.c''',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_LSARPC',
+ source='''lsa/srv_lsa_nt.c''',
+ deps='SRV_ACCESS_CHECK LIBLSA GNUTLS_HELPERS')
+
+bld.SAMBA3_SUBSYSTEM('RPC_NETDFS',
+ source='''dfs/srv_dfs_nt.c''',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_NETLOGON',
+ source='''netlogon/srv_netlog_nt.c''',
+ deps='LIBCLI_AUTH DCERPC_SERVER_NETLOGON')
+
+bld.SAMBA3_SUBSYSTEM('RPC_NTSVCS',
+ source='''ntsvcs/srv_ntsvcs_nt.c''',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_RPCECHO',
+ source='''echo/srv_echo_nt.c''',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('RPC_SAMR',
+ source='''samr/srv_samr_nt.c
+ samr/srv_samr_util.c
+ samr/srv_samr_chgpasswd.c''',
+ deps='PLAINTEXT_AUTH SRV_ACCESS_CHECK DCERPC_HELPER')
+
+bld.SAMBA3_SUBSYSTEM('RPC_SPOOLSS',
+ source='''spoolss/srv_spoolss_nt.c
+ spoolss/srv_spoolss_util.c''',
+ deps='PRINTING PRINTBACKEND LIBCLI_WINREG_INTERNAL')
+
+bld.SAMBA3_SUBSYSTEM('RPC_IREMOTEWINSPOOL',
+ source='''
+ spoolss/srv_iremotewinspool_nt.c
+ spoolss/srv_iremotewinspool.c
+ spoolss/iremotewinspool_util.c
+ ''',
+ deps='RPC_SPOOLSS')
+
+bld.SAMBA3_SUBSYSTEM('RPC_SRVSVC',
+ source='''srvsvc/srv_srvsvc_nt.c''',
+ deps='samba-util tdb')
+
+bld.SAMBA3_SUBSYSTEM('RPC_SVCCTL',
+ source='''svcctl/srv_svcctl_nt.c
+ svcctl/srv_svcctl_reg.c''',
+ deps='SERVICES LIBCLI_WINREG_INTERNAL')
+
+bld.SAMBA3_SUBSYSTEM('RPC_WINREG',
+ source='''winreg/srv_winreg_nt.c''',
+ deps='REG_FULL REGFIO NDR_PERFCOUNT')
+
+bld.SAMBA3_SUBSYSTEM('RPC_WKSSVC',
+ source='''wkssvc/srv_wkssvc_nt.c''',
+ deps='LIBNET')
+
+bld.SAMBA3_SUBSYSTEM('RPC_WITNESS',
+ source='''witness/srv_witness_nt.c''',
+ deps='samba-util samba-cluster-support samba3core',
+ enabled=bld.env.with_ctdb)
+
+bld.SAMBA3_SUBSYSTEM('mdssvc',
+ source='''
+ mdssvc/dalloc.c
+ mdssvc/marshalling.c
+ ''')
+
+rpc_mdssvc_sources = '''
+ mdssvc/mdssvc.c
+ mdssvc/mdssvc_noindex.c
+ mdssvc/srv_mdssvc_nt.c
+ '''
+rpc_mdssvc_deps = 'mdssvc samba-util smbd_base '
+
+if bld.env.spotlight_backend_tracker:
+ rpc_mdssvc_sources += '''
+ mdssvc/mdssvc_tracker.c
+ mdssvc/sparql_mapping.c
+ mdssvc/sparql_parser.y
+ mdssvc/sparql_lexer.l
+ '''
+ rpc_mdssvc_deps += 'tevent-glib-glue ' + bld.env['libtracker']
+
+if bld.env.spotlight_backend_es:
+ rpc_mdssvc_sources += '''
+ mdssvc/mdssvc_es.c
+ mdssvc/es_mapping.c
+ mdssvc/es_parser.y
+ mdssvc/es_lexer.l
+ '''
+ rpc_mdssvc_deps += ' http jansson'
+
+ bld.INSTALL_FILES(bld.env.SAMBA_DATADIR,
+ 'mdssvc/elasticsearch_mappings.json')
+
+bld.SAMBA3_BINARY('rpcd_mdssvc',
+ source='rpcd_mdssvc.c ' + rpc_mdssvc_sources,
+ deps='''
+ CMDLINE_S3
+ RPC_WORKER
+ smbd_base
+ ''' + rpc_mdssvc_deps,
+ install_path='${SAMBA_LIBEXECDIR}')
+
+bld.SAMBA3_SUBSYSTEM('RPC_SERVICE',
+ source='',
+ deps='''
+ rpc
+ RPC_SERVER
+ RPC_SAMR
+ RPC_LSARPC
+ RPC_WINREG
+ RPC_INITSHUTDOWN
+ RPC_DSSETUP
+ RPC_WKSSVC
+ RPC_SVCCTL
+ RPC_NTSVCS
+ RPC_NETLOGON
+ RPC_NETDFS
+ RPC_SRVSVC
+ RPC_IREMOTEWINSPOOL
+ RPC_EVENTLOG
+ RPC_RPCECHO
+ RPC_EPMAPPER
+ RPC_FSS_AGENT
+ ''')
+
+# RPC_DAEMONS
+bld.SAMBA3_SUBSYSTEM('RPC_SOCK_HELPER',
+ source='rpc_sock_helper.c',
+ deps='')