summaryrefslogtreecommitdiff
path: root/source3/rpc_server
diff options
context:
space:
mode:
Diffstat (limited to 'source3/rpc_server')
-rw-r--r--source3/rpc_server/srv_dfs_nt.c530
-rw-r--r--source3/rpc_server/srv_dssetup_nt.c223
-rw-r--r--source3/rpc_server/srv_echo_nt.c125
-rw-r--r--source3/rpc_server/srv_eventlog.c118
-rw-r--r--source3/rpc_server/srv_eventlog_lib.c742
-rw-r--r--source3/rpc_server/srv_eventlog_nt.c968
-rw-r--r--source3/rpc_server/srv_initshutdown_nt.c77
-rw-r--r--source3/rpc_server/srv_lsa_hnd.c271
-rw-r--r--source3/rpc_server/srv_lsa_nt.c2426
-rw-r--r--source3/rpc_server/srv_netlog_nt.c1414
-rw-r--r--source3/rpc_server/srv_ntsvcs.c163
-rw-r--r--source3/rpc_server/srv_ntsvcs_nt.c778
-rw-r--r--source3/rpc_server/srv_pipe.c2419
-rw-r--r--source3/rpc_server/srv_pipe_hnd.c1249
-rw-r--r--source3/rpc_server/srv_samr_nt.c5956
-rw-r--r--source3/rpc_server/srv_samr_util.c382
-rw-r--r--source3/rpc_server/srv_spoolss.c1639
-rw-r--r--source3/rpc_server/srv_spoolss_nt.c9946
-rw-r--r--source3/rpc_server/srv_srvsvc_nt.c2624
-rw-r--r--source3/rpc_server/srv_svcctl.c273
-rw-r--r--source3/rpc_server/srv_svcctl_nt.c1109
-rw-r--r--source3/rpc_server/srv_util.c82
-rw-r--r--source3/rpc_server/srv_winreg_nt.c965
-rw-r--r--source3/rpc_server/srv_wkssvc_nt.c494
24 files changed, 34973 insertions, 0 deletions
diff --git a/source3/rpc_server/srv_dfs_nt.c b/source3/rpc_server/srv_dfs_nt.c
new file mode 100644
index 0000000000..661d262dc4
--- /dev/null
+++ b/source3/rpc_server/srv_dfs_nt.c
@@ -0,0 +1,530 @@
+/*
+ * 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"
+
+#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(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(pipes_struct *p, struct dfs_Add *r)
+{
+ struct junction_map *jn = NULL;
+ struct referral *old_referral_list = NULL;
+ bool self_ref = False;
+ int consumedcnt = 0;
+ char *altpath = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ if (p->pipe_user.ut.uid != sec_initial_uid()) {
+ DEBUG(10,("_dfs_add: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ jn = TALLOC_ZERO_P(ctx, struct junction_map);
+ if (!jn) {
+ return WERR_NOMEM;
+ }
+
+ 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_NOMEM;
+ }
+
+ /* The following call can change the cwd. */
+ status = get_referred_path(ctx, r->in.path, 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_NOMEM;
+ }
+
+ 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_DFS_INTERNAL_ERROR;
+ }
+
+ 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)) {
+ return WERR_DFS_CANT_CREATE_JUNCT;
+ }
+
+ return WERR_OK;
+}
+
+WERROR _dfs_Remove(pipes_struct *p, struct dfs_Remove *r)
+{
+ struct junction_map *jn = NULL;
+ bool self_ref = False;
+ int consumedcnt = 0;
+ bool found = False;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *altpath = NULL;
+
+ if (p->pipe_user.ut.uid != sec_initial_uid()) {
+ DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ jn = TALLOC_ZERO_P(ctx, struct junction_map);
+ if (!jn) {
+ return WERR_NOMEM;
+ }
+
+ if (r->in.servername && r->in.sharename) {
+ altpath = talloc_asprintf(ctx, "%s\\%s",
+ r->in.servername,
+ r->in.sharename);
+ if (!altpath) {
+ return WERR_NOMEM;
+ }
+ strlower_m(altpath);
+ DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n",
+ r->in.dfs_entry_path, r->in.servername, r->in.sharename));
+ }
+
+ if(!NT_STATUS_IS_OK(get_referred_path(ctx, r->in.dfs_entry_path, jn,
+ &consumedcnt, &self_ref))) {
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+
+ /* if no server-share pair given, remove the msdfs link completely */
+ if(!r->in.servername && !r->in.sharename) {
+ if(!remove_msdfs_link(jn)) {
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+ } else {
+ int i=0;
+ /* compare each referral in the list with the one to remove */
+ DEBUG(10,("altpath: .%s. refcnt: %d\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_NOMEM;
+ }
+ 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_DFS_NO_SUCH_SHARE;
+ }
+
+ /* Only one referral, remove it */
+ if(jn->referral_count == 1) {
+ if(!remove_msdfs_link(jn)) {
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+ } else {
+ if(!create_msdfs_link(jn)) {
+ return WERR_DFS_CANT_CREATE_JUNCT;
+ }
+ }
+ }
+
+ 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", global_myname(),
+ 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", global_myname(), 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)
+{
+ int ii;
+ if (j->volume_name[0] == '\0')
+ dfs3->path = talloc_asprintf(mem_ctx, "\\\\%s\\%s",
+ global_myname(), j->service_name);
+ else
+ dfs3->path = talloc_asprintf(mem_ctx, "\\\\%s\\%s\\%s", global_myname(),
+ 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';
+ DEBUG(5,("storage %d: %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(pipes_struct *p, struct dfs_Enum *r)
+{
+ struct junction_map *jn = NULL;
+ size_t num_jn = 0;
+ size_t i;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ jn = enum_msdfs_links(ctx, &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_NOMEM;
+ }
+ } 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_NOMEM;
+ }
+ } 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_NOMEM;
+ }
+ } else {
+ r->out.info->e.info3->s = NULL;
+ }
+ r->out.info->e.info3->count = num_jn;
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+
+ 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_PARAM;
+ }
+ }
+
+ return WERR_OK;
+}
+
+WERROR _dfs_GetInfo(pipes_struct *p, struct dfs_GetInfo *r)
+{
+ int consumedcnt = strlen(r->in.dfs_entry_path);
+ struct junction_map *jn = NULL;
+ bool self_ref = False;
+ TALLOC_CTX *ctx = talloc_tos();
+ bool ret;
+
+ jn = TALLOC_ZERO_P(ctx, struct junction_map);
+ if (!jn) {
+ return WERR_NOMEM;
+ }
+
+ if(!create_junction(ctx, r->in.dfs_entry_path, jn)) {
+ return WERR_DFS_NO_SUCH_SERVER;
+ }
+
+ /* The following call can change the cwd. */
+ if(!NT_STATUS_IS_OK(get_referred_path(ctx, r->in.dfs_entry_path,
+ jn, &consumedcnt, &self_ref)) ||
+ consumedcnt < strlen(r->in.dfs_entry_path)) {
+ return WERR_DFS_NO_SUCH_VOL;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ r->out.info->info1 = TALLOC_ZERO_P(ctx,struct dfs_Info1);
+ if (!r->out.info->info1) {
+ return WERR_NOMEM;
+ }
+ ret = init_reply_dfs_info_1(ctx, jn, r->out.info->info1);
+ break;
+ case 2:
+ r->out.info->info2 = TALLOC_ZERO_P(ctx,struct dfs_Info2);
+ if (!r->out.info->info2) {
+ return WERR_NOMEM;
+ }
+ ret = init_reply_dfs_info_2(ctx, jn, r->out.info->info2);
+ break;
+ case 3:
+ r->out.info->info3 = TALLOC_ZERO_P(ctx,struct dfs_Info3);
+ if (!r->out.info->info3) {
+ return WERR_NOMEM;
+ }
+ ret = init_reply_dfs_info_3(ctx, jn, r->out.info->info3);
+ break;
+ case 100:
+ r->out.info->info100 = TALLOC_ZERO_P(ctx,struct dfs_Info100);
+ if (!r->out.info->info100) {
+ return WERR_NOMEM;
+ }
+ ret = init_reply_dfs_info_100(ctx, jn, r->out.info->info100);
+ break;
+ default:
+ r->out.info->info1 = NULL;
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!ret)
+ return WERR_INVALID_PARAM;
+
+ return WERR_OK;
+}
+
+WERROR _dfs_SetInfo(pipes_struct *p, struct dfs_SetInfo *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Rename(pipes_struct *p, struct dfs_Rename *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Move(pipes_struct *p, struct dfs_Move *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_ManagerGetConfigInfo(pipes_struct *p, struct dfs_ManagerGetConfigInfo *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_ManagerSendSiteInfo(pipes_struct *p, struct dfs_ManagerSendSiteInfo *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_AddFtRoot(pipes_struct *p, struct dfs_AddFtRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_RemoveFtRoot(pipes_struct *p, struct dfs_RemoveFtRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_AddStdRoot(pipes_struct *p, struct dfs_AddStdRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_RemoveStdRoot(pipes_struct *p, struct dfs_RemoveStdRoot *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_ManagerInitialize(pipes_struct *p, struct dfs_ManagerInitialize *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_AddStdRootForced(pipes_struct *p, struct dfs_AddStdRootForced *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_GetDcAddress(pipes_struct *p, struct dfs_GetDcAddress *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_SetDcAddress(pipes_struct *p, struct dfs_SetDcAddress *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_FlushFtTable(pipes_struct *p, struct dfs_FlushFtTable *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Add2(pipes_struct *p, struct dfs_Add2 *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_Remove2(pipes_struct *p, struct dfs_Remove2 *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_EnumEx(pipes_struct *p, struct dfs_EnumEx *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _dfs_SetInfo2(pipes_struct *p, struct dfs_SetInfo2 *r)
+{
+ /* FIXME: Implement your code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
diff --git a/source3/rpc_server/srv_dssetup_nt.c b/source3/rpc_server/srv_dssetup_nt.c
new file mode 100644
index 0000000000..2b18e6b2ae
--- /dev/null
+++ b/source3/rpc_server/srv_dssetup_nt.c
@@ -0,0 +1,223 @@
+/*
+ * 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"
+
+#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_P(ctx, struct dssetup_DsRolePrimaryDomInfoBasic);
+ if (!basic) {
+ DEBUG(0,("fill_dsrole_dominfo_basic: out of memory\n"));
+ return WERR_NOMEM;
+ }
+
+ 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:
+ 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_NOMEM;
+ }
+ strlower_m(dnsdomain);
+ 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(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_UNKNOWN_LEVEL;
+ }
+
+ return werr;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDnsNameToFlatName(pipes_struct *p,
+ struct dssetup_DsRoleDnsNameToFlatName *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDcAsDc(pipes_struct *p,
+ struct dssetup_DsRoleDcAsDc *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDcAsReplica(pipes_struct *p,
+ struct dssetup_DsRoleDcAsReplica *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleDemoteDc(pipes_struct *p,
+ struct dssetup_DsRoleDemoteDc *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleGetDcOperationProgress(pipes_struct *p,
+ struct dssetup_DsRoleGetDcOperationProgress *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleGetDcOperationResults(pipes_struct *p,
+ struct dssetup_DsRoleGetDcOperationResults *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleCancel(pipes_struct *p,
+ struct dssetup_DsRoleCancel *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleServerSaveStateForUpgrade(pipes_struct *p,
+ struct dssetup_DsRoleServerSaveStateForUpgrade *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleUpgradeDownlevelServer(pipes_struct *p,
+ struct dssetup_DsRoleUpgradeDownlevelServer *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _dssetup_DsRoleAbortDownlevelServerUpgrade(pipes_struct *p,
+ struct dssetup_DsRoleAbortDownlevelServerUpgrade *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
diff --git a/source3/rpc_server/srv_echo_nt.c b/source3/rpc_server/srv_echo_nt.c
new file mode 100644
index 0000000000..1179a162b0
--- /dev/null
+++ b/source3/rpc_server/srv_echo_nt.c
@@ -0,0 +1,125 @@
+/*
+ * 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 "nterr.h"
+
+#ifdef DEVELOPER
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* Add one to the input and return it */
+
+void _echo_AddOne(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(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, r->in.len);
+ memcpy( r->out.out_data, r->in.in_data, r->in.len );
+ return;
+}
+
+/* Sink an array of data */
+
+void _echo_SinkData(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(pipes_struct *p, struct echo_SourceData *r)
+{
+ uint32 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, r->in.len );
+
+ for (i = 0; i < r->in.len; i++ ) {
+ r->out.data[i] = i & 0xff;
+ }
+
+ return;
+}
+
+void _echo_TestCall(pipes_struct *p, struct echo_TestCall *r)
+{
+ p->rng_fault_state = True;
+ return;
+}
+
+NTSTATUS _echo_TestCall2(pipes_struct *p, struct echo_TestCall2 *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_OK;
+}
+
+uint32 _echo_TestSleep(pipes_struct *p, struct echo_TestSleep *r)
+{
+ p->rng_fault_state = True;
+ return 0;
+}
+
+void _echo_TestEnum(pipes_struct *p, struct echo_TestEnum *r)
+{
+ p->rng_fault_state = True;
+ return;
+}
+
+void _echo_TestSurrounding(pipes_struct *p, struct echo_TestSurrounding *r)
+{
+ p->rng_fault_state = True;
+ return;
+}
+
+uint16 _echo_TestDoublePointer(pipes_struct *p, struct echo_TestDoublePointer *r)
+{
+ p->rng_fault_state = True;
+ return 0;
+}
+
+#endif /* DEVELOPER */
diff --git a/source3/rpc_server/srv_eventlog.c b/source3/rpc_server/srv_eventlog.c
new file mode 100644
index 0000000000..da761c905e
--- /dev/null
+++ b/source3/rpc_server/srv_eventlog.c
@@ -0,0 +1,118 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Marcin Krzysztof Porwit 2005.
+ * Copyright (C) Gerald Carter 2005 - 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static bool proxy_eventlog_call(pipes_struct *p, uint8 opnum)
+{
+ struct api_struct *fns;
+ int n_fns;
+
+ eventlog_get_pipe_fns(&fns, &n_fns);
+
+ if (opnum >= n_fns)
+ return False;
+
+ if (fns[opnum].opnum != opnum) {
+ smb_panic("EVENTLOG function table not sorted\n");
+ }
+
+ return fns[opnum].fn(p);
+}
+
+static bool api_eventlog_open_eventlog(pipes_struct *p)
+{
+ return proxy_eventlog_call(p, NDR_EVENTLOG_OPENEVENTLOGW);
+}
+
+static bool api_eventlog_close_eventlog(pipes_struct *p)
+{
+ return proxy_eventlog_call( p, NDR_EVENTLOG_CLOSEEVENTLOG );
+}
+
+static bool api_eventlog_get_num_records(pipes_struct *p)
+{
+ return proxy_eventlog_call(p, NDR_EVENTLOG_GETNUMRECORDS);
+}
+
+static bool api_eventlog_get_oldest_entry(pipes_struct *p)
+{
+ return proxy_eventlog_call(p, NDR_EVENTLOG_GETOLDESTRECORD);
+}
+
+static bool api_eventlog_read_eventlog(pipes_struct *p)
+{
+ EVENTLOG_Q_READ_EVENTLOG q_u;
+ EVENTLOG_R_READ_EVENTLOG r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!(eventlog_io_q_read_eventlog("", &q_u, data, 0))) {
+ DEBUG(0, ("eventlog_io_q_read_eventlog: unable to unmarshall EVENTLOG_Q_READ_EVENTLOG.\n"));
+ return False;
+ }
+
+ r_u.status = _eventlog_read_eventlog(p, &q_u, &r_u);
+
+ if (!(eventlog_io_r_read_eventlog("", &q_u, &r_u, rdata, 0))) {
+ DEBUG(0, ("eventlog_io_r_read_eventlog: unable to marshall EVENTLOG_R_READ_EVENTLOG.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+static bool api_eventlog_clear_eventlog(pipes_struct *p)
+{
+ return proxy_eventlog_call(p, NDR_EVENTLOG_CLEAREVENTLOGW);
+}
+
+/*
+ \pipe\eventlog commands
+*/
+struct api_struct api_eventlog_cmds[] =
+{
+ {"EVENTLOG_OPENEVENTLOG", EVENTLOG_OPENEVENTLOG, api_eventlog_open_eventlog },
+ {"EVENTLOG_CLOSEEVENTLOG", EVENTLOG_CLOSEEVENTLOG, api_eventlog_close_eventlog },
+ {"EVENTLOG_GETNUMRECORDS", EVENTLOG_GETNUMRECORDS, api_eventlog_get_num_records },
+ {"EVENTLOG_GETOLDESTENTRY", EVENTLOG_GETOLDESTENTRY, api_eventlog_get_oldest_entry },
+ {"EVENTLOG_READEVENTLOG", EVENTLOG_READEVENTLOG, api_eventlog_read_eventlog },
+ {"EVENTLOG_CLEAREVENTLOG", EVENTLOG_CLEAREVENTLOG, api_eventlog_clear_eventlog }
+};
+
+NTSTATUS rpc_eventlog2_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ "eventlog", "eventlog", &ndr_table_eventlog.syntax_id,
+ api_eventlog_cmds,
+ sizeof(api_eventlog_cmds)/sizeof(struct api_struct));
+}
+
+void eventlog2_get_pipe_fns(struct api_struct **fns, int *n_fns)
+{
+ *fns = api_eventlog_cmds;
+ *n_fns = sizeof(api_eventlog_cmds) / sizeof(struct api_struct);
+}
diff --git a/source3/rpc_server/srv_eventlog_lib.c b/source3/rpc_server/srv_eventlog_lib.c
new file mode 100644
index 0000000000..e232a30078
--- /dev/null
+++ b/source3/rpc_server/srv_eventlog_lib.c
@@ -0,0 +1,742 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Eventlog utility routines
+ * Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Brian Moran 2005.
+ * Copyright (C) Gerald (Jerry) Carter 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"
+
+/* maintain a list of open eventlog tdbs with reference counts */
+
+static ELOG_TDB *open_elog_list;
+
+/********************************************************************
+ Init an Eventlog TDB, and return it. If null, something bad
+ happened.
+********************************************************************/
+
+TDB_CONTEXT *elog_init_tdb( char *tdbfilename )
+{
+ TDB_CONTEXT *tdb;
+
+ DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n",
+ tdbfilename));
+
+ tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT|O_TRUNC, 0660 );
+
+ if ( !tdb ) {
+ DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) );
+ return NULL;
+ }
+
+ /* initialize with defaults, copy real values in here from registry */
+
+ tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 );
+ tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 );
+ tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 );
+ tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 );
+
+ tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 );
+
+ return tdb;
+}
+
+/********************************************************************
+ make the tdb file name for an event log, given destination buffer
+ and size. Caller must free memory.
+********************************************************************/
+
+char *elog_tdbname(TALLOC_CTX *ctx, const char *name )
+{
+ char *path = talloc_asprintf(ctx, "%s/%s.tdb",
+ state_path("eventlog"),
+ name);
+ if (!path) {
+ return NULL;
+ }
+ strlower_m(path);
+ return path;
+}
+
+
+/********************************************************************
+ this function is used to count up the number of bytes in a
+ particular TDB
+********************************************************************/
+
+struct trav_size_struct {
+ int size;
+ int rec_count;
+};
+
+static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
+ void *state )
+{
+ struct trav_size_struct *tsize = (struct trav_size_struct *)state;
+
+ tsize->size += data.dsize;
+ tsize->rec_count++;
+
+ return 0;
+}
+
+/********************************************************************
+ returns the size of the eventlog, and if MaxSize is a non-null
+ ptr, puts the MaxSize there. This is purely a way not to have yet
+ another function that solely reads the maxsize of the eventlog.
+ Yeah, that's it.
+********************************************************************/
+
+int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
+{
+ struct trav_size_struct tsize;
+
+ if ( !tdb )
+ return 0;
+
+ ZERO_STRUCT( tsize );
+
+ tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize );
+
+ if ( MaxSize != NULL ) {
+ *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE );
+ }
+
+ if ( Retention != NULL ) {
+ *Retention = tdb_fetch_int32( tdb, EVT_RETENTION );
+ }
+
+ DEBUG( 1,
+ ( "eventlog size: [%d] for [%d] records\n", tsize.size,
+ tsize.rec_count ) );
+ return tsize.size;
+}
+
+/********************************************************************
+ Discard early event logs until we have enough for 'needed' bytes...
+ NO checking done beforehand to see that we actually need to do
+ this, and it's going to pluck records one-by-one. So, it's best
+ to determine that this needs to be done before doing it.
+
+ Setting whack_by_date to True indicates that eventlogs falling
+ outside of the retention range need to go...
+
+ return True if we made enough room to accommodate needed bytes
+********************************************************************/
+
+static bool make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
+ bool whack_by_date )
+{
+ int start_record, i, new_start;
+ int end_record;
+ int nbytes, reclen, len, Retention, MaxSize;
+ int tresv1, trecnum, timegen, timewr;
+ TDB_DATA key, ret;
+ time_t current_time, exp_time;
+
+ /* discard some eventlogs */
+
+ /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
+ although records not necessarily guaranteed to have successive times */
+ /* */
+
+ /* lock */
+ tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
+ /* read */
+ end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
+ start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
+ Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
+ MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );
+
+ time( &current_time );
+
+ /* calculate ... */
+ exp_time = current_time - Retention; /* discard older than exp_time */
+
+ /* todo - check for sanity in next_record */
+ nbytes = 0;
+
+ DEBUG( 3,
+ ( "MaxSize [%d] Retention [%d] Current Time [%d] exp_time [%d]\n",
+ MaxSize, Retention, (uint32)current_time, (uint32)exp_time ) );
+ DEBUG( 3,
+ ( "Start Record [%d] End Record [%d]\n", start_record,
+ end_record ) );
+
+ for ( i = start_record; i < end_record; i++ ) {
+ /* read a record, add the amt to nbytes */
+ key.dsize = sizeof( int32 );
+ key.dptr = ( uint8 * ) ( int32 * ) & i;
+ ret = tdb_fetch( the_tdb, key );
+ if ( ret.dsize == 0 ) {
+ DEBUG( 8,
+ ( "Can't find a record for the key, record [%d]\n",
+ i ) );
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
+ return False;
+ }
+ nbytes += ret.dsize; /* note this includes overhead */
+
+ len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
+ &tresv1, &trecnum, &timegen, &timewr );
+ if (len == -1) {
+ DEBUG( 10,("make_way_for_eventlogs: tdb_unpack failed.\n"));
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
+ return False;
+ }
+
+ DEBUG( 8,
+ ( "read record %d, record size is [%d], total so far [%d]\n",
+ i, reclen, nbytes ) );
+
+ SAFE_FREE( ret.dptr );
+
+ /* note that other servers may just stop writing records when the size limit
+ is reached, and there are no records older than 'retention'. This doesn't
+ like a very useful thing to do, so instead we whack (as in sleeps with the
+ fishes) just enough records to fit the what we need. This behavior could
+ be changed to 'match', if the need arises. */
+
+ if ( !whack_by_date && ( nbytes >= needed ) )
+ break; /* done */
+ if ( whack_by_date && ( timegen >= exp_time ) )
+ break; /* done */
+ }
+
+ DEBUG( 3,
+ ( "nbytes [%d] needed [%d] start_record is [%d], should be set to [%d]\n",
+ nbytes, needed, start_record, i ) );
+ /* todo - remove eventlog entries here and set starting record to start_record... */
+ new_start = i;
+ if ( start_record != new_start ) {
+ for ( i = start_record; i < new_start; i++ ) {
+ key.dsize = sizeof( int32 );
+ key.dptr = ( uint8 * ) ( int32 * ) & i;
+ tdb_delete( the_tdb, key );
+ }
+
+ tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
+ }
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
+ return True;
+}
+
+/********************************************************************
+ some hygiene for an eventlog - see how big it is, and then
+ calculate how many bytes we need to remove
+********************************************************************/
+
+bool prune_eventlog( TDB_CONTEXT * tdb )
+{
+ int MaxSize, Retention, CalcdSize;
+
+ if ( !tdb ) {
+ DEBUG( 4, ( "No eventlog tdb handle\n" ) );
+ return False;
+ }
+
+ CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention );
+ DEBUG( 3,
+ ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
+ MaxSize ) );
+
+ if ( CalcdSize > MaxSize ) {
+ return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
+ False );
+ }
+
+ return make_way_for_eventlogs( tdb, 0, True );
+}
+
+/********************************************************************
+********************************************************************/
+
+bool can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
+{
+ int calcd_size;
+ int MaxSize, Retention;
+
+ /* see if we can write to the eventlog -- do a policy enforcement */
+ if ( !tdb )
+ return False; /* tdb is null, so we can't write to it */
+
+
+ if ( needed < 0 )
+ return False;
+ MaxSize = 0;
+ Retention = 0;
+
+ calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention );
+
+ if ( calcd_size <= MaxSize )
+ return True; /* you betcha */
+ if ( calcd_size + needed < MaxSize )
+ return True;
+
+ if ( Retention == 0xffffffff ) {
+ return False; /* see msdn - we can't write no room, discard */
+ }
+ /*
+ note don't have to test, but always good to show intent, in case changes needed
+ later
+ */
+
+ if ( Retention == 0x00000000 ) {
+ /* discard record(s) */
+ /* todo - decide when to remove a bunch vs. just what we need... */
+ return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
+ True );
+ }
+
+ return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
+}
+
+/*******************************************************************
+*******************************************************************/
+
+ELOG_TDB *elog_open_tdb( char *logname, bool force_clear )
+{
+ TDB_CONTEXT *tdb = NULL;
+ uint32 vers_id;
+ ELOG_TDB *ptr;
+ char *tdbpath = NULL;
+ ELOG_TDB *tdb_node = NULL;
+ char *eventlogdir;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ /* first see if we have an open context */
+
+ for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
+ if ( strequal( ptr->name, logname ) ) {
+ ptr->ref_count++;
+
+ /* trick to alow clearing of the eventlog tdb.
+ The force_clear flag should imply that someone
+ has done a force close. So make sure the tdb
+ is NULL. If this is a normal open, then just
+ return the existing reference */
+
+ if ( force_clear ) {
+ SMB_ASSERT( ptr->tdb == NULL );
+ break;
+ }
+ else
+ return ptr;
+ }
+ }
+
+ /* make sure that the eventlog dir exists */
+
+ eventlogdir = state_path( "eventlog" );
+ if ( !directory_exist( eventlogdir, NULL ) )
+ mkdir( eventlogdir, 0755 );
+
+ /* get the path on disk */
+
+ tdbpath = elog_tdbname(ctx, logname);
+ if (!tdbpath) {
+ return NULL;
+ }
+
+ DEBUG(7,("elog_open_tdb: Opening %s...(force_clear == %s)\n",
+ tdbpath, force_clear?"True":"False" ));
+
+ /* the tdb wasn't already open or this is a forced clear open */
+
+ if ( !force_clear ) {
+
+ tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, O_RDWR , 0 );
+ if ( tdb ) {
+ vers_id = tdb_fetch_int32( tdb, EVT_VERSION );
+
+ if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) {
+ DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n",
+ vers_id, tdbpath));
+ tdb_close( tdb );
+ tdb = elog_init_tdb( tdbpath );
+ }
+ }
+ }
+
+ if ( !tdb )
+ tdb = elog_init_tdb( tdbpath );
+
+ /* if we got a valid context, then add it to the list */
+
+ if ( tdb ) {
+ /* on a forced clear, just reset the tdb context if we already
+ have an open entry in the list */
+
+ if ( ptr ) {
+ ptr->tdb = tdb;
+ return ptr;
+ }
+
+ if ( !(tdb_node = TALLOC_ZERO_P( NULL, ELOG_TDB)) ) {
+ DEBUG(0,("elog_open_tdb: talloc() failure!\n"));
+ tdb_close( tdb );
+ return NULL;
+ }
+
+ tdb_node->name = talloc_strdup( tdb_node, logname );
+ tdb_node->tdb = tdb;
+ tdb_node->ref_count = 1;
+
+ DLIST_ADD( open_elog_list, tdb_node );
+ }
+
+ return tdb_node;
+}
+
+/*******************************************************************
+ Wrapper to handle reference counts to the tdb
+*******************************************************************/
+
+int elog_close_tdb( ELOG_TDB *etdb, bool force_close )
+{
+ TDB_CONTEXT *tdb;
+
+ if ( !etdb )
+ return 0;
+
+ etdb->ref_count--;
+
+ SMB_ASSERT( etdb->ref_count >= 0 );
+
+ if ( etdb->ref_count == 0 ) {
+ tdb = etdb->tdb;
+ DLIST_REMOVE( open_elog_list, etdb );
+ TALLOC_FREE( etdb );
+ return tdb_close( tdb );
+ }
+
+ if ( force_close ) {
+ tdb = etdb->tdb;
+ etdb->tdb = NULL;
+ return tdb_close( tdb );
+ }
+
+ return 0;
+}
+
+
+/*******************************************************************
+ write an eventlog entry. Note that we have to lock, read next
+ eventlog, increment, write, write the record, unlock
+
+ coming into this, ee has the eventlog record, and the auxilliary date
+ (computer name, etc.) filled into the other structure. Before packing
+ into a record, this routine will calc the appropriate padding, etc.,
+ and then blast out the record in a form that can be read back in
+*******************************************************************/
+
+#define MARGIN 512
+
+int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
+{
+ int32 next_record;
+ uint8 *packed_ee;
+ TALLOC_CTX *mem_ctx = NULL;
+ TDB_DATA kbuf, ebuf;
+ uint32 n_packed;
+
+ if ( !ee )
+ return 0;
+
+ mem_ctx = talloc_init( "write_eventlog_tdb" );
+
+ if ( mem_ctx == NULL )
+ return 0;
+
+ /* discard any entries that have bogus time, which usually indicates a bogus entry as well. */
+ if ( ee->record.time_generated == 0 )
+ return 0;
+
+ /* todo - check for sanity in next_record */
+
+ fixup_eventlog_entry( ee );
+
+ if ( !can_write_to_eventlog( the_tdb, ee->record.length ) ) {
+ DEBUG( 3, ( "Can't write to Eventlog, no room \n" ) );
+ talloc_destroy( mem_ctx );
+ return 0;
+ }
+
+ /* alloc mem for the packed version */
+ packed_ee = (uint8 *)TALLOC( mem_ctx, ee->record.length + MARGIN );
+ if ( !packed_ee ) {
+ talloc_destroy( mem_ctx );
+ return 0;
+ }
+
+ /* need to read the record number and insert it into the entry here */
+
+ /* lock */
+ tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
+ /* read */
+ next_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
+
+ n_packed =
+ tdb_pack( (uint8 *)packed_ee, ee->record.length + MARGIN,
+ "ddddddwwwwddddddBBdBBBd", ee->record.length,
+ ee->record.reserved1, next_record,
+ ee->record.time_generated, ee->record.time_written,
+ ee->record.event_id, ee->record.event_type,
+ ee->record.num_strings, ee->record.event_category,
+ ee->record.reserved2,
+ ee->record.closing_record_number,
+ ee->record.string_offset,
+ ee->record.user_sid_length,
+ ee->record.user_sid_offset, ee->record.data_length,
+ ee->record.data_offset,
+ ee->data_record.source_name_len,
+ ee->data_record.source_name,
+ ee->data_record.computer_name_len,
+ ee->data_record.computer_name,
+ ee->data_record.sid_padding,
+ ee->record.user_sid_length, ee->data_record.sid,
+ ee->data_record.strings_len,
+ ee->data_record.strings,
+ ee->data_record.user_data_len,
+ ee->data_record.user_data,
+ ee->data_record.data_padding );
+
+ /*DEBUG(3,("write_eventlog_tdb: packed into %d\n",n_packed)); */
+
+ /* increment the record count */
+
+ kbuf.dsize = sizeof( int32 );
+ kbuf.dptr = (uint8 * ) & next_record;
+
+ ebuf.dsize = n_packed;
+ ebuf.dptr = (uint8 *)packed_ee;
+
+ if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
+ /* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
+ talloc_destroy( mem_ctx );
+ return 0;
+ }
+ next_record++;
+ tdb_store_int32( the_tdb, EVT_NEXT_RECORD, next_record );
+ tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
+ talloc_destroy( mem_ctx );
+ return ( next_record - 1 );
+}
+
+/*******************************************************************
+ calculate the correct fields etc for an eventlog entry
+*******************************************************************/
+
+void fixup_eventlog_entry( Eventlog_entry * ee )
+{
+ /* fix up the eventlog entry structure as necessary */
+
+ ee->data_record.sid_padding =
+ ( ( 4 -
+ ( ( ee->data_record.source_name_len +
+ ee->data_record.computer_name_len ) % 4 ) ) % 4 );
+ ee->data_record.data_padding =
+ ( 4 -
+ ( ( ee->data_record.strings_len +
+ ee->data_record.user_data_len ) % 4 ) ) % 4;
+ ee->record.length = sizeof( Eventlog_record );
+ ee->record.length += ee->data_record.source_name_len;
+ ee->record.length += ee->data_record.computer_name_len;
+ if ( ee->record.user_sid_length == 0 ) {
+ /* Should not pad to a DWORD boundary for writing out the sid if there is
+ no SID, so just propagate the padding to pad the data */
+ ee->data_record.data_padding += ee->data_record.sid_padding;
+ ee->data_record.sid_padding = 0;
+ }
+ /* DEBUG(10, ("sid_padding is [%d].\n", ee->data_record.sid_padding)); */
+ /* DEBUG(10, ("data_padding is [%d].\n", ee->data_record.data_padding)); */
+
+ ee->record.length += ee->data_record.sid_padding;
+ ee->record.length += ee->record.user_sid_length;
+ ee->record.length += ee->data_record.strings_len;
+ ee->record.length += ee->data_record.user_data_len;
+ ee->record.length += ee->data_record.data_padding;
+ /* need another copy of length at the end of the data */
+ ee->record.length += sizeof( ee->record.length );
+}
+
+/********************************************************************
+ Note that it's a pretty good idea to initialize the Eventlog_entry
+ structure to zero's before calling parse_logentry on an batch of
+ lines that may resolve to a record. ALSO, it's a good idea to
+ remove any linefeeds (that's EOL to you and me) on the lines
+ going in.
+********************************************************************/
+
+bool parse_logentry( char *line, Eventlog_entry * entry, bool * eor )
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *start = NULL, *stop = NULL;
+
+ start = line;
+
+ /* empty line signyfiying record delimeter, or we're at the end of the buffer */
+ if ( start == NULL || strlen( start ) == 0 ) {
+ DEBUG( 6,
+ ( "parse_logentry: found end-of-record indicator.\n" ) );
+ *eor = True;
+ return True;
+ }
+ if ( !( stop = strchr( line, ':' ) ) ) {
+ return False;
+ }
+
+ DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
+
+ if ( 0 == strncmp( start, "LEN", stop - start ) ) {
+ /* This will get recomputed later anyway -- probably not necessary */
+ entry->record.length = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
+ /* For now all these reserved entries seem to have the same value,
+ which can be hardcoded to int(1699505740) for now */
+ entry->record.reserved1 = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
+ entry->record.record_number = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
+ entry->record.time_generated = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
+ entry->record.time_written = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
+ entry->record.event_id = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
+ if ( strstr( start, "ERROR" ) ) {
+ entry->record.event_type = EVENTLOG_ERROR_TYPE;
+ } else if ( strstr( start, "WARNING" ) ) {
+ entry->record.event_type = EVENTLOG_WARNING_TYPE;
+ } else if ( strstr( start, "INFO" ) ) {
+ entry->record.event_type = EVENTLOG_INFORMATION_TYPE;
+ } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
+ entry->record.event_type = EVENTLOG_AUDIT_SUCCESS;
+ } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
+ entry->record.event_type = EVENTLOG_AUDIT_FAILURE;
+ } else if ( strstr( start, "SUCCESS" ) ) {
+ entry->record.event_type = EVENTLOG_SUCCESS;
+ } else {
+ /* some other eventlog type -- currently not defined in MSDN docs, so error out */
+ return False;
+ }
+ }
+
+/*
+ else if(0 == strncmp(start, "NST", stop - start))
+ {
+ entry->record.num_strings = atoi(stop + 1);
+ }
+*/
+ else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
+ entry->record.event_category = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
+ entry->record.reserved2 = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
+ entry->record.closing_record_number = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
+ entry->record.user_sid_length = atoi( stop + 1 );
+ } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
+ stop++;
+ while ( isspace( stop[0] ) ) {
+ stop++;
+ }
+ entry->data_record.source_name_len = rpcstr_push_talloc(ctx,
+ &entry->data_record.source_name,
+ stop);
+ if (entry->data_record.source_name_len == (uint32_t)-1 ||
+ entry->data_record.source_name == NULL) {
+ return false;
+ }
+ } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
+ stop++;
+ while ( isspace( stop[0] ) ) {
+ stop++;
+ }
+ entry->data_record.computer_name_len = rpcstr_push_talloc(ctx,
+ &entry->data_record.computer_name,
+ stop);
+ if (entry->data_record.computer_name_len == (uint32_t)-1 ||
+ entry->data_record.computer_name == NULL) {
+ return false;
+ }
+ } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
+ stop++;
+ while ( isspace( stop[0] ) ) {
+ stop++;
+ }
+ entry->record.user_sid_length = rpcstr_push_talloc(ctx,
+ &entry->data_record.sid,
+ stop);
+ if (entry->record.user_sid_length == (uint32_t)-1 ||
+ entry->data_record.sid == NULL) {
+ return false;
+ }
+ } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
+ smb_ucs2_t *temp = NULL;
+ size_t tmp_len;
+ uint32_t old_len;
+ /* skip past initial ":" */
+ stop++;
+ /* now skip any other leading whitespace */
+ while ( isspace(stop[0])) {
+ stop++;
+ }
+ tmp_len = rpcstr_push_talloc(ctx,
+ &temp,
+ stop);
+ if (tmp_len == (size_t)-1 || !temp) {
+ return false;
+ }
+ old_len = entry->data_record.strings_len;
+ entry->data_record.strings = (smb_ucs2_t *)TALLOC_REALLOC_ARRAY(ctx,
+ entry->data_record.strings,
+ char,
+ old_len + tmp_len);
+ if (!entry->data_record.strings) {
+ return false;
+ }
+ memcpy(entry->data_record.strings + old_len,
+ temp,
+ tmp_len);
+ entry->data_record.strings_len += tmp_len;
+ entry->record.num_strings++;
+ } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
+ /* skip past initial ":" */
+ stop++;
+ /* now skip any other leading whitespace */
+ while ( isspace( stop[0] ) ) {
+ stop++;
+ }
+ entry->data_record.user_data_len = strlen(stop);
+ entry->data_record.user_data = talloc_strdup(ctx,
+ stop);
+ if (!entry->data_record.user_data) {
+ return false;
+ }
+ } else {
+ /* some other eventlog entry -- not implemented, so dropping on the floor */
+ DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
+ /* For now return true so that we can keep on parsing this mess. Eventually
+ we will return False here. */
+ return true;
+ }
+ return true;
+}
diff --git a/source3/rpc_server/srv_eventlog_nt.c b/source3/rpc_server/srv_eventlog_nt.c
new file mode 100644
index 0000000000..0e2bcf4126
--- /dev/null
+++ b/source3/rpc_server/srv_eventlog_nt.c
@@ -0,0 +1,968 @@
+/*
+ * 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.
+ *
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+typedef struct {
+ char *logname;
+ ELOG_TDB *etdb;
+ uint32 current_record;
+ uint32 num_records;
+ uint32 oldest_entry;
+ uint32 flags;
+ uint32 access_granted;
+} EVENTLOG_INFO;
+
+/********************************************************************
+ ********************************************************************/
+
+static void free_eventlog_info( void *ptr )
+{
+ EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
+
+ if ( elog->etdb )
+ elog_close_tdb( elog->etdb, False );
+
+ TALLOC_FREE( elog );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
+ POLICY_HND * handle )
+{
+ EVENTLOG_INFO *info;
+
+ if ( !find_policy_by_hnd( p, handle, (void **)(void *)&info ) ) {
+ DEBUG( 2,
+ ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
+ return NULL;
+ }
+
+ return info;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
+{
+ char *tdbname = elog_tdbname(talloc_tos(), info->logname );
+ SEC_DESC *sec_desc;
+ bool ret;
+ NTSTATUS ntstatus;
+
+ if ( !tdbname )
+ return False;
+
+ /* get the security descriptor for the file */
+
+ sec_desc = get_nt_acl_no_snum( info, tdbname );
+ SAFE_FREE( tdbname );
+
+ if ( !sec_desc ) {
+ DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n",
+ tdbname));
+ return False;
+ }
+
+ /* root free pass */
+
+ if ( geteuid() == sec_initial_uid() ) {
+ DEBUG(5,("elog_check_access: using root's token\n"));
+ token = get_root_nt_token();
+ }
+
+ /* run the check, try for the max allowed */
+
+ ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
+ &info->access_granted, &ntstatus );
+
+ if ( sec_desc )
+ TALLOC_FREE( sec_desc );
+
+ if ( !ret ) {
+ DEBUG(8,("elog_check_access: se_access_check() return %s\n",
+ nt_errstr( ntstatus)));
+ return False;
+ }
+
+ /* we have to have READ permission for a successful open */
+
+ return ( info->access_granted & SA_RIGHT_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( pipes_struct * p, const char *logname, POLICY_HND *hnd )
+{
+ 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_P( NULL, EVENTLOG_INFO )) )
+ return NT_STATUS_NO_MEMORY;
+
+ 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 );
+ 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, p->pipe_user.nt_user_token ) ) {
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ become_root();
+ elog->etdb = elog_open_tdb( elog->logname, 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, p->pipe_user.nt_user_token ) ) {
+ elog_close_tdb( elog->etdb, False );
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* create the policy handle */
+
+ if ( !create_policy_hnd
+ ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
+ free_eventlog_info( 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( pipes_struct *p, POLICY_HND *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 );
+}
+
+/********************************************************************
+ For the given tdb, get the next eventlog record into the passed
+ Eventlog_entry. returns NULL if it can't get the record for some reason.
+ ********************************************************************/
+
+static Eventlog_entry *get_eventlog_record(prs_struct *ps,
+ TDB_CONTEXT *tdb,
+ int recno)
+{
+ Eventlog_entry *ee = NULL;
+ TDB_DATA ret, key;
+
+ int srecno;
+ int reclen;
+ int len;
+
+ char *wpsource = NULL;
+ char *wpcomputer = NULL;
+ char *wpsid = NULL;
+ char *wpstrs = NULL;
+ char *puserdata = NULL;
+
+ key.dsize = sizeof(int32);
+
+ srecno = recno;
+ key.dptr = ( uint8 * ) &srecno;
+
+ ret = tdb_fetch( tdb, key );
+
+ if ( ret.dsize == 0 ) {
+ DEBUG( 8,
+ ( "Can't find a record for the key, record %d\n",
+ recno ) );
+ return NULL;
+ }
+
+ len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
+
+ DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
+
+ if ( !len )
+ return NULL;
+
+ ee = TALLOC_ARRAY(ps->mem_ctx, Eventlog_entry, 1);
+ if (!ee) {
+ return NULL;
+ }
+ ZERO_STRUCTP(ee);
+
+ len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
+ &ee->record.length, &ee->record.reserved1,
+ &ee->record.record_number,
+ &ee->record.time_generated,
+ &ee->record.time_written, &ee->record.event_id,
+ &ee->record.event_type, &ee->record.num_strings,
+ &ee->record.event_category, &ee->record.reserved2,
+ &ee->record.closing_record_number,
+ &ee->record.string_offset,
+ &ee->record.user_sid_length,
+ &ee->record.user_sid_offset,
+ &ee->record.data_length, &ee->record.data_offset,
+ &ee->data_record.source_name_len, &wpsource,
+ &ee->data_record.computer_name_len, &wpcomputer,
+ &ee->data_record.sid_padding,
+ &ee->record.user_sid_length, &wpsid,
+ &ee->data_record.strings_len, &wpstrs,
+ &ee->data_record.user_data_len, &puserdata,
+ &ee->data_record.data_padding );
+ DEBUG( 10,
+ ( "Read record %d, len in tdb was %d\n",
+ ee->record.record_number, len ) );
+
+ /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
+ into it's 2nd argment for 'B' */
+
+ if (wpcomputer) {
+ ee->data_record.computer_name = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpcomputer, ee->data_record.computer_name_len);
+ if (!ee->data_record.computer_name) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+ if (wpsource) {
+ ee->data_record.source_name = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpsource, ee->data_record.source_name_len);
+ if (!ee->data_record.source_name) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+
+ if (wpsid) {
+ ee->data_record.sid = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpsid, ee->record.user_sid_length);
+ if (!ee->data_record.sid) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+ if (wpstrs) {
+ ee->data_record.strings = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpstrs, ee->data_record.strings_len);
+ if (!ee->data_record.strings) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+
+ if (puserdata) {
+ ee->data_record.user_data = (char *)TALLOC_MEMDUP(
+ ee, puserdata, ee->data_record.user_data_len);
+ if (!ee->data_record.user_data) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+
+ out:
+
+ SAFE_FREE(wpcomputer);
+ SAFE_FREE(wpsource);
+ SAFE_FREE(wpsid);
+ SAFE_FREE(wpstrs);
+ SAFE_FREE(puserdata);
+
+ DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
+ DEBUG( 10,
+ ( "get_eventlog_record: computer_name %d is ",
+ ee->data_record.computer_name_len ) );
+ SAFE_FREE(ret.dptr);
+ return ee;
+}
+
+/********************************************************************
+ 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( EVENTLOG_INFO *info )
+{
+ char *path = NULL;
+ uint32 uiMaxSize;
+ uint32 uiRetention;
+ struct registry_key *key;
+ struct registry_value *value;
+ WERROR wresult;
+ char *elogname = info->logname;
+ TALLOC_CTX *ctx = talloc_tos();
+ bool ret = false;
+
+ DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
+
+ if ( !info->etdb ) {
+ DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
+ return False;
+ }
+ /* set resonable 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", KEY_EVENTLOG, elogname );
+ if (!path) {
+ return false;
+ }
+
+ wresult = reg_open_path(ctx, path, REG_KEY_READ, get_root_nt_token(),
+ &key);
+
+ if ( !W_ERROR_IS_OK( wresult ) ) {
+ DEBUG( 4,
+ ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
+ path, dos_errstr( wresult ) ) );
+ return false;
+ }
+
+ wresult = reg_queryvalue(key, key, "Retention", &value);
+ if (!W_ERROR_IS_OK(wresult)) {
+ DEBUG(4, ("Failed to query value \"Retention\": %s\n",
+ dos_errstr(wresult)));
+ ret = false;
+ goto done;
+ }
+ uiRetention = value->v.dword;
+
+ wresult = reg_queryvalue(key, key, "MaxSize", &value);
+ if (!W_ERROR_IS_OK(wresult)) {
+ DEBUG(4, ("Failed to query value \"MaxSize\": %s\n",
+ dos_errstr(wresult)));
+ ret = false;
+ goto done;
+ }
+ uiMaxSize = value->v.dword;
+
+ 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:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static Eventlog_entry *read_package_entry( prs_struct * ps,
+ Eventlog_entry * entry )
+{
+ uint8 *offset;
+ Eventlog_entry *ee_new = NULL;
+
+ ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
+ if ( ee_new == NULL ) {
+ return NULL;
+ }
+
+ entry->data_record.sid_padding =
+ ( ( 4 -
+ ( ( entry->data_record.source_name_len +
+ entry->data_record.computer_name_len ) % 4 ) ) % 4 );
+ entry->data_record.data_padding =
+ ( 4 -
+ ( ( entry->data_record.strings_len +
+ entry->data_record.user_data_len ) % 4 ) ) % 4;
+ entry->record.length = sizeof( Eventlog_record );
+ entry->record.length += entry->data_record.source_name_len;
+ entry->record.length += entry->data_record.computer_name_len;
+ if ( entry->record.user_sid_length == 0 ) {
+ /* Should not pad to a DWORD boundary for writing out the sid if there is
+ no SID, so just propagate the padding to pad the data */
+ entry->data_record.data_padding +=
+ entry->data_record.sid_padding;
+ entry->data_record.sid_padding = 0;
+ }
+ DEBUG( 10,
+ ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );
+ DEBUG( 10,
+ ( "data_padding is [%d].\n",
+ entry->data_record.data_padding ) );
+
+ entry->record.length += entry->data_record.sid_padding;
+ entry->record.length += entry->record.user_sid_length;
+ entry->record.length += entry->data_record.strings_len;
+ entry->record.length += entry->data_record.user_data_len;
+ entry->record.length += entry->data_record.data_padding;
+ /* need another copy of length at the end of the data */
+ entry->record.length += sizeof( entry->record.length );
+ DEBUG( 10,
+ ( "entry->record.length is [%d].\n", entry->record.length ) );
+ entry->data =
+ PRS_ALLOC_MEM( ps, uint8,
+ entry->record.length -
+ sizeof( Eventlog_record ) -
+ sizeof( entry->record.length ) );
+ if ( entry->data == NULL ) {
+ return NULL;
+ }
+ offset = entry->data;
+ memcpy( offset, &( entry->data_record.source_name ),
+ entry->data_record.source_name_len );
+ offset += entry->data_record.source_name_len;
+ memcpy( offset, &( entry->data_record.computer_name ),
+ entry->data_record.computer_name_len );
+ offset += entry->data_record.computer_name_len;
+ /* SID needs to be DWORD-aligned */
+ offset += entry->data_record.sid_padding;
+ entry->record.user_sid_offset =
+ sizeof( Eventlog_record ) + ( offset - entry->data );
+ memcpy( offset, &( entry->data_record.sid ),
+ entry->record.user_sid_length );
+ offset += entry->record.user_sid_length;
+ /* Now do the strings */
+ entry->record.string_offset =
+ sizeof( Eventlog_record ) + ( offset - entry->data );
+ memcpy( offset, &( entry->data_record.strings ),
+ entry->data_record.strings_len );
+ offset += entry->data_record.strings_len;
+ /* Now do the data */
+ entry->record.data_length = entry->data_record.user_data_len;
+ entry->record.data_offset =
+ sizeof( Eventlog_record ) + ( offset - entry->data );
+ memcpy( offset, &( entry->data_record.user_data ),
+ entry->data_record.user_data_len );
+ offset += entry->data_record.user_data_len;
+
+ memcpy( &( ee_new->record ), &entry->record,
+ sizeof( Eventlog_record ) );
+ memcpy( &( ee_new->data_record ), &entry->data_record,
+ sizeof( Eventlog_data_record ) );
+ ee_new->data = entry->data;
+
+ return ee_new;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static bool add_record_to_resp( EVENTLOG_R_READ_EVENTLOG * r_u,
+ Eventlog_entry * ee_new )
+{
+ Eventlog_entry *insert_point;
+
+ insert_point = r_u->entry;
+
+ if ( NULL == insert_point ) {
+ r_u->entry = ee_new;
+ ee_new->next = NULL;
+ } else {
+ while ( ( NULL != insert_point->next ) ) {
+ insert_point = insert_point->next;
+ }
+ ee_new->next = NULL;
+ insert_point->next = ee_new;
+ }
+ r_u->num_records++;
+ r_u->num_bytes_in_resp += ee_new->record.length;
+
+ return True;
+}
+
+/********************************************************************
+ _eventlog_OpenEventLogW
+ ********************************************************************/
+
+NTSTATUS _eventlog_OpenEventLogW(pipes_struct *p,
+ struct eventlog_OpenEventLogW *r)
+{
+ const char *servername = "";
+ const char *logname = "";
+ EVENTLOG_INFO *info;
+ NTSTATUS result;
+
+ if (r->in.servername->string) {
+ servername = r->in.servername->string;
+ }
+
+ if (r->in.logname->string) {
+ logname = r->in.logname->string;
+ }
+
+ DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
+ servername, logname ));
+
+ /* 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, logname, r->out.handle )) )
+ return result;
+
+ if ( !(info = find_eventlog_info_by_hnd( p, r->out.handle )) ) {
+ DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
+ logname ));
+ elog_close( p, r->out.handle );
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info )));
+
+ sync_eventlog_params( info );
+ 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(pipes_struct *p,
+ struct eventlog_ClearEventLogW *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+ const char *backup_file_name = NULL;
+
+ if ( !info )
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (r->in.backupfile && r->in.backupfile->string) {
+
+ backup_file_name = r->in.backupfile->string;
+
+ DEBUG(8,( "_eventlog_clear_eventlog: Using [%s] as the backup "
+ "file name for log [%s].",
+ backup_file_name, info->logname ) );
+ }
+
+ /* check for WRITE access to the file */
+
+ if ( !(info->access_granted&SA_RIGHT_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 );
+ unbecome_root();
+
+ if ( !info->etdb )
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+NTSTATUS _eventlog_CloseEventLog( pipes_struct * p, struct eventlog_CloseEventLog *r )
+{
+ return elog_close( p, r->in.handle );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+NTSTATUS _eventlog_read_eventlog( pipes_struct * p,
+ EVENTLOG_Q_READ_EVENTLOG * q_u,
+ EVENTLOG_R_READ_EVENTLOG * r_u )
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
+ Eventlog_entry *entry = NULL, *ee_new = NULL;
+ uint32 num_records_read = 0;
+ prs_struct *ps;
+ int bytes_left, record_number;
+ uint32 elog_read_type, elog_read_dir;
+
+ if (info == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ info->flags = q_u->flags;
+ ps = &p->out_data.rdata;
+
+ bytes_left = q_u->max_read_size;
+
+ if ( !info->etdb )
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check for valid flags. Can't use the sequential and seek flags together */
+
+ elog_read_type = q_u->flags & (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ);
+ elog_read_dir = q_u->flags & (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ);
+
+ if ( elog_read_type == (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ)
+ || elog_read_dir == (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ) )
+ {
+ DEBUG(3,("_eventlog_read_eventlog: Invalid flags [0x%x] for ReadEventLog\n", q_u->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 = q_u->offset;
+
+ while ( bytes_left > 0 ) {
+
+ /* assume that when the record fetch fails, that we are done */
+
+ entry = get_eventlog_record (ps, ELOG_TDB_CTX(info->etdb), record_number);
+ if (!entry) {
+ break;
+ }
+
+ DEBUG( 8, ( "Retrieved record %d\n", record_number ) );
+
+ /* Now see if there is enough room to add */
+
+ if ( !(ee_new = read_package_entry( ps, entry )) )
+ return NT_STATUS_NO_MEMORY;
+
+ if ( r_u->num_bytes_in_resp + ee_new->record.length > q_u->max_read_size ) {
+ r_u->bytes_in_next_record = ee_new->record.length;
+
+ /* response would be too big to fit in client-size buffer */
+
+ bytes_left = 0;
+ break;
+ }
+
+ add_record_to_resp( r_u, ee_new );
+ bytes_left -= ee_new->record.length;
+ TALLOC_FREE(entry);
+ num_records_read = r_u->num_records - num_records_read;
+
+ DEBUG( 10, ( "_eventlog_read_eventlog: read [%d] records for a total "
+ "of [%d] records using [%d] bytes out of a max of [%d].\n",
+ num_records_read, r_u->num_records,
+ r_u->num_bytes_in_resp,
+ q_u->max_read_size ) );
+
+ if ( info->flags & EVENTLOG_FORWARDS_READ )
+ record_number++;
+ else
+ record_number--;
+
+ /* update the eventlog record pointer */
+
+ info->current_record = record_number;
+ }
+
+ /* crazy by WinXP uses NT_STATUS_BUFFER_TOO_SMALL to
+ say when there are no more records */
+
+ return (num_records_read ? NT_STATUS_OK : NT_STATUS_BUFFER_TOO_SMALL);
+}
+
+/********************************************************************
+ _eventlog_GetOldestRecord
+ ********************************************************************/
+
+NTSTATUS _eventlog_GetOldestRecord(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(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(pipes_struct *p, struct eventlog_BackupEventLogW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_DeregisterEventSource(pipes_struct *p, struct eventlog_DeregisterEventSource *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ChangeNotify(pipes_struct *p, struct eventlog_ChangeNotify *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterEventSourceW(pipes_struct *p, struct eventlog_RegisterEventSourceW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenBackupEventLogW(pipes_struct *p, struct eventlog_OpenBackupEventLogW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReadEventLogW(pipes_struct *p, struct eventlog_ReadEventLogW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReportEventW(pipes_struct *p, struct eventlog_ReportEventW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ClearEventLogA(pipes_struct *p, struct eventlog_ClearEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_BackupEventLogA(pipes_struct *p, struct eventlog_BackupEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenEventLogA(pipes_struct *p, struct eventlog_OpenEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterEventSourceA(pipes_struct *p, struct eventlog_RegisterEventSourceA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenBackupEventLogA(pipes_struct *p, struct eventlog_OpenBackupEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReadEventLogA(pipes_struct *p, struct eventlog_ReadEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReportEventA(pipes_struct *p, struct eventlog_ReportEventA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterClusterSvc(pipes_struct *p, struct eventlog_RegisterClusterSvc *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_DeregisterClusterSvc(pipes_struct *p, struct eventlog_DeregisterClusterSvc *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_WriteClusterEvents(pipes_struct *p, struct eventlog_WriteClusterEvents *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_GetLogIntormation(pipes_struct *p, struct eventlog_GetLogIntormation *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_FlushEventLog(pipes_struct *p, struct eventlog_FlushEventLog *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
diff --git a/source3/rpc_server/srv_initshutdown_nt.c b/source3/rpc_server/srv_initshutdown_nt.c
new file mode 100644
index 0000000000..8b3ef52293
--- /dev/null
+++ b/source3/rpc_server/srv_initshutdown_nt.c
@@ -0,0 +1,77 @@
+/*
+ * 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 "regfio.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+
+/*******************************************************************
+ ********************************************************************/
+WERROR _initshutdown_Init(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(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(pipes_struct *p, struct initshutdown_Abort *r)
+{
+ struct winreg_AbortSystemShutdown s;
+ s.in.server = r->in.server;
+ return _winreg_AbortSystemShutdown( p, &s );
+}
diff --git a/source3/rpc_server/srv_lsa_hnd.c b/source3/rpc_server/srv_lsa_hnd.c
new file mode 100644
index 0000000000..377ed505b4
--- /dev/null
+++ b/source3/rpc_server/srv_lsa_hnd.c
@@ -0,0 +1,271 @@
+/*
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/* This is the max handles across all instances of a pipe name. */
+#ifndef MAX_OPEN_POLS
+#define MAX_OPEN_POLS 1024
+#endif
+
+/****************************************************************************
+ Hack as handles need to be persisant over lsa pipe closes so long as a samr
+ pipe is open. JRA.
+****************************************************************************/
+
+static bool is_samr_lsa_pipe(const char *pipe_name)
+{
+ return (strstr(pipe_name, "samr") || strstr(pipe_name, "lsa"));
+}
+
+/****************************************************************************
+ Initialise a policy handle list on a pipe. Handle list is shared between all
+ pipes of the same name.
+****************************************************************************/
+
+bool init_pipe_handle_list(pipes_struct *p, const char *pipe_name)
+{
+ pipes_struct *plist = get_first_internal_pipe();
+ struct handle_list *hl = NULL;
+
+ for (plist = get_first_internal_pipe(); plist; plist = get_next_internal_pipe(plist)) {
+ if (strequal( plist->name, pipe_name) ||
+ (is_samr_lsa_pipe(plist->name) && is_samr_lsa_pipe(pipe_name))) {
+ if (!plist->pipe_handles) {
+ char *msg;
+ asprintf(&msg, "init_pipe_handles: NULL "
+ "pipe_handle pointer in pipe %s",
+ pipe_name);
+ smb_panic(msg);
+ }
+ hl = plist->pipe_handles;
+ break;
+ }
+ }
+
+ if (!hl) {
+ /*
+ * No handle list for this pipe (first open of pipe).
+ * Create list.
+ */
+
+ if ((hl = SMB_MALLOC_P(struct handle_list)) == NULL)
+ return False;
+ ZERO_STRUCTP(hl);
+
+ DEBUG(10,("init_pipe_handles: created handle list for pipe %s\n", pipe_name ));
+ }
+
+ /*
+ * One more pipe is using this list.
+ */
+
+ hl->pipe_ref_count++;
+
+ /*
+ * Point this pipe at this list.
+ */
+
+ p->pipe_handles = hl;
+
+ DEBUG(10,("init_pipe_handles: pipe_handles ref count = %lu for pipe %s\n",
+ (unsigned long)p->pipe_handles->pipe_ref_count, pipe_name ));
+
+ return True;
+}
+
+/****************************************************************************
+ find first available policy slot. creates a policy handle for you.
+****************************************************************************/
+
+bool create_policy_hnd(pipes_struct *p, POLICY_HND *hnd, void (*free_fn)(void *), void *data_ptr)
+{
+ static uint32 pol_hnd_low = 0;
+ static uint32 pol_hnd_high = 0;
+ time_t t = time(NULL);
+
+ struct policy *pol;
+
+ if (p->pipe_handles->count > MAX_OPEN_POLS) {
+ DEBUG(0,("create_policy_hnd: ERROR: too many handles (%d) on this pipe.\n",
+ (int)p->pipe_handles->count));
+ return False;
+ }
+
+ pol = SMB_MALLOC_P(struct policy);
+ if (!pol) {
+ DEBUG(0,("create_policy_hnd: ERROR: out of memory!\n"));
+ return False;
+ }
+
+ ZERO_STRUCTP(pol);
+
+ pol->data_ptr = data_ptr;
+ pol->free_fn = free_fn;
+
+ pol_hnd_low++;
+ if (pol_hnd_low == 0)
+ (pol_hnd_high)++;
+
+ SIVAL(&pol->pol_hnd.handle_type, 0 , 0); /* first bit must be null */
+ SIVAL(&pol->pol_hnd.uuid.time_low, 0 , pol_hnd_low ); /* second bit is incrementing */
+ SSVAL(&pol->pol_hnd.uuid.time_mid, 0 , pol_hnd_high); /* second bit is incrementing */
+ SSVAL(&pol->pol_hnd.uuid.time_hi_and_version, 0 , (pol_hnd_high>>16)); /* second bit is incrementing */
+
+ /* split the current time into two 16 bit values */
+
+ SSVAL(pol->pol_hnd.uuid.clock_seq, 0, (t>>16)); /* something random */
+ SSVAL(pol->pol_hnd.uuid.node, 0, t); /* something random */
+
+ SIVAL(pol->pol_hnd.uuid.node, 2, sys_getpid()); /* something more random */
+
+ DLIST_ADD(p->pipe_handles->Policy, pol);
+ p->pipe_handles->count++;
+
+ *hnd = pol->pol_hnd;
+
+ DEBUG(4,("Opened policy hnd[%d] ", (int)p->pipe_handles->count));
+ dump_data(4, (uint8 *)hnd, sizeof(*hnd));
+
+ return True;
+}
+
+/****************************************************************************
+ find policy by handle - internal version.
+****************************************************************************/
+
+static struct policy *find_policy_by_hnd_internal(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+ struct policy *pol;
+ size_t i;
+
+ if (data_p)
+ *data_p = NULL;
+
+ for (i = 0, pol=p->pipe_handles->Policy;pol;pol=pol->next, i++) {
+ if (memcmp(&pol->pol_hnd, hnd, sizeof(*hnd)) == 0) {
+ DEBUG(4,("Found policy hnd[%d] ", (int)i));
+ dump_data(4, (uint8 *)hnd, sizeof(*hnd));
+ if (data_p)
+ *data_p = pol->data_ptr;
+ return pol;
+ }
+ }
+
+ DEBUG(4,("Policy not found: "));
+ dump_data(4, (uint8 *)hnd, sizeof(*hnd));
+
+ p->bad_handle_fault_state = True;
+
+ return NULL;
+}
+
+/****************************************************************************
+ find policy by handle
+****************************************************************************/
+
+bool find_policy_by_hnd(pipes_struct *p, POLICY_HND *hnd, void **data_p)
+{
+ return find_policy_by_hnd_internal(p, hnd, data_p) == NULL ? False : True;
+}
+
+/****************************************************************************
+ Close a policy.
+****************************************************************************/
+
+bool close_policy_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ struct policy *pol = find_policy_by_hnd_internal(p, hnd, NULL);
+
+ if (!pol) {
+ DEBUG(3,("Error closing policy\n"));
+ return False;
+ }
+
+ DEBUG(3,("Closed policy\n"));
+
+ if (pol->free_fn && pol->data_ptr)
+ (*pol->free_fn)(pol->data_ptr);
+
+ p->pipe_handles->count--;
+
+ DLIST_REMOVE(p->pipe_handles->Policy, pol);
+
+ ZERO_STRUCTP(pol);
+
+ SAFE_FREE(pol);
+
+ return True;
+}
+
+/****************************************************************************
+ Close a pipe - free the handle list if it was the last pipe reference.
+****************************************************************************/
+
+void close_policy_by_pipe(pipes_struct *p)
+{
+ p->pipe_handles->pipe_ref_count--;
+
+ if (p->pipe_handles->pipe_ref_count == 0) {
+ /*
+ * Last pipe open on this list - free the list.
+ */
+ while (p->pipe_handles->Policy)
+ close_policy_hnd(p, &p->pipe_handles->Policy->pol_hnd);
+
+ p->pipe_handles->Policy = NULL;
+ p->pipe_handles->count = 0;
+
+ SAFE_FREE(p->pipe_handles);
+ DEBUG(10,("close_policy_by_pipe: deleted handle list for pipe %s\n", p->name ));
+ }
+}
+
+/*******************************************************************
+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(pipes_struct *p)
+{
+ /* Don't let anonymous users access this RPC if restrict
+ anonymous > 0 */
+
+ if (lp_restrict_anonymous() > 0) {
+
+ /* schannel, so we must be ok */
+ if (p->pipe_bound && (p->auth.auth_type == PIPE_AUTH_TYPE_SCHANNEL)) {
+ return True;
+ }
+
+ if (p->server_info->guest) {
+ return False;
+ }
+ }
+
+ return True;
+}
diff --git a/source3/rpc_server/srv_lsa_nt.c b/source3/rpc_server/srv_lsa_nt.c
new file mode 100644
index 0000000000..94517f3478
--- /dev/null
+++ b/source3/rpc_server/srv_lsa_nt.c
@@ -0,0 +1,2426 @@
+/*
+ * 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.
+ *
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+extern PRIVS privs[];
+
+struct lsa_info {
+ DOM_SID sid;
+ uint32 access;
+};
+
+const struct generic_mapping lsa_generic_mapping = {
+ LSA_POLICY_READ,
+ LSA_POLICY_WRITE,
+ LSA_POLICY_EXECUTE,
+ LSA_POLICY_ALL_ACCESS
+};
+
+/***************************************************************************
+ init_lsa_ref_domain_list - adds a domain if it's not already in, returns the index.
+***************************************************************************/
+
+static int init_lsa_ref_domain_list(TALLOC_CTX *mem_ctx,
+ struct lsa_RefDomainList *ref,
+ const char *dom_name,
+ DOM_SID *dom_sid)
+{
+ int num = 0;
+
+ if (dom_name != NULL) {
+ for (num = 0; num < ref->count; num++) {
+ if (sid_equal(dom_sid, ref->domains[num].sid)) {
+ return num;
+ }
+ }
+ } else {
+ num = ref->count;
+ }
+
+ if (num >= MAX_REF_DOMAINS) {
+ /* index not found, already at maximum domain limit */
+ return -1;
+ }
+
+ ref->count = num + 1;
+ ref->max_size = MAX_REF_DOMAINS;
+
+ ref->domains = TALLOC_REALLOC_ARRAY(mem_ctx, ref->domains,
+ struct lsa_DomainInfo, ref->count);
+ if (!ref->domains) {
+ return -1;
+ }
+
+ ZERO_STRUCT(ref->domains[num]);
+
+ init_lsa_StringLarge(&ref->domains[num].name, dom_name);
+ ref->domains[num].sid = sid_dup_talloc(mem_ctx, dom_sid);
+ if (!ref->domains[num].sid) {
+ return -1;
+ }
+
+ return num;
+}
+
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_lsa_info(void *ptr)
+{
+ struct lsa_info *lsa = (struct lsa_info *)ptr;
+
+ SAFE_FREE(lsa);
+}
+
+/***************************************************************************
+ initialize a lsa_DomainInfo structure.
+ ***************************************************************************/
+
+static void init_dom_query_3(struct lsa_DomainInfo *r,
+ const char *name,
+ 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,
+ 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 mapped_count, i;
+
+ SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+ mapped_count = 0;
+ *pmapped_count = 0;
+
+ for (i = 0; i < num_entries; i++) {
+ DOM_SID sid;
+ uint32 rid;
+ int dom_idx;
+ const char *full_name;
+ const char *domain;
+ enum lsa_SidType type = SID_NAME_UNKNOWN;
+
+ /* 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_rids: looking up name %s\n", full_name));
+
+ /* We can ignore the result of lookup_name, it will not touch
+ "type" if it's not successful */
+
+ lookup_name(mem_ctx, full_name, flags, &domain, NULL,
+ &sid, &type);
+
+ 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) {
+ sid_split_rid(&sid, &rid);
+ dom_idx = init_lsa_ref_domain_list(mem_ctx, ref, domain, &sid);
+ mapped_count++;
+ }
+
+ init_lsa_translated_sid(&prid[i], type, rid, 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 *pmapped_count)
+{
+ uint32 mapped_count, i;
+
+ SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS);
+
+ mapped_count = 0;
+ *pmapped_count = 0;
+
+ for (i = 0; i < num_entries; i++) {
+ DOM_SID sid;
+ uint32 rid;
+ int dom_idx;
+ const char *full_name;
+ const char *domain;
+ enum lsa_SidType type = SID_NAME_UNKNOWN;
+
+ 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, ("init_lsa_sids: looking up name %s\n", full_name));
+
+ /* We can ignore the result of lookup_name, it will not touch
+ "type" if it's not successful */
+
+ lookup_name(mem_ctx, full_name, flags, &domain, NULL,
+ &sid, &type);
+
+ 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_sids: %s found\n", full_name));
+ /* Leave these unchanged */
+ break;
+ default:
+ /* Don't hand out anything but the list above */
+ DEBUG(5, ("init_lsa_sids: %s not found\n", full_name));
+ type = SID_NAME_UNKNOWN;
+ break;
+ }
+
+ rid = 0;
+ dom_idx = -1;
+
+ if (type != SID_NAME_UNKNOWN) {
+ 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 = sid_dup_talloc(mem_ctx, &sid);
+ trans_sids[i].sid_index = dom_idx;
+ }
+
+ *pmapped_count = mapped_count;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS lsa_get_generic_sd(TALLOC_CTX *mem_ctx, SEC_DESC **sd, size_t *sd_size)
+{
+ DOM_SID local_adm_sid;
+ DOM_SID adm_sid;
+
+ SEC_ACE ace[3];
+ SEC_ACCESS mask;
+
+ SEC_ACL *psa = NULL;
+
+ init_sec_access(&mask, LSA_POLICY_EXECUTE);
+ init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ sid_copy(&adm_sid, get_global_sam_sid());
+ sid_append_rid(&adm_sid, DOMAIN_GROUP_RID_ADMINS);
+ init_sec_access(&mask, LSA_POLICY_ALL_ACCESS);
+ init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ sid_copy(&local_adm_sid, &global_sid_Builtin);
+ sid_append_rid(&local_adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+ init_sec_access(&mask, LSA_POLICY_ALL_ACCESS);
+ init_sec_ace(&ace[2], &local_adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ if((psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, 3, 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;
+}
+
+#if 0 /* AD DC work in ongoing in Samba 4 */
+
+/***************************************************************************
+ Init_dns_dom_info.
+***************************************************************************/
+
+static void init_dns_dom_info(LSA_DNS_DOM_INFO *r_l, const char *nb_name,
+ const char *dns_name, const char *forest_name,
+ struct GUID *dom_guid, DOM_SID *dom_sid)
+{
+ if (nb_name && *nb_name) {
+ init_unistr2(&r_l->uni_nb_dom_name, nb_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_nb_dom_name, &r_l->uni_nb_dom_name);
+ r_l->hdr_nb_dom_name.uni_max_len += 2;
+ r_l->uni_nb_dom_name.uni_max_len += 1;
+ }
+
+ if (dns_name && *dns_name) {
+ init_unistr2(&r_l->uni_dns_dom_name, dns_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_dns_dom_name, &r_l->uni_dns_dom_name);
+ r_l->hdr_dns_dom_name.uni_max_len += 2;
+ r_l->uni_dns_dom_name.uni_max_len += 1;
+ }
+
+ if (forest_name && *forest_name) {
+ init_unistr2(&r_l->uni_forest_name, forest_name, UNI_FLAGS_NONE);
+ init_uni_hdr(&r_l->hdr_forest_name, &r_l->uni_forest_name);
+ r_l->hdr_forest_name.uni_max_len += 2;
+ r_l->uni_forest_name.uni_max_len += 1;
+ }
+
+ /* how do we init the guid ? probably should write an init fn */
+ if (dom_guid) {
+ memcpy(&r_l->dom_guid, dom_guid, sizeof(struct GUID));
+ }
+
+ if (dom_sid) {
+ r_l->ptr_dom_sid = 1;
+ init_dom_sid2(&r_l->dom_sid, dom_sid);
+ }
+}
+#endif /* AD DC work in ongoing in Samba 4 */
+
+
+/***************************************************************************
+ _lsa_OpenPolicy2
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenPolicy2(pipes_struct *p,
+ struct lsa_OpenPolicy2 *r)
+{
+ struct lsa_info *info;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 des_access = r->in.access_mask;
+ uint32 acc_granted;
+ NTSTATUS status;
+
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_generic_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+ if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status)) {
+ if (p->pipe_user.ut.uid != sec_initial_uid()) {
+ return status;
+ }
+ DEBUG(4,("ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ acc_granted, des_access));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ }
+
+ /* This is needed for lsa_open_account and rpcclient .... :-) */
+
+ if (p->pipe_user.ut.uid == sec_initial_uid())
+ acc_granted = LSA_POLICY_ALL_ACCESS;
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = SMB_MALLOC_P(struct lsa_info)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ sid_copy(&info->sid,get_global_sam_sid());
+ info->access = acc_granted;
+
+ /* set up the LSA QUERY INFO response */
+ if (!create_policy_hnd(p, r->out.handle, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_OpenPolicy
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenPolicy(pipes_struct *p,
+ struct lsa_OpenPolicy *r)
+{
+ struct lsa_info *info;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ uint32 des_access= r->in.access_mask;
+ uint32 acc_granted;
+ NTSTATUS status;
+
+
+ /* map the generic bits to the lsa policy ones */
+ se_map_generic(&des_access, &lsa_generic_mapping);
+
+ /* get the generic lsa policy SD until we store it */
+ lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+
+ if(!se_access_check(psd, p->pipe_user.nt_user_token, des_access, &acc_granted, &status)) {
+ if (geteuid() != 0) {
+ return status;
+ }
+ DEBUG(4,("ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ acc_granted, des_access));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+ acc_granted = des_access;
+ }
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = SMB_MALLOC_P(struct lsa_info)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ sid_copy(&info->sid,get_global_sam_sid());
+ info->access = acc_granted;
+
+ /* set up the LSA QUERY INFO response */
+ if (!create_policy_hnd(p, r->out.handle, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_EnumTrustDom - this needs fixing to do more than return NULL ! JRA.
+ ufff, done :) mimir
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumTrustDom(pipes_struct *p,
+ struct lsa_EnumTrustDom *r)
+{
+ struct lsa_info *info;
+ uint32 next_idx;
+ struct trustdom_info **domains;
+ struct lsa_DomainInfo *lsa_domains = NULL;
+ int i;
+
+ /*
+ * preferred length is set to 5 as a "our" preferred length
+ * nt sets this parameter to 2
+ * update (20.08.2002): it's not preferred length, but preferred size!
+ * it needs further investigation how to optimally choose this value
+ */
+ uint32 max_num_domains =
+ r->in.max_size < 5 ? r->in.max_size : 10;
+ uint32 num_domains;
+ NTSTATUS nt_status;
+ uint32 num_thistime;
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ 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, &num_domains, &domains);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (*r->in.resume_handle < num_domains) {
+ num_thistime = MIN(num_domains, max_num_domains);
+
+ nt_status = STATUS_MORE_ENTRIES;
+
+ if (*r->in.resume_handle + num_thistime > num_domains) {
+ num_thistime = num_domains - *r->in.resume_handle;
+ nt_status = NT_STATUS_OK;
+ }
+
+ next_idx = *r->in.resume_handle + num_thistime;
+ } else {
+ num_thistime = 0;
+ next_idx = 0xffffffff;
+ nt_status = NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* set up the lsa_enum_trust_dom response */
+
+ lsa_domains = TALLOC_ZERO_ARRAY(p->mem_ctx, struct lsa_DomainInfo,
+ num_thistime);
+ if (!lsa_domains) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_thistime; i++) {
+ init_lsa_StringLarge(&lsa_domains[i].name, domains[i]->name);
+ lsa_domains[i].sid = &domains[i]->sid;
+ }
+
+ *r->out.resume_handle = next_idx;
+ r->out.domains->count = num_thistime;
+ r->out.domains->domains = lsa_domains;
+
+ return nt_status;
+}
+
+#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(pipes_struct *p,
+ struct lsa_QueryInfoPolicy *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct lsa_info *handle;
+ DOM_SID domain_sid;
+ const char *name;
+ DOM_SID *sid = NULL;
+ union lsa_PolicyInformation *info = NULL;
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ info = TALLOC_ZERO_P(p->mem_ctx, union lsa_PolicyInformation);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 0x02:
+ {
+
+ uint32 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 0x03:
+ /* 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:
+ name = get_global_sam_name();
+ sid = sid_dup_talloc(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 = sid_dup_talloc(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 0x05:
+ /* 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 0x06:
+ /* 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 = 2;
+ break;
+ default:
+ /*
+ * any other role is a primary
+ * of the domain, it controls.
+ */
+ info->role.role = 3;
+ break;
+ }
+ 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_lookup_sids_internal
+ ***************************************************************************/
+
+static NTSTATUS _lsa_lookup_sids_internal(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 DOM_SID **sids = NULL;
+ struct lsa_RefDomainList *ref = NULL;
+ uint32 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 DOM_SID *, num_sids);
+ ref = TALLOC_ZERO_P(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<MAX_REF_DOMAINS; 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) {
+ fstring tmp;
+ 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 = talloc_asprintf(p->mem_ctx, "%s",
+ sid_to_fstring(tmp,
+ sids[i]));
+ if (name->name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ mapped_count += 1;
+ }
+
+ init_lsa_translated_name2(&names[i], name->type,
+ name->name, name->dom_idx, 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(pipes_struct *p,
+ struct lsa_LookupSids *r)
+{
+ NTSTATUS status;
+ struct lsa_info *handle;
+ int num_sids = r->in.sids->num_sids;
+ uint32 mapped_count = 0;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedName *names_out = NULL;
+ struct lsa_TranslatedName2 *names = NULL;
+ int i;
+
+ if ((r->in.level < 1) || (r->in.level > 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle)) {
+ 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);
+
+ /* 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;
+}
+
+/***************************************************************************
+ _lsa_LookupSids2
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupSids2(pipes_struct *p,
+ struct lsa_LookupSids2 *r)
+{
+ NTSTATUS status;
+ struct lsa_info *handle;
+ int num_sids = r->in.sids->num_sids;
+ uint32 mapped_count = 0;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedName2 *names = NULL;
+ bool check_policy = true;
+
+ switch (p->hdr_req.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) {
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle)) {
+ 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_LookupSids3
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupSids3(pipes_struct *p,
+ struct lsa_LookupSids3 *r)
+{
+ struct lsa_LookupSids2 q;
+
+ /* No policy handle on this call. Restrict to crypto connections. */
+ if (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) {
+ DEBUG(0,("_lsa_LookupSids3: client %s not using schannel for netlogon\n",
+ get_remote_machine_name() ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ q.in.handle = NULL;
+ q.in.sids = r->in.sids;
+ q.in.level = r->in.level;
+ q.in.unknown1 = r->in.unknown1;
+ q.in.unknown2 = r->in.unknown2;
+ 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_LookupSids2(p, &q);
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+static int lsa_lookup_level_to_flags(uint16 level)
+{
+ int flags;
+
+ switch (level) {
+ case 1:
+ flags = LOOKUP_NAME_ALL;
+ break;
+ case 2:
+ flags = LOOKUP_NAME_DOMAIN|LOOKUP_NAME_REMOTE|LOOKUP_NAME_ISOLATED;
+ break;
+ case 3:
+ flags = LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ default:
+ flags = LOOKUP_NAME_NONE;
+ break;
+ }
+
+ return flags;
+}
+
+/***************************************************************************
+ _lsa_LookupNames
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames(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 num_entries = r->in.num_names;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedSid *rids = NULL;
+ uint32 mapped_count = 0;
+ int flags = 0;
+
+ 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(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;
+ }
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle)) {
+ 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(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;
+
+ sid_array = TALLOC_ZERO_P(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->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;
+}
+
+/***************************************************************************
+ _lsa_LookupNames3
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames3(pipes_struct *p,
+ struct lsa_LookupNames3 *r)
+{
+ NTSTATUS status;
+ struct lsa_info *handle;
+ struct lsa_String *names = r->in.names;
+ uint32 num_entries = r->in.num_names;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TranslatedSid3 *trans_sids = NULL;
+ uint32 mapped_count = 0;
+ int flags = 0;
+ bool check_policy = true;
+
+ switch (p->hdr_req.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));
+ }
+
+ /* Probably the lookup_level is some sort of bitmask. */
+ if (r->in.level == 1) {
+ flags = LOOKUP_NAME_ALL;
+ }
+
+ domains = TALLOC_ZERO_P(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) {
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle)) {
+ 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_LookupNames4
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupNames4(pipes_struct *p,
+ struct lsa_LookupNames4 *r)
+{
+ struct lsa_LookupNames3 q;
+
+ /* No policy handle on this call. Restrict to crypto connections. */
+ if (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) {
+ DEBUG(0,("_lsa_lookup_names4: client %s not using schannel for netlogon\n",
+ get_remote_machine_name() ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ 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.unknown1 = r->in.unknown1;
+ q.in.unknown2 = r->in.unknown2;
+ 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_LookupNames3(p, &q);
+}
+
+/***************************************************************************
+ _lsa_close. Also weird - needs to check if lsa handle is correct. JRA.
+ ***************************************************************************/
+
+NTSTATUS _lsa_Close(pipes_struct *p, struct lsa_Close *r)
+{
+ if (!find_policy_by_hnd(p, r->in.handle, NULL)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ close_policy_hnd(p, r->in.handle);
+ ZERO_STRUCTP(r->out.handle);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenSecret(pipes_struct *p, struct lsa_OpenSecret *r)
+{
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenTrustedDomain(pipes_struct *p, struct lsa_OpenTrustedDomain *r)
+{
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateTrustedDomain(pipes_struct *p, struct lsa_CreateTrustedDomain *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateSecret(pipes_struct *p, struct lsa_CreateSecret *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS _lsa_SetSecret(pipes_struct *p, struct lsa_SetSecret *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/***************************************************************************
+ _lsa_DeleteObject
+ ***************************************************************************/
+
+NTSTATUS _lsa_DeleteObject(pipes_struct *p,
+ struct lsa_DeleteObject *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/***************************************************************************
+ _lsa_EnumPrivs
+ ***************************************************************************/
+
+NTSTATUS _lsa_EnumPrivs(pipes_struct *p,
+ struct lsa_EnumPrivs *r)
+{
+ struct lsa_info *handle;
+ uint32 i;
+ uint32 enum_context = *r->in.resume_handle;
+ int num_privs = count_all_privileges();
+ struct lsa_PrivEntry *entries = NULL;
+ LUID_ATTR luid;
+
+ /* 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));
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ 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, privs[i].name);
+
+ luid = get_privilege_luid( &privs[i].se_priv );
+
+ entries[i].luid.low = luid.luid.low;
+ entries[i].luid.high = luid.luid.high;
+ }
+ }
+
+ 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(pipes_struct *p,
+ struct lsa_LookupPrivDisplayName *r)
+{
+ struct lsa_info *handle;
+ const char *description;
+ struct lsa_StringLarge *lsa_name;
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ 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(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(pipes_struct *p,
+ struct lsa_EnumAccounts *r)
+{
+ struct lsa_info *handle;
+ DOM_SID *sid_list;
+ int i, j, num_entries;
+ NTSTATUS status;
+ struct lsa_SidPtr *sids = NULL;
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ 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) {
+ SAFE_FREE(sid_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = *r->in.resume_handle, j = 0; i < num_entries; i++, j++) {
+ sids[j].sid = sid_dup_talloc(p->mem_ctx, &sid_list[i]);
+ if (!sids[j].sid) {
+ SAFE_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(pipes_struct *p,
+ struct lsa_GetUserName *r)
+{
+ const char *username, *domname;
+ struct lsa_String *account_name = NULL;
+ struct lsa_String *authority_name = NULL;
+
+ if (p->server_info->guest) {
+ /*
+ * 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 = p->server_info->sanitized_username;
+ domname = pdb_get_domain(p->server_info->sam_account);
+ }
+
+ account_name = TALLOC_ZERO_P(p->mem_ctx, struct lsa_String);
+ if (!account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ authority_name = TALLOC_ZERO_P(p->mem_ctx, struct lsa_String);
+ if (!authority_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ init_lsa_String(account_name, username);
+ init_lsa_String(authority_name, domname);
+
+ *r->out.account_name = account_name;
+ *r->out.authority_name = authority_name;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_CreateAccount
+ ***************************************************************************/
+
+NTSTATUS _lsa_CreateAccount(pipes_struct *p,
+ struct lsa_CreateAccount *r)
+{
+ struct lsa_info *handle;
+ struct lsa_info *info;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user has enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ * but guessed with rpcclient.
+ */
+ if (!(handle->access & LSA_POLICY_GET_PRIVATE_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check to see if the pipe_user is a Domain Admin since
+ account_pol.tdb was already opened as root, this is all we have */
+
+ if ( !nt_token_check_domain_rid( p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ if ( is_privileged_sid( r->in.sid ) )
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+
+ /* associate the user/group SID with the (unique) handle. */
+
+ if ((info = SMB_MALLOC_P(struct lsa_info)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ info->sid = *r->in.sid;
+ info->access = r->in.access_mask;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.acct_handle, free_lsa_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return privilege_create_account( &info->sid );
+}
+
+
+/***************************************************************************
+ _lsa_OpenAccount
+ ***************************************************************************/
+
+NTSTATUS _lsa_OpenAccount(pipes_struct *p,
+ struct lsa_OpenAccount *r)
+{
+ struct lsa_info *handle;
+ struct lsa_info *info;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user has enough rights */
+
+ /*
+ * I don't know if it's the right one. not documented.
+ * but guessed with rpcclient.
+ */
+ if (!(handle->access & LSA_POLICY_GET_PRIVATE_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* 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
+ /* associate the user/group SID with the (unique) handle. */
+ if ((info = SMB_MALLOC_P(struct lsa_info)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(info);
+ info->sid = *r->in.sid;
+ info->access = r->in.access_mask;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.acct_handle, free_lsa_info, (void *)info))
+ 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(pipes_struct *p,
+ struct lsa_EnumPrivsAccount *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct lsa_info *info=NULL;
+ SE_PRIV mask;
+ PRIVILEGE_SET privileges;
+ struct lsa_PrivilegeSet *priv_set = NULL;
+ struct lsa_LUIDAttribute *luid_attrs = NULL;
+ int i;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if ( !get_privileges_for_sids( &mask, &info->sid, 1 ) )
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ privilege_set_init( &privileges );
+
+ if ( se_priv_to_privilege_set( &privileges, &mask ) ) {
+
+ DEBUG(10,("_lsa_EnumPrivsAccount: %s has %d privileges\n",
+ sid_string_dbg(&info->sid),
+ privileges.count));
+
+ priv_set = TALLOC_ZERO_P(p->mem_ctx, struct lsa_PrivilegeSet);
+ if (!priv_set) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ luid_attrs = TALLOC_ZERO_ARRAY(p->mem_ctx,
+ struct lsa_LUIDAttribute,
+ privileges.count);
+ if (!luid_attrs) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<privileges.count; i++) {
+ luid_attrs[i].luid.low = privileges.set[i].luid.low;
+ luid_attrs[i].luid.high = privileges.set[i].luid.high;
+ luid_attrs[i].attribute = privileges.set[i].attr;
+ }
+
+ priv_set->count = privileges.count;
+ priv_set->unknown = 0;
+ priv_set->set = luid_attrs;
+
+ *r->out.privs = priv_set;
+ } else {
+ status = NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ done:
+ privilege_set_free( &privileges );
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_GetSystemAccessAccount
+ ***************************************************************************/
+
+NTSTATUS _lsa_GetSystemAccessAccount(pipes_struct *p,
+ struct lsa_GetSystemAccessAccount *r)
+{
+ struct lsa_info *info=NULL;
+
+ /* find the connection policy handle. */
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (!lookup_sid(p->mem_ctx, &info->sid, NULL, NULL, NULL))
+ return NT_STATUS_ACCESS_DENIED;
+
+ /*
+ 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 = PR_LOG_ON_LOCALLY | PR_ACCESS_FROM_NETWORK;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ update the systemaccount information
+ ***************************************************************************/
+
+NTSTATUS _lsa_SetSystemAccessAccount(pipes_struct *p,
+ struct lsa_SetSystemAccessAccount *r)
+{
+ struct lsa_info *info=NULL;
+ GROUP_MAP map;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check to see if the pipe_user is a Domain Admin since
+ account_pol.tdb was already opened as root, this is all we have */
+
+ if ( !nt_token_check_domain_rid( p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ if (!pdb_getgrsid(&map, info->sid))
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ return pdb_update_group_mapping_entry(&map);
+}
+
+/***************************************************************************
+ _lsa_AddPrivilegesToAccount
+ For a given SID, add some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_AddPrivilegesToAccount(pipes_struct *p,
+ struct lsa_AddPrivilegesToAccount *r)
+{
+ struct lsa_info *info = NULL;
+ SE_PRIV mask;
+ struct lsa_PrivilegeSet *set = NULL;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check to see if the pipe_user is root or a Domain Admin since
+ account_pol.tdb was already opened as root, this is all we have */
+
+ if ( p->pipe_user.ut.uid != sec_initial_uid()
+ && !nt_token_check_domain_rid( p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS ) )
+ {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ set = r->in.privs;
+ if ( !privilege_set_to_se_priv( &mask, set ) )
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+
+ if ( !grant_privilege( &info->sid, &mask ) ) {
+ DEBUG(3,("_lsa_AddPrivilegesToAccount: grant_privilege(%s) failed!\n",
+ sid_string_dbg(&info->sid) ));
+ DEBUG(3,("Privilege mask:\n"));
+ dump_se_priv( DBGC_ALL, 3, &mask );
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_RemovePrivilegesFromAccount
+ For a given SID, remove some privileges.
+ ***************************************************************************/
+
+NTSTATUS _lsa_RemovePrivilegesFromAccount(pipes_struct *p,
+ struct lsa_RemovePrivilegesFromAccount *r)
+{
+ struct lsa_info *info = NULL;
+ SE_PRIV mask;
+ struct lsa_PrivilegeSet *set = NULL;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check to see if the pipe_user is root or a Domain Admin since
+ account_pol.tdb was already opened as root, this is all we have */
+
+ if ( p->pipe_user.ut.uid != sec_initial_uid()
+ && !nt_token_check_domain_rid( p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS ) )
+ {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ set = r->in.privs;
+
+ if ( !privilege_set_to_se_priv( &mask, set ) )
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+
+ if ( !revoke_privilege( &info->sid, &mask ) ) {
+ DEBUG(3,("_lsa_RemovePrivilegesFromAccount: revoke_privilege(%s) failed!\n",
+ sid_string_dbg(&info->sid) ));
+ DEBUG(3,("Privilege mask:\n"));
+ dump_se_priv( DBGC_ALL, 3, &mask );
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ _lsa_QuerySecurity
+ ***************************************************************************/
+
+NTSTATUS _lsa_QuerySecurity(pipes_struct *p,
+ struct lsa_QuerySecurity *r)
+{
+ struct lsa_info *handle=NULL;
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ NTSTATUS status;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check if the user has enough rights */
+ if (!(handle->access & LSA_POLICY_VIEW_LOCAL_INFORMATION))
+ return NT_STATUS_ACCESS_DENIED;
+
+
+ switch (r->in.sec_info) {
+ case 1:
+ /* SD contains only the owner */
+
+ status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+ if(!NT_STATUS_IS_OK(status))
+ return NT_STATUS_NO_MEMORY;
+
+
+ if((*r->out.sdbuf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ break;
+ case 4:
+ /* SD contains only the ACL */
+
+ status=lsa_get_generic_sd(p->mem_ctx, &psd, &sd_size);
+ if(!NT_STATUS_IS_OK(status))
+ return NT_STATUS_NO_MEMORY;
+
+ if((*r->out.sdbuf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+#if 0 /* AD DC work in ongoing in Samba 4 */
+
+/***************************************************************************
+ ***************************************************************************/
+
+ NTSTATUS _lsa_query_info2(pipes_struct *p, LSA_Q_QUERY_INFO2 *q_u, LSA_R_QUERY_INFO2 *r_u)
+{
+ struct lsa_info *handle;
+ const char *nb_name;
+ char *dns_name = NULL;
+ char *forest_name = NULL;
+ DOM_SID *sid = NULL;
+ struct GUID guid;
+ fstring dnsdomname;
+
+ ZERO_STRUCT(guid);
+ r_u->status = NT_STATUS_OK;
+
+ if (!find_policy_by_hnd(p, &q_u->pol, (void **)(void *)&handle))
+ return NT_STATUS_INVALID_HANDLE;
+
+ switch (q_u->info_class) {
+ case 0x0c:
+ /* 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:
+ nb_name = get_global_sam_name();
+ /* ugly temp hack for these next two */
+
+ /* This should be a 'netbios domain -> DNS domain' mapping */
+ dnsdomname = get_mydnsdomname(p->mem_ctx);
+ if (!dnsdomname || !*dnsdomname) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ strlower_m(dnsdomname);
+
+ dns_name = dnsdomname;
+ forest_name = dnsdomname;
+
+ sid = get_global_sam_sid();
+ secrets_fetch_domain_guid(lp_workgroup(), &guid);
+ break;
+ default:
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ init_dns_dom_info(&r_u->info.dns_dom_info, nb_name, dns_name,
+ forest_name,&guid,sid);
+ break;
+ default:
+ DEBUG(0,("_lsa_query_info2: unknown info level in Lsa Query: %d\n", q_u->info_class));
+ r_u->status = NT_STATUS_INVALID_INFO_CLASS;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(r_u->status)) {
+ r_u->ptr = 0x1;
+ r_u->info_class = q_u->info_class;
+ }
+
+ return r_u->status;
+}
+#endif /* AD DC work in ongoing in Samba 4 */
+
+/***************************************************************************
+ _lsa_AddAccountRights
+ ***************************************************************************/
+
+NTSTATUS _lsa_AddAccountRights(pipes_struct *p,
+ struct lsa_AddAccountRights *r)
+{
+ struct lsa_info *info = NULL;
+ int i = 0;
+ DOM_SID sid;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check to see if the pipe_user is a Domain Admin since
+ account_pol.tdb was already opened as root, this is all we have */
+
+ if ( p->pipe_user.ut.uid != sec_initial_uid()
+ && !nt_token_check_domain_rid( p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS ) )
+ {
+ 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. */
+
+ 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(pipes_struct *p,
+ struct lsa_RemoveAccountRights *r)
+{
+ struct lsa_info *info = NULL;
+ int i = 0;
+ DOM_SID sid;
+ const char *privname = NULL;
+
+ /* find the connection policy handle. */
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* check to see if the pipe_user is a Domain Admin since
+ account_pol.tdb was already opened as root, this is all we have */
+
+ if ( p->pipe_user.ut.uid != sec_initial_uid()
+ && !nt_token_check_domain_rid( p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS ) )
+ {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ 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 i;
+ const char *privname;
+ const char **privname_array = NULL;
+ int num_priv = 0;
+
+ for (i=0; i<privileges->count; i++) {
+
+ privname = luid_to_privilege_name(&privileges->set[i].luid);
+ 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(pipes_struct *p,
+ struct lsa_EnumAccountRights *r)
+{
+ NTSTATUS status;
+ struct lsa_info *info = NULL;
+ DOM_SID sid;
+ PRIVILEGE_SET privileges;
+ SE_PRIV mask;
+
+ /* find the connection policy handle. */
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* 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 );
+
+ if ( !get_privileges_for_sids( &mask, &sid, 1 ) )
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ privilege_set_init( &privileges );
+
+ if ( se_priv_to_privilege_set( &privileges, &mask ) ) {
+
+ DEBUG(10,("_lsa_EnumAccountRights: %s has %d privileges\n",
+ sid_string_dbg(&sid), privileges.count));
+
+ status = init_lsa_right_set(p->mem_ctx, r->out.rights, &privileges);
+ } else {
+ status = NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privilege_set_free( &privileges );
+
+ return status;
+}
+
+/***************************************************************************
+ _lsa_LookupPrivValue
+ ***************************************************************************/
+
+NTSTATUS _lsa_LookupPrivValue(pipes_struct *p,
+ struct lsa_LookupPrivValue *r)
+{
+ struct lsa_info *info = NULL;
+ const char *name = NULL;
+ LUID_ATTR priv_luid;
+ SE_PRIV mask;
+
+ /* find the connection policy handle. */
+
+ if (!find_policy_by_hnd(p, r->in.handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ name = r->in.name->string;
+
+ DEBUG(10,("_lsa_lookup_priv_value: name = %s\n", name));
+
+ if ( !se_priv_from_name( name, &mask ) )
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+
+ priv_luid = get_privilege_luid( &mask );
+
+ r->out.luid->low = priv_luid.luid.low;
+ r->out.luid->high = priv_luid.luid.high;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * 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_Delete(pipes_struct *p, struct lsa_Delete *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetSecObj(pipes_struct *p, struct lsa_SetSecObj *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_ChangePassword(pipes_struct *p, struct lsa_ChangePassword *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetInfoPolicy(pipes_struct *p, struct lsa_SetInfoPolicy *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_ClearAuditLog(pipes_struct *p, struct lsa_ClearAuditLog *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_GetQuotasForAccount(pipes_struct *p, struct lsa_GetQuotasForAccount *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetQuotasForAccount(pipes_struct *p, struct lsa_SetQuotasForAccount *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_QueryTrustedDomainInfo(pipes_struct *p, struct lsa_QueryTrustedDomainInfo *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetInformationTrustedDomain(pipes_struct *p, struct lsa_SetInformationTrustedDomain *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_QuerySecret(pipes_struct *p, struct lsa_QuerySecret *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LookupPrivName(pipes_struct *p, struct lsa_LookupPrivName *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_EnumAccountsWithUserRight(pipes_struct *p, struct lsa_EnumAccountsWithUserRight *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_QueryTrustedDomainInfoBySid(pipes_struct *p, struct lsa_QueryTrustedDomainInfoBySid *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetTrustedDomainInfo(pipes_struct *p, struct lsa_SetTrustedDomainInfo *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_DeleteTrustedDomain(pipes_struct *p, struct lsa_DeleteTrustedDomain *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_StorePrivateData(pipes_struct *p, struct lsa_StorePrivateData *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_RetrievePrivateData(pipes_struct *p, struct lsa_RetrievePrivateData *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_QueryInfoPolicy2(pipes_struct *p, struct lsa_QueryInfoPolicy2 *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetInfoPolicy2(pipes_struct *p, struct lsa_SetInfoPolicy2 *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_QueryTrustedDomainInfoByName(pipes_struct *p, struct lsa_QueryTrustedDomainInfoByName *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetTrustedDomainInfoByName(pipes_struct *p, struct lsa_SetTrustedDomainInfoByName *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_EnumTrustedDomainsEx(pipes_struct *p, struct lsa_EnumTrustedDomainsEx *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CreateTrustedDomainEx(pipes_struct *p, struct lsa_CreateTrustedDomainEx *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CloseTrustedDomainEx(pipes_struct *p, struct lsa_CloseTrustedDomainEx *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_QueryDomainInformationPolicy(pipes_struct *p, struct lsa_QueryDomainInformationPolicy *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_SetDomainInformationPolicy(pipes_struct *p, struct lsa_SetDomainInformationPolicy *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_OpenTrustedDomainByName(pipes_struct *p, struct lsa_OpenTrustedDomainByName *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_TestCall(pipes_struct *p, struct lsa_TestCall *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CreateTrustedDomainEx2(pipes_struct *p, struct lsa_CreateTrustedDomainEx2 *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRWRITE(pipes_struct *p, struct lsa_CREDRWRITE *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRREAD(pipes_struct *p, struct lsa_CREDRREAD *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRENUMERATE(pipes_struct *p, struct lsa_CREDRENUMERATE *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRWRITEDOMAINCREDENTIALS(pipes_struct *p, struct lsa_CREDRWRITEDOMAINCREDENTIALS *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRREADDOMAINCREDENTIALS(pipes_struct *p, struct lsa_CREDRREADDOMAINCREDENTIALS *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRDELETE(pipes_struct *p, struct lsa_CREDRDELETE *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRGETTARGETINFO(pipes_struct *p, struct lsa_CREDRGETTARGETINFO *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRPROFILELOADED(pipes_struct *p, struct lsa_CREDRPROFILELOADED *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRGETSESSIONTYPES(pipes_struct *p, struct lsa_CREDRGETSESSIONTYPES *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARREGISTERAUDITEVENT(pipes_struct *p, struct lsa_LSARREGISTERAUDITEVENT *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARGENAUDITEVENT(pipes_struct *p, struct lsa_LSARGENAUDITEVENT *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARUNREGISTERAUDITEVENT(pipes_struct *p, struct lsa_LSARUNREGISTERAUDITEVENT *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_lsaRQueryForestTrustInformation(pipes_struct *p, struct lsa_lsaRQueryForestTrustInformation *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARSETFORESTTRUSTINFORMATION(pipes_struct *p, struct lsa_LSARSETFORESTTRUSTINFORMATION *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_CREDRRENAME(pipes_struct *p, struct lsa_CREDRRENAME *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSAROPENPOLICYSCE(pipes_struct *p, struct lsa_LSAROPENPOLICYSCE *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARADTREGISTERSECURITYEVENTSOURCE(pipes_struct *p, struct lsa_LSARADTREGISTERSECURITYEVENTSOURCE *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE(pipes_struct *p, struct lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _lsa_LSARADTREPORTSECURITYEVENT(pipes_struct *p, struct lsa_LSARADTREPORTSECURITYEVENT *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c
new file mode 100644
index 0000000000..4e211cfb81
--- /dev/null
+++ b/source3/rpc_server/srv_netlog_nt.c
@@ -0,0 +1,1414 @@
+/*
+ * 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.
+ *
+ * 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"
+
+extern userdom_struct current_user_info;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*************************************************************************
+ init_net_r_req_chal:
+ *************************************************************************/
+
+static void init_net_r_req_chal(struct netr_Credential *r,
+ struct netr_Credential *srv_chal)
+{
+ DEBUG(6,("init_net_r_req_chal: %d\n", __LINE__));
+
+ memcpy(r->data, srv_chal->data, sizeof(r->data));
+}
+
+/*******************************************************************
+ Inits a netr_NETLOGON_INFO_1 structure.
+********************************************************************/
+
+static void init_netlogon_info1(struct netr_NETLOGON_INFO_1 *r,
+ uint32_t flags,
+ uint32_t pdc_connection_status)
+{
+ r->flags = flags;
+ r->pdc_connection_status = pdc_connection_status;
+}
+
+/*******************************************************************
+ Inits a netr_NETLOGON_INFO_2 structure.
+********************************************************************/
+
+static void init_netlogon_info2(struct netr_NETLOGON_INFO_2 *r,
+ uint32_t flags,
+ uint32_t pdc_connection_status,
+ const char *trusted_dc_name,
+ uint32_t tc_connection_status)
+{
+ r->flags = flags;
+ r->pdc_connection_status = pdc_connection_status;
+ r->trusted_dc_name = trusted_dc_name;
+ r->tc_connection_status = tc_connection_status;
+}
+
+/*******************************************************************
+ Inits a netr_NETLOGON_INFO_3 structure.
+********************************************************************/
+
+static void init_netlogon_info3(struct netr_NETLOGON_INFO_3 *r,
+ uint32_t flags,
+ uint32_t logon_attempts)
+{
+ r->flags = flags;
+ r->logon_attempts = logon_attempts;
+}
+
+/*************************************************************************
+ _netr_LogonControl
+ *************************************************************************/
+
+WERROR _netr_LogonControl(pipes_struct *p,
+ struct netr_LogonControl *r)
+{
+ struct netr_NETLOGON_INFO_1 *info1;
+ uint32_t flags = 0x0;
+ uint32_t pdc_connection_status = W_ERROR_V(WERR_OK);
+
+ /* Setup the Logon Control response */
+
+ switch (r->in.level) {
+ case 1:
+ info1 = TALLOC_ZERO_P(p->mem_ctx, struct netr_NETLOGON_INFO_1);
+ if (!info1) {
+ return WERR_NOMEM;
+ }
+ init_netlogon_info1(info1,
+ flags,
+ pdc_connection_status);
+ r->out.info->info1 = info1;
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+Send a message to smbd to do a sam synchronisation
+**************************************************************************/
+
+static void send_sync_message(void)
+{
+ DEBUG(3, ("sending sam synchronisation message\n"));
+ message_send_all(smbd_messaging_context(), MSG_SMB_SAM_SYNC, NULL, 0,
+ NULL);
+}
+
+/*************************************************************************
+ _netr_LogonControl2
+ *************************************************************************/
+
+WERROR _netr_LogonControl2(pipes_struct *p,
+ struct netr_LogonControl2 *r)
+{
+ uint32 flags = 0x0;
+ uint32 pdc_connection_status = 0x0;
+ uint32 logon_attempts = 0x0;
+ uint32 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;
+
+ tc_status = W_ERROR_V(WERR_NO_SUCH_DOMAIN);
+
+ switch (r->in.function_code) {
+ case NETLOGON_CONTROL_TC_QUERY:
+ domain = r->in.data->domain;
+
+ if ( !is_trusted_domain( domain ) )
+ break;
+
+ if ( !get_dc_name( domain, NULL, dc_name2, &dc_ss ) ) {
+ tc_status = W_ERROR_V(WERR_NO_LOGON_SERVERS);
+ break;
+ }
+
+ dc_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dc_name2);
+ if (!dc_name) {
+ return WERR_NOMEM;
+ }
+
+ tc_status = W_ERROR_V(WERR_OK);
+
+ break;
+
+ case NETLOGON_CONTROL_REDISCOVER:
+ domain = r->in.data->domain;
+
+ if ( !is_trusted_domain( domain ) )
+ break;
+
+ if ( !get_dc_name( domain, NULL, dc_name2, &dc_ss ) ) {
+ tc_status = W_ERROR_V(WERR_NO_LOGON_SERVERS);
+ break;
+ }
+
+ dc_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dc_name2);
+ if (!dc_name) {
+ return WERR_NOMEM;
+ }
+
+ tc_status = W_ERROR_V(WERR_OK);
+
+ break;
+
+ default:
+ /* no idea what this should be */
+ DEBUG(0,("_netr_LogonControl2: unimplemented function level [%d]\n",
+ r->in.function_code));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ /* prepare the response */
+
+ switch (r->in.level) {
+ case 1:
+ info1 = TALLOC_ZERO_P(p->mem_ctx, struct netr_NETLOGON_INFO_1);
+ W_ERROR_HAVE_NO_MEMORY(info1);
+
+ init_netlogon_info1(info1,
+ flags,
+ pdc_connection_status);
+ r->out.query->info1 = info1;
+ break;
+ case 2:
+ info2 = TALLOC_ZERO_P(p->mem_ctx, struct netr_NETLOGON_INFO_2);
+ W_ERROR_HAVE_NO_MEMORY(info2);
+
+ init_netlogon_info2(info2,
+ flags,
+ pdc_connection_status,
+ dc_name,
+ tc_status);
+ r->out.query->info2 = info2;
+ break;
+ case 3:
+ info3 = TALLOC_ZERO_P(p->mem_ctx, struct netr_NETLOGON_INFO_3);
+ W_ERROR_HAVE_NO_MEMORY(info3);
+
+ init_netlogon_info3(info3,
+ flags,
+ logon_attempts);
+ r->out.query->info3 = info3;
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC) {
+ send_sync_message();
+ }
+
+ return WERR_OK;
+}
+
+/*************************************************************************
+ _netr_NetrEnumerateTrustedDomains
+ *************************************************************************/
+
+WERROR _netr_NetrEnumerateTrustedDomains(pipes_struct *p,
+ struct netr_NetrEnumerateTrustedDomains *r)
+{
+ struct netr_Blob trusted_domains_blob;
+ DATA_BLOB blob;
+
+ DEBUG(6,("_netr_NetrEnumerateTrustedDomains: %d\n", __LINE__));
+
+ /* set up the Trusted Domain List response */
+
+ blob = data_blob_talloc_zero(p->mem_ctx, 2);
+ trusted_domains_blob.data = blob.data;
+ trusted_domains_blob.length = blob.length;
+
+ DEBUG(6,("_netr_NetrEnumerateTrustedDomains: %d\n", __LINE__));
+
+ *r->out.trusted_domains_blob = trusted_domains_blob;
+
+ return WERR_OK;
+}
+
+/******************************************************************
+ gets a machine password entry. checks access rights of the host.
+ ******************************************************************/
+
+static NTSTATUS get_md4pw(char *md4pw, const char *mach_acct, uint16 sec_chan_type)
+{
+ struct samu *sampass = NULL;
+ const uint8 *pass;
+ bool ret;
+ uint32 acct_ctrl;
+
+#if 0
+ char addr[INET6_ADDRSTRLEN];
+
+ /*
+ * Currently this code is redundent 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(),
+ client_name(get_client_fd()),
+ client_addr(get_client_fd(),addr,sizeof(addr)))) {
+ DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct));
+ return False;
+ }
+#endif /* 0 */
+
+ if ( !(sampass = samu_new( NULL )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* JRA. This is ok as it is only used for generating the challenge. */
+ become_root();
+ ret = pdb_getsampwnam(sampass, mach_acct);
+ unbecome_root();
+
+ if (!ret) {
+ DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(0,("get_md4pw: Workstation %s: account is disabled\n", mach_acct));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ 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));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+
+ 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));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ 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));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ 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));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ((pass = pdb_get_nt_passwd(sampass)) == NULL) {
+ DEBUG(0,("get_md4pw: Workstation %s: account does not have a password\n", mach_acct));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ memcpy(md4pw, pass, 16);
+ dump_data(5, (uint8 *)md4pw, 16);
+
+ TALLOC_FREE(sampass);
+
+ return NT_STATUS_OK;
+
+
+}
+
+/*************************************************************************
+ _netr_ServerReqChallenge
+ *************************************************************************/
+
+NTSTATUS _netr_ServerReqChallenge(pipes_struct *p,
+ struct netr_ServerReqChallenge *r)
+{
+ if (!p->dc) {
+ p->dc = TALLOC_ZERO_P(p, struct dcinfo);
+ if (!p->dc) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ DEBUG(10,("_netr_ServerReqChallenge: new challenge requested. Clearing old state.\n"));
+ ZERO_STRUCTP(p->dc);
+ }
+
+ fstrcpy(p->dc->remote_machine, r->in.computer_name);
+
+ /* Save the client challenge to the server. */
+ memcpy(p->dc->clnt_chal.data, r->in.credentials->data,
+ sizeof(r->in.credentials->data));
+
+ /* Create a server challenge for the client */
+ /* Set this to a random value. */
+ generate_random_buffer(p->dc->srv_chal.data, 8);
+
+ /* set up the LSA REQUEST CHALLENGE response */
+ init_net_r_req_chal(r->out.return_credentials, &p->dc->srv_chal);
+
+ p->dc->challenge_sent = True;
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ _netr_ServerAuthenticate
+ Create the initial credentials.
+ *************************************************************************/
+
+NTSTATUS _netr_ServerAuthenticate(pipes_struct *p,
+ struct netr_ServerAuthenticate *r)
+{
+ NTSTATUS status;
+ struct netr_Credential srv_chal_out;
+
+ if (!p->dc || !p->dc->challenge_sent) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = get_md4pw((char *)p->dc->mach_pw,
+ r->in.account_name,
+ r->in.secure_channel_type);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("_netr_ServerAuthenticate: get_md4pw failed. Failed to "
+ "get password for machine account %s "
+ "from client %s: %s\n",
+ r->in.account_name,
+ r->in.computer_name,
+ nt_errstr(status) ));
+ /* always return NT_STATUS_ACCESS_DENIED */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* From the client / server challenges and md4 password, generate sess key */
+ creds_server_init(0, /* No neg flags. */
+ p->dc,
+ &p->dc->clnt_chal, /* Stored client chal. */
+ &p->dc->srv_chal, /* Stored server chal. */
+ p->dc->mach_pw,
+ &srv_chal_out);
+
+ /* Check client credentials are valid. */
+ if (!netlogon_creds_server_check(p->dc, r->in.credentials)) {
+ DEBUG(0,("_netr_ServerAuthenticate: netlogon_creds_server_check failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ r->in.computer_name,
+ r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ fstrcpy(p->dc->mach_acct, r->in.account_name);
+ fstrcpy(p->dc->remote_machine, r->in.computer_name);
+ p->dc->authenticated = True;
+
+ /* set up the LSA AUTH response */
+ /* Return the server credentials. */
+
+ memcpy(r->out.return_credentials->data, &srv_chal_out.data,
+ sizeof(r->out.return_credentials->data));
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ _netr_ServerAuthenticate2
+ *************************************************************************/
+
+NTSTATUS _netr_ServerAuthenticate2(pipes_struct *p,
+ struct netr_ServerAuthenticate2 *r)
+{
+ NTSTATUS status;
+ uint32_t srv_flgs;
+ struct netr_Credential srv_chal_out;
+
+ /* We use this as the key to store the creds: */
+ /* r->in.computer_name */
+
+ if (!p->dc || !p->dc->challenge_sent) {
+ DEBUG(0,("_netr_ServerAuthenticate2: no challenge sent to client %s\n",
+ r->in.computer_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ( (lp_server_schannel() == true) &&
+ ((*r->in.negotiate_flags & NETLOGON_NEG_SCHANNEL) == 0) ) {
+
+ /* schannel must be used, but client did not offer it. */
+ DEBUG(0,("_netr_ServerAuthenticate2: schannel required but client failed "
+ "to offer it. Client was %s\n",
+ r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = get_md4pw((char *)p->dc->mach_pw,
+ r->in.account_name,
+ r->in.secure_channel_type);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("_netr_ServerAuthenticate2: failed to get machine password for "
+ "account %s: %s\n",
+ r->in.account_name, nt_errstr(status) ));
+ /* always return NT_STATUS_ACCESS_DENIED */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* From the client / server challenges and md4 password, generate sess key */
+ creds_server_init(*r->in.negotiate_flags,
+ p->dc,
+ &p->dc->clnt_chal, /* Stored client chal. */
+ &p->dc->srv_chal, /* Stored server chal. */
+ p->dc->mach_pw,
+ &srv_chal_out);
+
+ /* Check client credentials are valid. */
+ if (!netlogon_creds_server_check(p->dc, r->in.credentials)) {
+ DEBUG(0,("_netr_ServerAuthenticate2: netlogon_creds_server_check failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ r->in.computer_name,
+ r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* 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;
+
+ if (lp_server_schannel() != false) {
+ srv_flgs |= NETLOGON_NEG_SCHANNEL;
+ }
+
+ /* set up the LSA AUTH 2 response */
+ memcpy(r->out.return_credentials->data, &srv_chal_out.data,
+ sizeof(r->out.return_credentials->data));
+ *r->out.negotiate_flags = srv_flgs;
+
+ fstrcpy(p->dc->mach_acct, r->in.account_name);
+ fstrcpy(p->dc->remote_machine, r->in.computer_name);
+ fstrcpy(p->dc->domain, lp_workgroup() );
+
+ p->dc->authenticated = True;
+
+ /* Store off the state so we can continue after client disconnect. */
+ become_root();
+ secrets_store_schannel_session_info(p->mem_ctx,
+ r->in.computer_name,
+ p->dc);
+ unbecome_root();
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ _netr_ServerPasswordSet
+ *************************************************************************/
+
+NTSTATUS _netr_ServerPasswordSet(pipes_struct *p,
+ struct netr_ServerPasswordSet *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ fstring remote_machine;
+ struct samu *sampass=NULL;
+ bool ret = False;
+ unsigned char pwd[16];
+ int i;
+ uint32 acct_ctrl;
+ struct netr_Authenticator cred_out;
+ const uchar *old_pw;
+
+ DEBUG(5,("_netr_ServerPasswordSet: %d\n", __LINE__));
+
+ /* We need the remote machine name for the creds lookup. */
+ fstrcpy(remote_machine, r->in.computer_name);
+
+ if ( (lp_server_schannel() == True) && (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) ) {
+ /* 'server schannel = yes' should enforce use of
+ schannel, the client did offer it in auth2, but
+ obviously did not use it. */
+ DEBUG(0,("_netr_ServerPasswordSet: client %s not using schannel for netlogon\n",
+ remote_machine ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!p->dc) {
+ /* Restore the saved state of the netlogon creds. */
+ become_root();
+ ret = secrets_restore_schannel_session_info(p, remote_machine,
+ &p->dc);
+ unbecome_root();
+ if (!ret) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (!p->dc || !p->dc->authenticated) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(3,("_netr_ServerPasswordSet: Server Password Set by remote machine:[%s] on account [%s]\n",
+ remote_machine, p->dc->mach_acct));
+
+ /* Step the creds chain forward. */
+ if (!netlogon_creds_server_step(p->dc, r->in.credential, &cred_out)) {
+ DEBUG(2,("_netr_ServerPasswordSet: netlogon_creds_server_step failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ remote_machine, p->dc->mach_acct ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* We must store the creds state after an update. */
+ sampass = samu_new( NULL );
+ if (!sampass) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ secrets_store_schannel_session_info(p, remote_machine, p->dc);
+ ret = pdb_getsampwnam(sampass, p->dc->mach_acct);
+ unbecome_root();
+
+ if (!ret) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Ensure the account exists and is a machine account. */
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+
+ if (!(acct_ctrl & ACB_WSTRUST ||
+ acct_ctrl & ACB_SVRTRUST ||
+ acct_ctrl & ACB_DOMTRUST)) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ /* Woah - what does this to to the credential chain ? JRA */
+ cred_hash3(pwd, r->in.new_password->hash, p->dc->sess_key, 0);
+
+ DEBUG(100,("_netr_ServerPasswordSet: new given value was :\n"));
+ for(i = 0; i < sizeof(pwd); i++)
+ DEBUG(100,("%02X ", pwd[i]));
+ DEBUG(100,("\n"));
+
+ old_pw = pdb_get_nt_passwd(sampass);
+
+ if (old_pw && memcmp(pwd, old_pw, 16) == 0) {
+ /* Avoid backend modificiations and other fun if the
+ client changed the password to the *same thing* */
+
+ ret = True;
+ } else {
+
+ /* LM password should be NULL for machines */
+ if (!pdb_set_lanman_passwd(sampass, NULL, PDB_CHANGED)) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_nt_passwd(sampass, pwd, PDB_CHANGED)) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_pass_last_set_time(sampass, time(NULL), PDB_CHANGED)) {
+ TALLOC_FREE(sampass);
+ /* Not quite sure what this one qualifies as, but this will do */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ become_root();
+ status = pdb_update_sam_account(sampass);
+ unbecome_root();
+ }
+
+ /* set up the LSA Server Password Set response */
+
+ memcpy(r->out.return_authenticator, &cred_out,
+ sizeof(r->out.return_authenticator));
+
+ TALLOC_FREE(sampass);
+ return status;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogoff
+ *************************************************************************/
+
+NTSTATUS _netr_LogonSamLogoff(pipes_struct *p,
+ struct netr_LogonSamLogoff *r)
+{
+ if ( (lp_server_schannel() == True) && (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) ) {
+ /* 'server schannel = yes' should enforce use of
+ schannel, the client did offer it in auth2, but
+ obviously did not use it. */
+ DEBUG(0,("_netr_LogonSamLogoff: client %s not using schannel for netlogon\n",
+ get_remote_machine_name() ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+
+ /* Using the remote machine name for the creds store: */
+ /* r->in.computer_name */
+
+ if (!p->dc) {
+ /* Restore the saved state of the netlogon creds. */
+ bool ret;
+
+ become_root();
+ ret = secrets_restore_schannel_session_info(
+ p, r->in.computer_name, &p->dc);
+ unbecome_root();
+ if (!ret) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (!p->dc || !p->dc->authenticated) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!netlogon_creds_server_step(p->dc, r->in.credential, r->out.return_authenticator)) {
+ DEBUG(2,("_netr_LogonSamLogoff: netlogon_creds_server_step failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ r->in.computer_name, p->dc->mach_acct ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* We must store the creds state after an update. */
+ become_root();
+ secrets_store_schannel_session_info(p, r->in.computer_name, p->dc);
+ unbecome_root();
+
+ return NT_STATUS_OK;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogon
+ *************************************************************************/
+
+NTSTATUS _netr_LogonSamLogon(pipes_struct *p,
+ struct netr_LogonSamLogon *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct netr_SamInfo3 *sam3 = NULL;
+ union netr_LogonInfo *logon = r->in.logon;
+ fstring nt_username, nt_domain, nt_workstation;
+ auth_usersupplied_info *user_info = NULL;
+ auth_serversupplied_info *server_info = NULL;
+ struct auth_context *auth_context = NULL;
+ uint8_t pipe_session_key[16];
+ bool process_creds = true;
+
+ switch (p->hdr_req.opnum) {
+ case NDR_NETR_LOGONSAMLOGON:
+ process_creds = true;
+ break;
+ case NDR_NETR_LOGONSAMLOGONEX:
+ default:
+ process_creds = false;
+ }
+
+ if ( (lp_server_schannel() == True) && (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) ) {
+ /* 'server schannel = yes' should enforce use of
+ schannel, the client did offer it in auth2, but
+ obviously did not use it. */
+ DEBUG(0,("_netr_LogonSamLogon: client %s not using schannel for netlogon\n",
+ get_remote_machine_name() ));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sam3 = TALLOC_ZERO_P(p->mem_ctx, struct netr_SamInfo3);
+ if (!sam3) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* store the user information, if there is any. */
+ r->out.validation->sam3 = sam3;
+ *r->out.authoritative = true; /* authoritative response */
+ if (r->in.validation_level != 2 && r->in.validation_level != 3) {
+ DEBUG(0,("_netr_LogonSamLogon: bad validation_level value %d.\n",
+ (int)r->in.validation_level));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (process_creds) {
+ fstring remote_machine;
+
+ /* Get the remote machine name for the creds store. */
+ /* Note this is the remote machine this request is coming from (member server),
+ not neccessarily the workstation name the user is logging onto.
+ */
+
+ fstrcpy(remote_machine, r->in.computer_name);
+
+ if (!p->dc) {
+ /* Restore the saved state of the netlogon creds. */
+ bool ret;
+
+ become_root();
+ ret = secrets_restore_schannel_session_info(
+ p, remote_machine, &p->dc);
+ unbecome_root();
+ if (!ret) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (!p->dc || !p->dc->authenticated) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* checks and updates credentials. creates reply credentials */
+ if (!netlogon_creds_server_step(p->dc, r->in.credential, r->out.return_authenticator)) {
+ DEBUG(2,("_netr_LogonSamLogon: creds_server_step failed. Rejecting auth "
+ "request from client %s machine account %s\n",
+ remote_machine, p->dc->mach_acct ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* We must store the creds state after an update. */
+ become_root();
+ secrets_store_schannel_session_info(p, remote_machine, p->dc);
+ unbecome_root();
+ }
+
+ switch (r->in.logon_level) {
+ case INTERACTIVE_LOGON_TYPE:
+ fstrcpy(nt_username,
+ logon->password->identity_info.account_name.string);
+ fstrcpy(nt_domain,
+ logon->password->identity_info.domain_name.string);
+ fstrcpy(nt_workstation,
+ logon->password->identity_info.workstation.string);
+
+ DEBUG(3,("SAM Logon (Interactive). Domain:[%s]. ", lp_workgroup()));
+ break;
+ case NET_LOGON_TYPE:
+ fstrcpy(nt_username,
+ logon->network->identity_info.account_name.string);
+ fstrcpy(nt_domain,
+ logon->network->identity_info.domain_name.string);
+ fstrcpy(nt_workstation,
+ 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));
+ fstrcpy(current_user_info.smb_name, nt_username);
+ sub_set_smb_name(nt_username);
+
+ DEBUG(5,("Attempting validation level %d for unmapped username %s.\n",
+ r->in.validation_level, nt_username));
+
+ status = NT_STATUS_OK;
+
+ switch (r->in.logon_level) {
+ case NET_LOGON_TYPE:
+ {
+ const char *wksname = nt_workstation;
+
+ status = make_auth_context_fixed(&auth_context,
+ logon->network->challenge);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* 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 authenticaion */
+ if (!make_user_info_netlogon_network(&user_info,
+ nt_username, nt_domain,
+ wksname,
+ 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;
+ }
+ break;
+ }
+ case INTERACTIVE_LOGON_TYPE:
+ /* '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 */
+ {
+ const uint8 *chal;
+
+ if (!NT_STATUS_IS_OK(status = make_auth_context_subsystem(&auth_context))) {
+ return status;
+ }
+
+ chal = auth_context->get_ntlm_challenge(auth_context);
+
+ if (!make_user_info_netlogon_interactive(&user_info,
+ nt_username, nt_domain,
+ nt_workstation,
+ logon->password->identity_info.parameter_control,
+ chal,
+ logon->password->lmpassword.hash,
+ logon->password->ntpassword.hash,
+ p->dc->sess_key)) {
+ 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_context->check_ntlm_password(auth_context,
+ user_info, &server_info);
+ }
+
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+
+ DEBUG(5,("_netr_LogonSamLogon: check_password returned status %s\n",
+ nt_errstr(status)));
+
+ /* Check account and password */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* If we don't know what this domain is, we need to
+ indicate that we are not authoritative. This
+ allows the client to decide if it needs to try
+ a local user. Fix by jpjanosi@us.ibm.com, #2976 */
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+ && !strequal(nt_domain, get_global_sam_name())
+ && !is_trusted_domain(nt_domain) )
+ *r->out.authoritative = false; /* We are not authoritative */
+
+ TALLOC_FREE(server_info);
+ return status;
+ }
+
+ if (server_info->guest) {
+ /* We don't like guest domain logons... */
+ DEBUG(5,("_netr_LogonSamLogon: Attempted domain logon as GUEST "
+ "denied.\n"));
+ TALLOC_FREE(server_info);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ /* 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. */
+
+ if (process_creds) {
+ /* Get the pipe session key from the creds. */
+ memcpy(pipe_session_key, p->dc->sess_key, 16);
+ } else {
+ /* Get the pipe session key from the schannel. */
+ if ((p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL)
+ || (p->auth.a_u.schannel_auth == NULL)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ memcpy(pipe_session_key, p->auth.a_u.schannel_auth->sess_key, 16);
+ }
+
+ status = serverinfo_to_SamInfo3(server_info, pipe_session_key, sam3);
+ TALLOC_FREE(server_info);
+ return status;
+}
+
+/*************************************************************************
+ _netr_LogonSamLogonEx
+ - no credential chaining. Map into net sam logon.
+ *************************************************************************/
+
+NTSTATUS _netr_LogonSamLogonEx(pipes_struct *p,
+ struct netr_LogonSamLogonEx *r)
+{
+ struct netr_LogonSamLogon q;
+
+ /* Only allow this if the pipe is protected. */
+ if (p->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) {
+ DEBUG(0,("_net_sam_logon_ex: client %s not using schannel for netlogon\n",
+ get_remote_machine_name() ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ q.in.server_name = r->in.server_name;
+ q.in.computer_name = r->in.computer_name;
+ q.in.logon_level = r->in.logon_level;
+ q.in.logon = r->in.logon;
+ q.in.validation_level = r->in.validation_level;
+ /* we do not handle the flags */
+ /* = r->in.flags; */
+
+ q.out.validation = r->out.validation;
+ q.out.authoritative = r->out.authoritative;
+ /* we do not handle the flags */
+ /* = r->out.flags; */
+
+ return _netr_LogonSamLogon(p, &q);
+}
+
+/*************************************************************************
+ _ds_enum_dom_trusts
+ *************************************************************************/
+#if 0 /* JERRY -- not correct */
+ NTSTATUS _ds_enum_dom_trusts(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(pipes_struct *p,
+ struct netr_LogonUasLogon *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_LogonUasLogoff(pipes_struct *p,
+ struct netr_LogonUasLogoff *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseDeltas(pipes_struct *p,
+ struct netr_DatabaseDeltas *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseSync(pipes_struct *p,
+ struct netr_DatabaseSync *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_AccountDeltas(pipes_struct *p,
+ struct netr_AccountDeltas *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_AccountSync(pipes_struct *p,
+ struct netr_AccountSync *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_GetDcName(pipes_struct *p,
+ struct netr_GetDcName *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_GetAnyDCName(pipes_struct *p,
+ struct netr_GetAnyDCName *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseSync2(pipes_struct *p,
+ struct netr_DatabaseSync2 *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_DatabaseRedo(pipes_struct *p,
+ struct netr_DatabaseRedo *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_LogonControl2Ex(pipes_struct *p,
+ struct netr_LogonControl2Ex *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetDCName(pipes_struct *p,
+ struct netr_DsRGetDCName *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONDUMMYROUTINE1(pipes_struct *p,
+ struct netr_NETRLOGONDUMMYROUTINE1 *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONSETSERVICEBITS(pipes_struct *p,
+ struct netr_NETRLOGONSETSERVICEBITS *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_LogonGetTrustRid(pipes_struct *p,
+ struct netr_LogonGetTrustRid *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONCOMPUTESERVERDIGEST(pipes_struct *p,
+ struct netr_NETRLOGONCOMPUTESERVERDIGEST *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONCOMPUTECLIENTDIGEST(pipes_struct *p,
+ struct netr_NETRLOGONCOMPUTECLIENTDIGEST *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_ServerAuthenticate3(pipes_struct *p,
+ struct netr_ServerAuthenticate3 *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetDCNameEx(pipes_struct *p,
+ struct netr_DsRGetDCNameEx *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetSiteName(pipes_struct *p,
+ struct netr_DsRGetSiteName *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_LogonGetDomainInfo(pipes_struct *p,
+ struct netr_LogonGetDomainInfo *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_ServerPasswordSet2(pipes_struct *p,
+ struct netr_ServerPasswordSet2 *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_ServerPasswordGet(pipes_struct *p,
+ struct netr_ServerPasswordGet *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONSENDTOSAM(pipes_struct *p,
+ struct netr_NETRLOGONSENDTOSAM *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRAddressToSitenamesW(pipes_struct *p,
+ struct netr_DsRAddressToSitenamesW *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetDCNameEx2(pipes_struct *p,
+ struct netr_DsRGetDCNameEx2 *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN(pipes_struct *p,
+ struct netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NetrEnumerateTrustedDomainsEx(pipes_struct *p,
+ struct netr_NetrEnumerateTrustedDomainsEx *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRAddressToSitenamesExW(pipes_struct *p,
+ struct netr_DsRAddressToSitenamesExW *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsrGetDcSiteCoverageW(pipes_struct *p,
+ struct netr_DsrGetDcSiteCoverageW *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsrEnumerateDomainTrusts(pipes_struct *p,
+ struct netr_DsrEnumerateDomainTrusts *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsrDeregisterDNSHostRecords(pipes_struct *p,
+ struct netr_DsrDeregisterDNSHostRecords *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_ServerTrustPasswordsGet(pipes_struct *p,
+ struct netr_ServerTrustPasswordsGet *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_DsRGetForestTrustInformation(pipes_struct *p,
+ struct netr_DsRGetForestTrustInformation *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_GetForestTrustInformation(pipes_struct *p,
+ struct netr_GetForestTrustInformation *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _netr_LogonSamLogonWithFlags(pipes_struct *p,
+ struct netr_LogonSamLogonWithFlags *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _netr_NETRSERVERGETTRUSTINFO(pipes_struct *p,
+ struct netr_NETRSERVERGETTRUSTINFO *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
diff --git a/source3/rpc_server/srv_ntsvcs.c b/source3/rpc_server/srv_ntsvcs.c
new file mode 100644
index 0000000000..100d577010
--- /dev/null
+++ b/source3/rpc_server/srv_ntsvcs.c
@@ -0,0 +1,163 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool proxy_ntsvcs_call(pipes_struct *p, uint8_t opnum)
+{
+ struct api_struct *fns;
+ int n_fns;
+
+ ntsvcs_get_pipe_fns(&fns, &n_fns);
+
+ if (opnum >= n_fns) {
+ return false;
+ }
+
+ if (fns[opnum].opnum != opnum) {
+ smb_panic("NTSVCS function table not sorted");
+ }
+
+ return fns[opnum].fn(p);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_get_version(pipes_struct *p)
+{
+ return proxy_ntsvcs_call(p, NDR_PNP_GETVERSION);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_get_device_list_size(pipes_struct *p)
+{
+ return proxy_ntsvcs_call(p, NDR_PNP_GETDEVICELISTSIZE);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_get_device_list(pipes_struct *p)
+{
+ NTSVCS_Q_GET_DEVICE_LIST q_u;
+ NTSVCS_R_GET_DEVICE_LIST r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!ntsvcs_io_q_get_device_list("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _ntsvcs_get_device_list(p, &q_u, &r_u);
+
+ if(!ntsvcs_io_r_get_device_list("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_validate_device_instance(pipes_struct *p)
+{
+ return proxy_ntsvcs_call(p, NDR_PNP_VALIDATEDEVICEINSTANCE);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_get_device_reg_property(pipes_struct *p)
+{
+ NTSVCS_Q_GET_DEVICE_REG_PROPERTY q_u;
+ NTSVCS_R_GET_DEVICE_REG_PROPERTY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!ntsvcs_io_q_get_device_reg_property("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _ntsvcs_get_device_reg_property(p, &q_u, &r_u);
+
+ if(!ntsvcs_io_r_get_device_reg_property("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_get_hw_profile_info(pipes_struct *p)
+{
+ return proxy_ntsvcs_call(p, NDR_PNP_GETHWPROFINFO);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_ntsvcs_hw_profile_flags(pipes_struct *p)
+{
+ return proxy_ntsvcs_call(p, NDR_PNP_HWPROFFLAGS);
+}
+
+/*******************************************************************
+ \PIPE\svcctl commands
+ ********************************************************************/
+
+static struct api_struct api_ntsvcs_cmds[] =
+{
+ { "NTSVCS_GET_VERSION" , NTSVCS_GET_VERSION , api_ntsvcs_get_version },
+ { "NTSVCS_GET_DEVICE_LIST_SIZE" , NTSVCS_GET_DEVICE_LIST_SIZE , api_ntsvcs_get_device_list_size },
+ { "NTSVCS_GET_DEVICE_LIST" , NTSVCS_GET_DEVICE_LIST , api_ntsvcs_get_device_list },
+ { "NTSVCS_VALIDATE_DEVICE_INSTANCE" , NTSVCS_VALIDATE_DEVICE_INSTANCE , api_ntsvcs_validate_device_instance },
+ { "NTSVCS_GET_DEVICE_REG_PROPERTY" , NTSVCS_GET_DEVICE_REG_PROPERTY , api_ntsvcs_get_device_reg_property },
+ { "NTSVCS_GET_HW_PROFILE_INFO" , NTSVCS_GET_HW_PROFILE_INFO , api_ntsvcs_get_hw_profile_info },
+ { "NTSVCS_HW_PROFILE_FLAGS" , NTSVCS_HW_PROFILE_FLAGS , api_ntsvcs_hw_profile_flags }
+};
+
+
+void ntsvcs2_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_ntsvcs_cmds;
+ *n_fns = sizeof(api_ntsvcs_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_ntsvcs2_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ "ntsvcs", "ntsvcs",
+ &ndr_table_ntsvcs.syntax_id,
+ api_ntsvcs_cmds,
+ sizeof(api_ntsvcs_cmds) / sizeof(struct api_struct));
+}
diff --git a/source3/rpc_server/srv_ntsvcs_nt.c b/source3/rpc_server/srv_ntsvcs_nt.c
new file mode 100644
index 0000000000..268da52896
--- /dev/null
+++ b/source3/rpc_server/srv_ntsvcs_nt.c
@@ -0,0 +1,778 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ *
+ * Copyright (C) Gerald (Jerry) Carter 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"
+
+#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(pipes_struct *p,
+ struct PNP_GetVersion *r)
+{
+ *r->out.version = 0x0400; /* no idea what this means */
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_GetDeviceListSize(pipes_struct *p,
+ struct PNP_GetDeviceListSize *r)
+{
+ char *devicepath;
+
+ if (!r->in.devicename) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!(devicepath = get_device_path(p->mem_ctx, r->in.devicename))) {
+ return WERR_NOMEM;
+ }
+
+ *r->out.size = strlen(devicepath) + 2;
+
+ TALLOC_FREE(devicepath);
+
+ return WERR_OK;
+}
+
+
+/********************************************************************
+********************************************************************/
+
+WERROR _ntsvcs_get_device_list( pipes_struct *p, NTSVCS_Q_GET_DEVICE_LIST *q_u, NTSVCS_R_GET_DEVICE_LIST *r_u )
+{
+ fstring device;
+ char *devicepath;
+
+ if ( !q_u->devicename )
+ return WERR_ACCESS_DENIED;
+
+ rpcstr_pull(device, q_u->devicename->buffer, sizeof(device), q_u->devicename->uni_str_len*2, 0);
+
+ if (!(devicepath = get_device_path(p->mem_ctx, device))) {
+ return WERR_NOMEM;
+ }
+
+ /* This has to be DOUBLE NULL terminated */
+
+ init_unistr2( &r_u->devicepath, devicepath, UNI_STR_DBLTERMINATE );
+ TALLOC_FREE(devicepath);
+ r_u->needed = r_u->devicepath.uni_str_len;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _ntsvcs_get_device_reg_property( pipes_struct *p, NTSVCS_Q_GET_DEVICE_REG_PROPERTY *q_u, NTSVCS_R_GET_DEVICE_REG_PROPERTY *r_u )
+{
+ fstring devicepath;
+ char *ptr;
+ REGVAL_CTR *values;
+ REGISTRY_VALUE *val;
+
+ rpcstr_pull(devicepath, q_u->devicepath.buffer, sizeof(devicepath), q_u->devicepath.uni_str_len*2, 0);
+
+ switch( q_u->property ) {
+ case DEV_REGPROP_DESC:
+ /* just parse the service name from the device path and then
+ lookup the display name */
+ if ( !(ptr = strrchr_m( devicepath, '\\' )) )
+ return WERR_GENERAL_FAILURE;
+ *ptr = '\0';
+
+ if ( !(ptr = strrchr_m( devicepath, '_' )) )
+ return WERR_GENERAL_FAILURE;
+ ptr++;
+
+ if ( !(values = svcctl_fetch_regvalues( ptr, p->pipe_user.nt_user_token )) )
+ return WERR_GENERAL_FAILURE;
+
+ if ( !(val = regval_ctr_getvalue( values, "DisplayName" )) ) {
+ TALLOC_FREE( values );
+ return WERR_GENERAL_FAILURE;
+ }
+
+ r_u->unknown1 = 0x1; /* always 1...tested using a remove device manager connection */
+ r_u->size = reg_init_regval_buffer( &r_u->value, val );
+ r_u->needed = r_u->size;
+
+ TALLOC_FREE(values);
+
+ break;
+
+ default:
+ r_u->unknown1 = 0x00437c98;
+ return WERR_CM_NO_SUCH_VALUE;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_ValidateDeviceInstance(pipes_struct *p,
+ struct PNP_ValidateDeviceInstance *r)
+{
+ /* whatever dude */
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _PNP_GetHwProfInfo(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(pipes_struct *p,
+ struct PNP_HwProfFlags *r)
+{
+ /* just nod your head */
+
+ return WERR_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_Disconnect(pipes_struct *p,
+ struct PNP_Disconnect *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_Connect(pipes_struct *p,
+ struct PNP_Connect *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetGlobalState(pipes_struct *p,
+ struct PNP_GetGlobalState *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_InitDetection(pipes_struct *p,
+ struct PNP_InitDetection *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_ReportLogOn(pipes_struct *p,
+ struct PNP_ReportLogOn *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetRootDeviceInstance(pipes_struct *p,
+ struct PNP_GetRootDeviceInstance *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetRelatedDeviceInstance(pipes_struct *p,
+ struct PNP_GetRelatedDeviceInstance *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_EnumerateSubKeys(pipes_struct *p,
+ struct PNP_EnumerateSubKeys *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetDeviceList(pipes_struct *p,
+ struct PNP_GetDeviceList *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetDepth(pipes_struct *p,
+ struct PNP_GetDepth *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetDeviceRegProp(pipes_struct *p,
+ struct PNP_GetDeviceRegProp *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetDeviceRegProp(pipes_struct *p,
+ struct PNP_SetDeviceRegProp *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassInstance(pipes_struct *p,
+ struct PNP_GetClassInstance *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_CreateKey(pipes_struct *p,
+ struct PNP_CreateKey *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DeleteRegistryKey(pipes_struct *p,
+ struct PNP_DeleteRegistryKey *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassCount(pipes_struct *p,
+ struct PNP_GetClassCount *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassName(pipes_struct *p,
+ struct PNP_GetClassName *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DeleteClassKey(pipes_struct *p,
+ struct PNP_DeleteClassKey *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetInterfaceDeviceAlias(pipes_struct *p,
+ struct PNP_GetInterfaceDeviceAlias *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetInterfaceDeviceList(pipes_struct *p,
+ struct PNP_GetInterfaceDeviceList *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetInterfaceDeviceListSize(pipes_struct *p,
+ struct PNP_GetInterfaceDeviceListSize *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RegisterDeviceClassAssociation(pipes_struct *p,
+ struct PNP_RegisterDeviceClassAssociation *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_UnregisterDeviceClassAssociation(pipes_struct *p,
+ struct PNP_UnregisterDeviceClassAssociation *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetClassRegProp(pipes_struct *p,
+ struct PNP_GetClassRegProp *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetClassRegProp(pipes_struct *p,
+ struct PNP_SetClassRegProp *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_CreateDevInst(pipes_struct *p,
+ struct PNP_CreateDevInst *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DeviceInstanceAction(pipes_struct *p,
+ struct PNP_DeviceInstanceAction *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetDeviceStatus(pipes_struct *p,
+ struct PNP_GetDeviceStatus *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetDeviceProblem(pipes_struct *p,
+ struct PNP_SetDeviceProblem *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DisableDevInst(pipes_struct *p,
+ struct PNP_DisableDevInst *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_UninstallDevInst(pipes_struct *p,
+ struct PNP_UninstallDevInst *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_AddID(pipes_struct *p,
+ struct PNP_AddID *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RegisterDriver(pipes_struct *p,
+ struct PNP_RegisterDriver *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryRemove(pipes_struct *p,
+ struct PNP_QueryRemove *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RequestDeviceEject(pipes_struct *p,
+ struct PNP_RequestDeviceEject *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_IsDockStationPresent(pipes_struct *p,
+ struct PNP_IsDockStationPresent *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RequestEjectPC(pipes_struct *p,
+ struct PNP_RequestEjectPC *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_AddEmptyLogConf(pipes_struct *p,
+ struct PNP_AddEmptyLogConf *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_FreeLogConf(pipes_struct *p,
+ struct PNP_FreeLogConf *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetFirstLogConf(pipes_struct *p,
+ struct PNP_GetFirstLogConf *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetNextLogConf(pipes_struct *p,
+ struct PNP_GetNextLogConf *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetLogConfPriority(pipes_struct *p,
+ struct PNP_GetLogConfPriority *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_AddResDes(pipes_struct *p,
+ struct PNP_AddResDes *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_FreeResDes(pipes_struct *p,
+ struct PNP_FreeResDes *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetNextResDes(pipes_struct *p,
+ struct PNP_GetNextResDes *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetResDesData(pipes_struct *p,
+ struct PNP_GetResDesData *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetResDesDataSize(pipes_struct *p,
+ struct PNP_GetResDesDataSize *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_ModifyResDes(pipes_struct *p,
+ struct PNP_ModifyResDes *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_DetectResourceLimit(pipes_struct *p,
+ struct PNP_DetectResourceLimit *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryResConfList(pipes_struct *p,
+ struct PNP_QueryResConfList *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_SetHwProf(pipes_struct *p,
+ struct PNP_SetHwProf *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryArbitratorFreeData(pipes_struct *p,
+ struct PNP_QueryArbitratorFreeData *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_QueryArbitratorFreeSize(pipes_struct *p,
+ struct PNP_QueryArbitratorFreeSize *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RunDetection(pipes_struct *p,
+ struct PNP_RunDetection *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_RegisterNotification(pipes_struct *p,
+ struct PNP_RegisterNotification *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_UnregisterNotification(pipes_struct *p,
+ struct PNP_UnregisterNotification *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetCustomDevProp(pipes_struct *p,
+ struct PNP_GetCustomDevProp *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetVersionInternal(pipes_struct *p,
+ struct PNP_GetVersionInternal *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetBlockedDriverInfo(pipes_struct *p,
+ struct PNP_GetBlockedDriverInfo *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+WERROR _PNP_GetServerSideDeviceInstallFlags(pipes_struct *p,
+ struct PNP_GetServerSideDeviceInstallFlags *r)
+{
+ p->rng_fault_state = true;
+ return WERR_NOT_SUPPORTED;
+}
+
diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c
new file mode 100644
index 0000000000..be7d3db444
--- /dev/null
+++ b/source3/rpc_server/srv_pipe.c
@@ -0,0 +1,2419 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Almost completely rewritten by (C) Jeremy Allison 2005.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* this module apparently provides an implementation of DCE/RPC over a
+ * named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
+ * documentation are available (in on-line form) from the X-Open group.
+ *
+ * this module should provide a level of abstraction between SMB
+ * and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ * data copies, and network traffic.
+ *
+ */
+
+#include "includes.h"
+
+extern struct current_user current_user;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static void free_pipe_ntlmssp_auth_data(struct pipe_auth_data *auth)
+{
+ AUTH_NTLMSSP_STATE *a = auth->a_u.auth_ntlmssp_state;
+
+ if (a) {
+ auth_ntlmssp_end(&a);
+ }
+ auth->a_u.auth_ntlmssp_state = NULL;
+}
+
+static DATA_BLOB generic_session_key(void)
+{
+ return data_blob("SystemLibraryDTC", 16);
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata.
+ Handle NTLMSSP.
+ ********************************************************************/
+
+static bool create_next_pdu_ntlmssp(pipes_struct *p)
+{
+ RPC_HDR_RESP hdr_resp;
+ uint32 ss_padding_len = 0;
+ uint32 data_space_available;
+ uint32 data_len_left;
+ uint32 data_len;
+ prs_struct outgoing_pdu;
+ NTSTATUS status;
+ DATA_BLOB auth_blob;
+ RPC_HDR_AUTH auth_info;
+ uint8 auth_type, auth_level;
+ AUTH_NTLMSSP_STATE *a = p->auth.a_u.auth_ntlmssp_state;
+
+ /*
+ * If we're in the fault state, keep returning fault PDU's until
+ * the pipe gets closed. JRA.
+ */
+
+ if(p->fault_state) {
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ return True;
+ }
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ /* Change the incoming request header to a response. */
+ p->hdr.pkt_type = RPC_RESPONSE;
+
+ /* Set up rpc header flags. */
+ if (p->out_data.data_sent_length == 0) {
+ p->hdr.flags = RPC_FLG_FIRST;
+ } else {
+ p->hdr.flags = 0;
+ }
+
+ /*
+ * Work out how much we can fit in a single PDU.
+ */
+
+ data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length;
+
+ /*
+ * Ensure there really is data left to send.
+ */
+
+ if(!data_len_left) {
+ DEBUG(0,("create_next_pdu_ntlmssp: no data left to send !\n"));
+ return False;
+ }
+
+ data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN -
+ RPC_HDR_AUTH_LEN - NTLMSSP_SIG_SIZE;
+
+ /*
+ * The amount we send is the minimum of the available
+ * space and the amount left to send.
+ */
+
+ data_len = MIN(data_len_left, data_space_available);
+
+ /*
+ * Set up the alloc hint. This should be the data left to
+ * send.
+ */
+
+ hdr_resp.alloc_hint = data_len_left;
+
+ /*
+ * Work out if this PDU will be the last.
+ */
+
+ if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata)) {
+ p->hdr.flags |= RPC_FLG_LAST;
+ if (data_len_left % 8) {
+ ss_padding_len = 8 - (data_len_left % 8);
+ DEBUG(10,("create_next_pdu_ntlmssp: adding sign/seal padding of %u\n",
+ ss_padding_len ));
+ }
+ }
+
+ /*
+ * Set up the header lengths.
+ */
+
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN +
+ data_len + ss_padding_len +
+ RPC_HDR_AUTH_LEN + NTLMSSP_SIG_SIZE;
+ p->hdr.auth_len = NTLMSSP_SIG_SIZE;
+
+
+ /*
+ * Init the parse struct to point at the outgoing
+ * data.
+ */
+
+ prs_init_empty( &outgoing_pdu, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /* Store the header in the data stream. */
+ if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_ntlmssp: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_ntlmssp: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Copy the data into the PDU. */
+
+ if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) {
+ DEBUG(0,("create_next_pdu_ntlmssp: failed to copy %u bytes of data.\n", (unsigned int)data_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Copy the sign/seal padding data. */
+ if (ss_padding_len) {
+ char pad[8];
+
+ memset(pad, '\0', 8);
+ if (!prs_copy_data_in(&outgoing_pdu, pad, ss_padding_len)) {
+ DEBUG(0,("create_next_pdu_ntlmssp: failed to add %u bytes of pad data.\n",
+ (unsigned int)ss_padding_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ }
+
+
+ /* Now write out the auth header and null blob. */
+ if (p->auth.auth_type == PIPE_AUTH_TYPE_NTLMSSP) {
+ auth_type = RPC_NTLMSSP_AUTH_TYPE;
+ } else {
+ auth_type = RPC_SPNEGO_AUTH_TYPE;
+ }
+ if (p->auth.auth_level == PIPE_AUTH_LEVEL_PRIVACY) {
+ auth_level = RPC_AUTH_LEVEL_PRIVACY;
+ } else {
+ auth_level = RPC_AUTH_LEVEL_INTEGRITY;
+ }
+
+ init_rpc_hdr_auth(&auth_info, auth_type, auth_level, ss_padding_len, 1 /* context id. */);
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_ntlmssp: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Generate the sign blob. */
+
+ switch (p->auth.auth_level) {
+ case PIPE_AUTH_LEVEL_PRIVACY:
+ /* Data portion is encrypted. */
+ status = ntlmssp_seal_packet(a->ntlmssp_state,
+ (unsigned char *)prs_data_p(&outgoing_pdu) + RPC_HEADER_LEN + RPC_HDR_RESP_LEN,
+ data_len + ss_padding_len,
+ (unsigned char *)prs_data_p(&outgoing_pdu),
+ (size_t)prs_offset(&outgoing_pdu),
+ &auth_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&auth_blob);
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ break;
+ case PIPE_AUTH_LEVEL_INTEGRITY:
+ /* Data is signed. */
+ status = ntlmssp_sign_packet(a->ntlmssp_state,
+ (unsigned char *)prs_data_p(&outgoing_pdu) + RPC_HEADER_LEN + RPC_HDR_RESP_LEN,
+ data_len + ss_padding_len,
+ (unsigned char *)prs_data_p(&outgoing_pdu),
+ (size_t)prs_offset(&outgoing_pdu),
+ &auth_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&auth_blob);
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ break;
+ default:
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Append the auth blob. */
+ if (!prs_copy_data_in(&outgoing_pdu, (char *)auth_blob.data, NTLMSSP_SIG_SIZE)) {
+ DEBUG(0,("create_next_pdu_ntlmssp: failed to add %u bytes auth blob.\n",
+ (unsigned int)NTLMSSP_SIG_SIZE));
+ data_blob_free(&auth_blob);
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ data_blob_free(&auth_blob);
+
+ /*
+ * Setup the counts for this PDU.
+ */
+
+ p->out_data.data_sent_length += data_len;
+ p->out_data.current_pdu_len = p->hdr.frag_len;
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata.
+ Return an schannel authenticated fragment.
+ ********************************************************************/
+
+static bool create_next_pdu_schannel(pipes_struct *p)
+{
+ RPC_HDR_RESP hdr_resp;
+ uint32 ss_padding_len = 0;
+ uint32 data_len;
+ uint32 data_space_available;
+ uint32 data_len_left;
+ prs_struct outgoing_pdu;
+ uint32 data_pos;
+
+ /*
+ * If we're in the fault state, keep returning fault PDU's until
+ * the pipe gets closed. JRA.
+ */
+
+ if(p->fault_state) {
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ return True;
+ }
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ /* Change the incoming request header to a response. */
+ p->hdr.pkt_type = RPC_RESPONSE;
+
+ /* Set up rpc header flags. */
+ if (p->out_data.data_sent_length == 0) {
+ p->hdr.flags = RPC_FLG_FIRST;
+ } else {
+ p->hdr.flags = 0;
+ }
+
+ /*
+ * Work out how much we can fit in a single PDU.
+ */
+
+ data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length;
+
+ /*
+ * Ensure there really is data left to send.
+ */
+
+ if(!data_len_left) {
+ DEBUG(0,("create_next_pdu_schannel: no data left to send !\n"));
+ return False;
+ }
+
+ data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN -
+ RPC_HDR_AUTH_LEN - RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN;
+
+ /*
+ * The amount we send is the minimum of the available
+ * space and the amount left to send.
+ */
+
+ data_len = MIN(data_len_left, data_space_available);
+
+ /*
+ * Set up the alloc hint. This should be the data left to
+ * send.
+ */
+
+ hdr_resp.alloc_hint = data_len_left;
+
+ /*
+ * Work out if this PDU will be the last.
+ */
+
+ if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata)) {
+ p->hdr.flags |= RPC_FLG_LAST;
+ if (data_len_left % 8) {
+ ss_padding_len = 8 - (data_len_left % 8);
+ DEBUG(10,("create_next_pdu_schannel: adding sign/seal padding of %u\n",
+ ss_padding_len ));
+ }
+ }
+
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len + ss_padding_len +
+ RPC_HDR_AUTH_LEN + RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN;
+ p->hdr.auth_len = RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN;
+
+ /*
+ * Init the parse struct to point at the outgoing
+ * data.
+ */
+
+ prs_init_empty( &outgoing_pdu, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /* Store the header in the data stream. */
+ if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_schannel: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_schannel: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Store the current offset. */
+ data_pos = prs_offset(&outgoing_pdu);
+
+ /* Copy the data into the PDU. */
+
+ if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) {
+ DEBUG(0,("create_next_pdu_schannel: failed to copy %u bytes of data.\n", (unsigned int)data_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Copy the sign/seal padding data. */
+ if (ss_padding_len) {
+ char pad[8];
+ memset(pad, '\0', 8);
+ if (!prs_copy_data_in(&outgoing_pdu, pad, ss_padding_len)) {
+ DEBUG(0,("create_next_pdu_schannel: failed to add %u bytes of pad data.\n", (unsigned int)ss_padding_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+ }
+
+ {
+ /*
+ * Schannel processing.
+ */
+ char *data;
+ RPC_HDR_AUTH auth_info;
+ RPC_AUTH_SCHANNEL_CHK verf;
+
+ data = prs_data_p(&outgoing_pdu) + data_pos;
+ /* Check it's the type of reply we were expecting to decode */
+
+ init_rpc_hdr_auth(&auth_info,
+ RPC_SCHANNEL_AUTH_TYPE,
+ p->auth.auth_level == PIPE_AUTH_LEVEL_PRIVACY ?
+ RPC_AUTH_LEVEL_PRIVACY : RPC_AUTH_LEVEL_INTEGRITY,
+ ss_padding_len, 1);
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_schannel: failed to marshall RPC_HDR_AUTH.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ schannel_encode(p->auth.a_u.schannel_auth,
+ p->auth.auth_level,
+ SENDER_IS_ACCEPTOR,
+ &verf, data, data_len + ss_padding_len);
+
+ if (!smb_io_rpc_auth_schannel_chk("", RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN,
+ &verf, &outgoing_pdu, 0)) {
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ p->auth.a_u.schannel_auth->seq_num++;
+ }
+
+ /*
+ * Setup the counts for this PDU.
+ */
+
+ p->out_data.data_sent_length += data_len;
+ p->out_data.current_pdu_len = p->hdr.frag_len;
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata.
+ No authentication done.
+********************************************************************/
+
+static bool create_next_pdu_noauth(pipes_struct *p)
+{
+ RPC_HDR_RESP hdr_resp;
+ uint32 data_len;
+ uint32 data_space_available;
+ uint32 data_len_left;
+ prs_struct outgoing_pdu;
+
+ /*
+ * If we're in the fault state, keep returning fault PDU's until
+ * the pipe gets closed. JRA.
+ */
+
+ if(p->fault_state) {
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ return True;
+ }
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ /* Change the incoming request header to a response. */
+ p->hdr.pkt_type = RPC_RESPONSE;
+
+ /* Set up rpc header flags. */
+ if (p->out_data.data_sent_length == 0) {
+ p->hdr.flags = RPC_FLG_FIRST;
+ } else {
+ p->hdr.flags = 0;
+ }
+
+ /*
+ * Work out how much we can fit in a single PDU.
+ */
+
+ data_len_left = prs_offset(&p->out_data.rdata) - p->out_data.data_sent_length;
+
+ /*
+ * Ensure there really is data left to send.
+ */
+
+ if(!data_len_left) {
+ DEBUG(0,("create_next_pdu_noath: no data left to send !\n"));
+ return False;
+ }
+
+ data_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN;
+
+ /*
+ * The amount we send is the minimum of the available
+ * space and the amount left to send.
+ */
+
+ data_len = MIN(data_len_left, data_space_available);
+
+ /*
+ * Set up the alloc hint. This should be the data left to
+ * send.
+ */
+
+ hdr_resp.alloc_hint = data_len_left;
+
+ /*
+ * Work out if this PDU will be the last.
+ */
+
+ if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata)) {
+ p->hdr.flags |= RPC_FLG_LAST;
+ }
+
+ /*
+ * Set up the header lengths.
+ */
+
+ p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len;
+ p->hdr.auth_len = 0;
+
+ /*
+ * Init the parse struct to point at the outgoing
+ * data.
+ */
+
+ prs_init_empty( &outgoing_pdu, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /* Store the header in the data stream. */
+ if(!smb_io_rpc_hdr("hdr", &p->hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_noath: failed to marshall RPC_HDR.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("create_next_pdu_noath: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /* Copy the data into the PDU. */
+
+ if(!prs_append_some_prs_data(&outgoing_pdu, &p->out_data.rdata, p->out_data.data_sent_length, data_len)) {
+ DEBUG(0,("create_next_pdu_noauth: failed to copy %u bytes of data.\n", (unsigned int)data_len));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ /*
+ * Setup the counts for this PDU.
+ */
+
+ p->out_data.data_sent_length += data_len;
+ p->out_data.current_pdu_len = p->hdr.frag_len;
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+/*******************************************************************
+ Generate the next PDU to be returned from the data in p->rdata.
+********************************************************************/
+
+bool create_next_pdu(pipes_struct *p)
+{
+ switch(p->auth.auth_level) {
+ case PIPE_AUTH_LEVEL_NONE:
+ case PIPE_AUTH_LEVEL_CONNECT:
+ /* This is incorrect for auth level connect. Fixme. JRA */
+ return create_next_pdu_noauth(p);
+
+ default:
+ switch(p->auth.auth_type) {
+ case PIPE_AUTH_TYPE_NTLMSSP:
+ case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
+ return create_next_pdu_ntlmssp(p);
+ case PIPE_AUTH_TYPE_SCHANNEL:
+ return create_next_pdu_schannel(p);
+ default:
+ break;
+ }
+ }
+
+ DEBUG(0,("create_next_pdu: invalid internal auth level %u / type %u",
+ (unsigned int)p->auth.auth_level,
+ (unsigned int)p->auth.auth_type));
+ return False;
+}
+
+/*******************************************************************
+ Process an NTLMSSP authentication response.
+ If this function succeeds, the user has been authenticated
+ and their domain, name and calling workstation stored in
+ the pipe struct.
+*******************************************************************/
+
+static bool pipe_ntlmssp_verify_final(pipes_struct *p, DATA_BLOB *p_resp_blob)
+{
+ DATA_BLOB session_key, reply;
+ NTSTATUS status;
+ AUTH_NTLMSSP_STATE *a = p->auth.a_u.auth_ntlmssp_state;
+ bool ret;
+
+ DEBUG(5,("pipe_ntlmssp_verify_final: pipe %s checking user details\n", p->name));
+
+ ZERO_STRUCT(reply);
+
+ /* Set up for non-authenticated user. */
+ TALLOC_FREE(p->pipe_user.nt_user_token);
+ p->pipe_user.ut.ngroups = 0;
+ SAFE_FREE( p->pipe_user.ut.groups);
+
+ /* this has to be done as root in order to verify the password */
+ become_root();
+ status = auth_ntlmssp_update(a, *p_resp_blob, &reply);
+ unbecome_root();
+
+ /* Don't generate a reply. */
+ data_blob_free(&reply);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
+
+ /* Finally - if the pipe negotiated integrity (sign) or privacy (seal)
+ ensure the underlying NTLMSSP flags are also set. If not we should
+ refuse the bind. */
+
+ if (p->auth.auth_level == PIPE_AUTH_LEVEL_INTEGRITY) {
+ if (!(a->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
+ DEBUG(0,("pipe_ntlmssp_verify_final: pipe %s : packet integrity requested "
+ "but client declined signing.\n",
+ p->name ));
+ return False;
+ }
+ }
+ if (p->auth.auth_level == PIPE_AUTH_LEVEL_PRIVACY) {
+ if (!(a->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
+ DEBUG(0,("pipe_ntlmssp_verify_final: pipe %s : packet privacy requested "
+ "but client declined sealing.\n",
+ p->name ));
+ return False;
+ }
+ }
+
+ DEBUG(5, ("pipe_ntlmssp_verify_final: OK: user: %s domain: %s "
+ "workstation: %s\n", a->ntlmssp_state->user,
+ a->ntlmssp_state->domain, a->ntlmssp_state->workstation));
+
+ /*
+ * Store the UNIX credential data (uid/gid pair) in the pipe structure.
+ */
+
+ p->pipe_user.ut.uid = a->server_info->utok.uid;
+ p->pipe_user.ut.gid = a->server_info->utok.gid;
+
+ p->pipe_user.ut.ngroups = a->server_info->utok.ngroups;
+ if (p->pipe_user.ut.ngroups) {
+ if (!(p->pipe_user.ut.groups = (gid_t *)memdup(
+ a->server_info->utok.groups,
+ sizeof(gid_t) * p->pipe_user.ut.ngroups))) {
+ DEBUG(0,("failed to memdup group list to p->pipe_user.groups\n"));
+ return False;
+ }
+ }
+
+ if (a->server_info->ptok) {
+ p->pipe_user.nt_user_token =
+ dup_nt_token(NULL, a->server_info->ptok);
+ } else {
+ DEBUG(1,("Error: Authmodule failed to provide nt_user_token\n"));
+ p->pipe_user.nt_user_token = NULL;
+ return False;
+ }
+
+ TALLOC_FREE(p->server_info);
+
+ p->server_info = copy_serverinfo(p, a->server_info);
+ if (p->server_info == NULL) {
+ DEBUG(0, ("copy_serverinfo failed\n"));
+ return false;
+ }
+
+ /*
+ * We're an authenticated bind over smb, so the session key needs to
+ * be set to "SystemLibraryDTC". Weird, but this is what Windows
+ * does. See the RPC-SAMBA3SESSIONKEY.
+ */
+
+ session_key = generic_session_key();
+ if (session_key.data == NULL) {
+ return False;
+ }
+
+ ret = server_info_set_session_key(p->server_info, session_key);
+
+ data_blob_free(&session_key);
+
+ return True;
+}
+
+/*******************************************************************
+ The switch table for the pipe names and the functions to handle them.
+*******************************************************************/
+
+struct rpc_table {
+ struct {
+ const char *clnt;
+ const char *srv;
+ } pipe;
+ struct ndr_syntax_id rpc_interface;
+ const struct api_struct *cmds;
+ int n_cmds;
+};
+
+static struct rpc_table *rpc_lookup;
+static int rpc_lookup_size;
+
+/*******************************************************************
+ This is the "stage3" NTLMSSP response after a bind request and reply.
+*******************************************************************/
+
+bool api_pipe_bind_auth3(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_AUTH auth_info;
+ uint32 pad;
+ DATA_BLOB blob;
+
+ ZERO_STRUCT(blob);
+
+ DEBUG(5,("api_pipe_bind_auth3: decode request. %d\n", __LINE__));
+
+ if (p->hdr.auth_len == 0) {
+ DEBUG(0,("api_pipe_bind_auth3: No auth field sent !\n"));
+ goto err;
+ }
+
+ /* 4 bytes padding. */
+ if (!prs_uint32("pad", rpc_in_p, 0, &pad)) {
+ DEBUG(0,("api_pipe_bind_auth3: unmarshall of 4 byte pad failed.\n"));
+ goto err;
+ }
+
+ /*
+ * Decode the authentication verifier response.
+ */
+
+ if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_auth3: unmarshall of RPC_HDR_AUTH failed.\n"));
+ goto err;
+ }
+
+ if (auth_info.auth_type != RPC_NTLMSSP_AUTH_TYPE) {
+ DEBUG(0,("api_pipe_bind_auth3: incorrect auth type (%u).\n",
+ (unsigned int)auth_info.auth_type ));
+ return False;
+ }
+
+ blob = data_blob(NULL,p->hdr.auth_len);
+
+ if (!prs_copy_data_out((char *)blob.data, rpc_in_p, p->hdr.auth_len)) {
+ DEBUG(0,("api_pipe_bind_auth3: Failed to pull %u bytes - the response blob.\n",
+ (unsigned int)p->hdr.auth_len ));
+ goto err;
+ }
+
+ /*
+ * The following call actually checks the challenge/response data.
+ * for correctness against the given DOMAIN\user name.
+ */
+
+ if (!pipe_ntlmssp_verify_final(p, &blob)) {
+ goto err;
+ }
+
+ data_blob_free(&blob);
+
+ p->pipe_bound = True;
+
+ return True;
+
+ err:
+
+ data_blob_free(&blob);
+ free_pipe_ntlmssp_auth_data(&p->auth);
+ p->auth.a_u.auth_ntlmssp_state = NULL;
+
+ return False;
+}
+
+/*******************************************************************
+ Marshall a bind_nak pdu.
+*******************************************************************/
+
+static bool setup_bind_nak(pipes_struct *p)
+{
+ prs_struct outgoing_rpc;
+ RPC_HDR nak_hdr;
+ uint16 zero = 0;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init_empty( &outgoing_rpc, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Initialize a bind_nak header.
+ */
+
+ init_rpc_hdr(&nak_hdr, RPC_BINDNACK, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id, RPC_HEADER_LEN + sizeof(uint16), 0);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &nak_hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("setup_bind_nak: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ /*
+ * Now add the reject reason.
+ */
+
+ if(!prs_uint16("reject code", &outgoing_rpc, 0, &zero)) {
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ if (p->auth.auth_data_free_func) {
+ (*p->auth.auth_data_free_func)(&p->auth);
+ }
+ p->auth.auth_level = PIPE_AUTH_LEVEL_NONE;
+ p->auth.auth_type = PIPE_AUTH_TYPE_NONE;
+ p->pipe_bound = False;
+
+ return True;
+}
+
+/*******************************************************************
+ Marshall a fault pdu.
+*******************************************************************/
+
+bool setup_fault_pdu(pipes_struct *p, NTSTATUS status)
+{
+ prs_struct outgoing_pdu;
+ RPC_HDR fault_hdr;
+ RPC_HDR_RESP hdr_resp;
+ RPC_HDR_FAULT fault_resp;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init_empty( &outgoing_pdu, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Initialize a fault header.
+ */
+
+ init_rpc_hdr(&fault_hdr, RPC_FAULT, RPC_FLG_FIRST | RPC_FLG_LAST | RPC_FLG_NOCALL,
+ p->hdr.call_id, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_FAULT_LEN, 0);
+
+ /*
+ * Initialize the HDR_RESP and FAULT parts of the PDU.
+ */
+
+ memset((char *)&hdr_resp, '\0', sizeof(hdr_resp));
+
+ fault_resp.status = status;
+ fault_resp.reserved = 0;
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &fault_hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_resp("resp", &hdr_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_RESP.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_fault("fault", &fault_resp, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_fault_pdu: failed to marshall RPC_HDR_FAULT.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_pdu);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+
+#if 0
+/*******************************************************************
+ Marshall a cancel_ack pdu.
+ We should probably check the auth-verifier here.
+*******************************************************************/
+
+bool setup_cancel_ack_reply(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ prs_struct outgoing_pdu;
+ RPC_HDR ack_reply_hdr;
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&p->out_data.rdata);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_init_empty( &outgoing_pdu, p->mem_ctx, MARSHALL);
+ prs_give_memory( &outgoing_pdu, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Initialize a cancel_ack header.
+ */
+
+ init_rpc_hdr(&ack_reply_hdr, RPC_CANCEL_ACK, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id, RPC_HEADER_LEN, 0);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &ack_reply_hdr, &outgoing_pdu, 0)) {
+ DEBUG(0,("setup_cancel_ack_reply: marshalling of RPC_HDR failed.\n"));
+ prs_mem_free(&outgoing_pdu);
+ return False;
+ }
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_pdu);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&outgoing_pdu);
+ return True;
+}
+#endif
+
+/*******************************************************************
+ Ensure a bind request has the correct abstract & transfer interface.
+ Used to reject unknown binds from Win2k.
+*******************************************************************/
+
+bool check_bind_req(struct pipes_struct *p, RPC_IFACE* abstract,
+ RPC_IFACE* transfer, uint32 context_id)
+{
+ int i=0;
+ struct pipe_rpc_fns *context_fns;
+
+ DEBUG(3,("check_bind_req for %s\n", p->name));
+
+ /* we have to check all now since win2k introduced a new UUID on the lsaprpc pipe */
+
+ for (i=0; i<rpc_lookup_size; i++) {
+ DEBUGADD(10, ("checking %s\n", rpc_lookup[i].pipe.clnt));
+ if (strequal(rpc_lookup[i].pipe.clnt, p->name)
+ && ndr_syntax_id_equal(
+ abstract, &rpc_lookup[i].rpc_interface)
+ && ndr_syntax_id_equal(
+ transfer, &ndr_transfer_syntax)) {
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ return false;
+ }
+
+ context_fns = SMB_MALLOC_P(struct pipe_rpc_fns);
+ if (context_fns == NULL) {
+ DEBUG(0,("check_bind_req: malloc() failed!\n"));
+ return False;
+ }
+
+ context_fns->cmds = rpc_lookup[i].cmds;
+ context_fns->n_cmds = rpc_lookup[i].n_cmds;
+ context_fns->context_id = context_id;
+
+ /* add to the list of open contexts */
+
+ DLIST_ADD( p->contexts, context_fns );
+
+ return True;
+}
+
+/*******************************************************************
+ Register commands to an RPC pipe
+*******************************************************************/
+
+NTSTATUS rpc_pipe_register_commands(int version, const char *clnt,
+ const char *srv,
+ const struct ndr_syntax_id *interface,
+ const struct api_struct *cmds, int size)
+{
+ struct rpc_table *rpc_entry;
+
+ if (!clnt || !srv || !cmds) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (version != SMB_RPC_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register rpc commands!\n"
+ "You tried to register a rpc module with SMB_RPC_INTERFACE_VERSION %d"
+ ", while this version of samba uses version %d!\n",
+ version,SMB_RPC_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ /* TODO:
+ *
+ * we still need to make sure that don't register the same commands twice!!!
+ *
+ * --metze
+ */
+
+ /* We use a temporary variable because this call can fail and
+ rpc_lookup will still be valid afterwards. It could then succeed if
+ called again later */
+ rpc_lookup_size++;
+ rpc_entry = SMB_REALLOC_ARRAY_KEEP_OLD_ON_ERROR(rpc_lookup, struct rpc_table, rpc_lookup_size);
+ if (NULL == rpc_entry) {
+ rpc_lookup_size--;
+ DEBUG(0, ("rpc_pipe_register_commands: memory allocation failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ } else {
+ rpc_lookup = rpc_entry;
+ }
+
+ rpc_entry = rpc_lookup + (rpc_lookup_size - 1);
+ ZERO_STRUCTP(rpc_entry);
+ rpc_entry->pipe.clnt = SMB_STRDUP(clnt);
+ rpc_entry->pipe.srv = SMB_STRDUP(srv);
+ rpc_entry->rpc_interface = *interface;
+ rpc_entry->cmds = cmds;
+ rpc_entry->n_cmds = size;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Is a named pipe known?
+ * @param[in] cli_filename The pipe name requested by the client
+ * @result Do we want to serve this?
+ */
+bool is_known_pipename(const char *cli_filename)
+{
+ const char *pipename = cli_filename;
+ int i;
+
+ if (strnequal(pipename, "\\PIPE\\", 6)) {
+ pipename += 5;
+ }
+
+ if (*pipename == '\\') {
+ pipename += 1;
+ }
+
+ if (lp_disable_spoolss() && strequal(pipename, "spoolss")) {
+ DEBUG(10, ("refusing spoolss access\n"));
+ return false;
+ }
+
+ for (i=0; i<rpc_lookup_size; i++) {
+ if (strequal(pipename, rpc_lookup[i].pipe.clnt)) {
+ return true;
+ }
+ }
+
+ DEBUG(10, ("is_known_pipename: %s unknown\n", cli_filename));
+ return false;
+}
+
+/*******************************************************************
+ Handle a SPNEGO krb5 bind auth.
+*******************************************************************/
+
+static bool pipe_spnego_auth_bind_kerberos(pipes_struct *p, prs_struct *rpc_in_p, RPC_HDR_AUTH *pauth_info,
+ DATA_BLOB *psecblob, prs_struct *pout_auth)
+{
+ return False;
+}
+
+/*******************************************************************
+ Handle the first part of a SPNEGO bind auth.
+*******************************************************************/
+
+static bool pipe_spnego_auth_bind_negotiate(pipes_struct *p, prs_struct *rpc_in_p,
+ RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth)
+{
+ DATA_BLOB blob;
+ DATA_BLOB secblob;
+ DATA_BLOB response;
+ DATA_BLOB chal;
+ char *OIDs[ASN1_MAX_OIDS];
+ int i;
+ NTSTATUS status;
+ bool got_kerberos_mechanism = false;
+ AUTH_NTLMSSP_STATE *a = NULL;
+ RPC_HDR_AUTH auth_info;
+
+ ZERO_STRUCT(secblob);
+ ZERO_STRUCT(chal);
+ ZERO_STRUCT(response);
+
+ /* Grab the SPNEGO blob. */
+ blob = data_blob(NULL,p->hdr.auth_len);
+
+ if (!prs_copy_data_out((char *)blob.data, rpc_in_p, p->hdr.auth_len)) {
+ DEBUG(0,("pipe_spnego_auth_bind_negotiate: Failed to pull %u bytes - the SPNEGO auth header.\n",
+ (unsigned int)p->hdr.auth_len ));
+ goto err;
+ }
+
+ if (blob.data[0] != ASN1_APPLICATION(0)) {
+ goto err;
+ }
+
+ /* parse out the OIDs and the first sec blob */
+ if (!parse_negTokenTarg(blob, OIDs, &secblob)) {
+ DEBUG(0,("pipe_spnego_auth_bind_negotiate: Failed to parse the security blob.\n"));
+ goto err;
+ }
+
+ if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 || strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) {
+ got_kerberos_mechanism = true;
+ }
+
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("pipe_spnego_auth_bind_negotiate: Got OID %s\n", OIDs[i]));
+ SAFE_FREE(OIDs[i]);
+ }
+ DEBUG(3,("pipe_spnego_auth_bind_negotiate: Got secblob of size %lu\n", (unsigned long)secblob.length));
+
+ if ( got_kerberos_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) {
+ bool ret = pipe_spnego_auth_bind_kerberos(p, rpc_in_p, pauth_info, &secblob, pout_auth);
+ data_blob_free(&secblob);
+ data_blob_free(&blob);
+ return ret;
+ }
+
+ if (p->auth.auth_type == PIPE_AUTH_TYPE_SPNEGO_NTLMSSP && p->auth.a_u.auth_ntlmssp_state) {
+ /* Free any previous auth type. */
+ free_pipe_ntlmssp_auth_data(&p->auth);
+ }
+
+ if (!got_kerberos_mechanism) {
+ /* Initialize the NTLM engine. */
+ status = auth_ntlmssp_start(&a);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+
+ /*
+ * Pass the first security blob of data to it.
+ * This can return an error or NT_STATUS_MORE_PROCESSING_REQUIRED
+ * which means we need another packet to complete the bind.
+ */
+
+ status = auth_ntlmssp_update(a, secblob, &chal);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(3,("pipe_spnego_auth_bind_negotiate: auth_ntlmssp_update failed.\n"));
+ goto err;
+ }
+
+ /* Generate the response blob we need for step 2 of the bind. */
+ response = spnego_gen_auth_response(&chal, status, OID_NTLMSSP);
+ } else {
+ /*
+ * SPNEGO negotiate down to NTLMSSP. The subsequent
+ * code to process follow-up packets is not complete
+ * yet. JRA.
+ */
+ response = spnego_gen_auth_response(NULL,
+ NT_STATUS_MORE_PROCESSING_REQUIRED,
+ OID_NTLMSSP);
+ }
+
+ /* Copy the blob into the pout_auth parse struct */
+ init_rpc_hdr_auth(&auth_info, RPC_SPNEGO_AUTH_TYPE, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) {
+ DEBUG(0,("pipe_spnego_auth_bind_negotiate: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err;
+ }
+
+ if (!prs_copy_data_in(pout_auth, (char *)response.data, response.length)) {
+ DEBUG(0,("pipe_spnego_auth_bind_negotiate: marshalling of data blob failed.\n"));
+ goto err;
+ }
+
+ p->auth.a_u.auth_ntlmssp_state = a;
+ p->auth.auth_data_free_func = &free_pipe_ntlmssp_auth_data;
+ p->auth.auth_type = PIPE_AUTH_TYPE_SPNEGO_NTLMSSP;
+
+ data_blob_free(&blob);
+ data_blob_free(&secblob);
+ data_blob_free(&chal);
+ data_blob_free(&response);
+
+ /* We can't set pipe_bound True yet - we need an RPC_ALTER_CONTEXT response packet... */
+ return True;
+
+ err:
+
+ data_blob_free(&blob);
+ data_blob_free(&secblob);
+ data_blob_free(&chal);
+ data_blob_free(&response);
+
+ p->auth.a_u.auth_ntlmssp_state = NULL;
+
+ return False;
+}
+
+/*******************************************************************
+ Handle the second part of a SPNEGO bind auth.
+*******************************************************************/
+
+static bool pipe_spnego_auth_bind_continue(pipes_struct *p, prs_struct *rpc_in_p,
+ RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth)
+{
+ RPC_HDR_AUTH auth_info;
+ DATA_BLOB spnego_blob;
+ DATA_BLOB auth_blob;
+ DATA_BLOB auth_reply;
+ DATA_BLOB response;
+ AUTH_NTLMSSP_STATE *a = p->auth.a_u.auth_ntlmssp_state;
+
+ ZERO_STRUCT(spnego_blob);
+ ZERO_STRUCT(auth_blob);
+ ZERO_STRUCT(auth_reply);
+ ZERO_STRUCT(response);
+
+ /*
+ * NB. If we've negotiated down from krb5 to NTLMSSP we'll currently
+ * fail here as 'a' == NULL.
+ */
+ if (p->auth.auth_type != PIPE_AUTH_TYPE_SPNEGO_NTLMSSP || !a) {
+ DEBUG(0,("pipe_spnego_auth_bind_continue: not in NTLMSSP auth state.\n"));
+ goto err;
+ }
+
+ /* Grab the SPNEGO blob. */
+ spnego_blob = data_blob(NULL,p->hdr.auth_len);
+
+ if (!prs_copy_data_out((char *)spnego_blob.data, rpc_in_p, p->hdr.auth_len)) {
+ DEBUG(0,("pipe_spnego_auth_bind_continue: Failed to pull %u bytes - the SPNEGO auth header.\n",
+ (unsigned int)p->hdr.auth_len ));
+ goto err;
+ }
+
+ if (spnego_blob.data[0] != ASN1_CONTEXT(1)) {
+ DEBUG(0,("pipe_spnego_auth_bind_continue: invalid SPNEGO blob type.\n"));
+ goto err;
+ }
+
+ if (!spnego_parse_auth(spnego_blob, &auth_blob)) {
+ DEBUG(0,("pipe_spnego_auth_bind_continue: invalid SPNEGO blob.\n"));
+ goto err;
+ }
+
+ /*
+ * The following call actually checks the challenge/response data.
+ * for correctness against the given DOMAIN\user name.
+ */
+
+ if (!pipe_ntlmssp_verify_final(p, &auth_blob)) {
+ goto err;
+ }
+
+ data_blob_free(&spnego_blob);
+ data_blob_free(&auth_blob);
+
+ /* Generate the spnego "accept completed" blob - no incoming data. */
+ response = spnego_gen_auth_response(&auth_reply, NT_STATUS_OK, OID_NTLMSSP);
+
+ /* Copy the blob into the pout_auth parse struct */
+ init_rpc_hdr_auth(&auth_info, RPC_SPNEGO_AUTH_TYPE, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) {
+ DEBUG(0,("pipe_spnego_auth_bind_continue: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err;
+ }
+
+ if (!prs_copy_data_in(pout_auth, (char *)response.data, response.length)) {
+ DEBUG(0,("pipe_spnego_auth_bind_continue: marshalling of data blob failed.\n"));
+ goto err;
+ }
+
+ data_blob_free(&auth_reply);
+ data_blob_free(&response);
+
+ p->pipe_bound = True;
+
+ return True;
+
+ err:
+
+ data_blob_free(&spnego_blob);
+ data_blob_free(&auth_blob);
+ data_blob_free(&auth_reply);
+ data_blob_free(&response);
+
+ free_pipe_ntlmssp_auth_data(&p->auth);
+ p->auth.a_u.auth_ntlmssp_state = NULL;
+
+ return False;
+}
+
+/*******************************************************************
+ Handle an schannel bind auth.
+*******************************************************************/
+
+static bool pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p,
+ RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth)
+{
+ RPC_HDR_AUTH auth_info;
+ RPC_AUTH_SCHANNEL_NEG neg;
+ RPC_AUTH_VERIFIER auth_verifier;
+ bool ret;
+ struct dcinfo *pdcinfo;
+ uint32 flags;
+ DATA_BLOB session_key;
+
+ if (!smb_io_rpc_auth_schannel_neg("", &neg, rpc_in_p, 0)) {
+ DEBUG(0,("pipe_schannel_auth_bind: Could not unmarshal SCHANNEL auth neg\n"));
+ return False;
+ }
+
+ /*
+ * The neg.myname key here must match the remote computer name
+ * given in the DOM_CLNT_SRV.uni_comp_name used on all netlogon pipe
+ * operations that use credentials.
+ */
+
+ become_root();
+ ret = secrets_restore_schannel_session_info(p->mem_ctx, neg.myname, &pdcinfo);
+ unbecome_root();
+
+ if (!ret) {
+ DEBUG(0, ("pipe_schannel_auth_bind: Attempt to bind using schannel without successful serverauth2\n"));
+ return False;
+ }
+
+ p->auth.a_u.schannel_auth = talloc(p, struct schannel_auth_struct);
+ if (!p->auth.a_u.schannel_auth) {
+ TALLOC_FREE(pdcinfo);
+ return False;
+ }
+
+ memset(p->auth.a_u.schannel_auth->sess_key, 0, sizeof(p->auth.a_u.schannel_auth->sess_key));
+ memcpy(p->auth.a_u.schannel_auth->sess_key, pdcinfo->sess_key,
+ sizeof(pdcinfo->sess_key));
+
+ TALLOC_FREE(pdcinfo);
+
+ p->auth.a_u.schannel_auth->seq_num = 0;
+
+ /*
+ * JRA. Should we also copy the schannel session key into the pipe session key p->session_key
+ * here ? We do that for NTLMSSP, but the session key is already set up from the vuser
+ * struct of the person who opened the pipe. I need to test this further. JRA.
+ *
+ * VL. As we are mapping this to guest set the generic key
+ * "SystemLibraryDTC" key here. It's a bit difficult to test against
+ * W2k3, as it does not allow schannel binds against SAMR and LSA
+ * anymore.
+ */
+
+ session_key = generic_session_key();
+ if (session_key.data == NULL) {
+ DEBUG(0, ("pipe_schannel_auth_bind: Could not alloc session"
+ " key\n"));
+ return false;
+ }
+
+ ret = server_info_set_session_key(p->server_info, session_key);
+
+ data_blob_free(&session_key);
+
+ if (!ret) {
+ DEBUG(0, ("server_info_set_session_key failed\n"));
+ return false;
+ }
+
+ init_rpc_hdr_auth(&auth_info, RPC_SCHANNEL_AUTH_TYPE, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) {
+ DEBUG(0,("pipe_schannel_auth_bind: marshalling of RPC_HDR_AUTH failed.\n"));
+ return False;
+ }
+
+ /*** SCHANNEL verifier ***/
+
+ init_rpc_auth_verifier(&auth_verifier, "\001", 0x0);
+ if(!smb_io_rpc_schannel_verifier("", &auth_verifier, pout_auth, 0)) {
+ DEBUG(0,("pipe_schannel_auth_bind: marshalling of RPC_AUTH_VERIFIER failed.\n"));
+ return False;
+ }
+
+ prs_align(pout_auth);
+
+ flags = 5;
+ if(!prs_uint32("flags ", pout_auth, 0, &flags)) {
+ return False;
+ }
+
+ DEBUG(10,("pipe_schannel_auth_bind: schannel auth: domain [%s] myname [%s]\n",
+ neg.domain, neg.myname));
+
+ /* We're finished with this bind - no more packets. */
+ p->auth.auth_data_free_func = NULL;
+ p->auth.auth_type = PIPE_AUTH_TYPE_SCHANNEL;
+
+ p->pipe_bound = True;
+
+ return True;
+}
+
+/*******************************************************************
+ Handle an NTLMSSP bind auth.
+*******************************************************************/
+
+static bool pipe_ntlmssp_auth_bind(pipes_struct *p, prs_struct *rpc_in_p,
+ RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth)
+{
+ RPC_HDR_AUTH auth_info;
+ DATA_BLOB blob;
+ DATA_BLOB response;
+ NTSTATUS status;
+ AUTH_NTLMSSP_STATE *a = NULL;
+
+ ZERO_STRUCT(blob);
+ ZERO_STRUCT(response);
+
+ /* Grab the NTLMSSP blob. */
+ blob = data_blob(NULL,p->hdr.auth_len);
+
+ if (!prs_copy_data_out((char *)blob.data, rpc_in_p, p->hdr.auth_len)) {
+ DEBUG(0,("pipe_ntlmssp_auth_bind: Failed to pull %u bytes - the NTLM auth header.\n",
+ (unsigned int)p->hdr.auth_len ));
+ goto err;
+ }
+
+ if (strncmp((char *)blob.data, "NTLMSSP", 7) != 0) {
+ DEBUG(0,("pipe_ntlmssp_auth_bind: Failed to read NTLMSSP in blob\n"));
+ goto err;
+ }
+
+ /* We have an NTLMSSP blob. */
+ status = auth_ntlmssp_start(&a);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("pipe_ntlmssp_auth_bind: auth_ntlmssp_start failed: %s\n",
+ nt_errstr(status) ));
+ goto err;
+ }
+
+ status = auth_ntlmssp_update(a, blob, &response);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(0,("pipe_ntlmssp_auth_bind: auth_ntlmssp_update failed: %s\n",
+ nt_errstr(status) ));
+ goto err;
+ }
+
+ data_blob_free(&blob);
+
+ /* Copy the blob into the pout_auth parse struct */
+ init_rpc_hdr_auth(&auth_info, RPC_NTLMSSP_AUTH_TYPE, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1);
+ if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) {
+ DEBUG(0,("pipe_ntlmssp_auth_bind: marshalling of RPC_HDR_AUTH failed.\n"));
+ goto err;
+ }
+
+ if (!prs_copy_data_in(pout_auth, (char *)response.data, response.length)) {
+ DEBUG(0,("pipe_ntlmssp_auth_bind: marshalling of data blob failed.\n"));
+ goto err;
+ }
+
+ p->auth.a_u.auth_ntlmssp_state = a;
+ p->auth.auth_data_free_func = &free_pipe_ntlmssp_auth_data;
+ p->auth.auth_type = PIPE_AUTH_TYPE_NTLMSSP;
+
+ data_blob_free(&blob);
+ data_blob_free(&response);
+
+ DEBUG(10,("pipe_ntlmssp_auth_bind: NTLMSSP auth started\n"));
+
+ /* We can't set pipe_bound True yet - we need an RPC_AUTH3 response packet... */
+ return True;
+
+ err:
+
+ data_blob_free(&blob);
+ data_blob_free(&response);
+
+ free_pipe_ntlmssp_auth_data(&p->auth);
+ p->auth.a_u.auth_ntlmssp_state = NULL;
+ return False;
+}
+
+/*******************************************************************
+ Respond to a pipe bind request.
+*******************************************************************/
+
+bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_BA hdr_ba;
+ RPC_HDR_RB hdr_rb;
+ RPC_HDR_AUTH auth_info;
+ uint16 assoc_gid;
+ fstring ack_pipe_name;
+ prs_struct out_hdr_ba;
+ prs_struct out_auth;
+ prs_struct outgoing_rpc;
+ int i = 0;
+ int auth_len = 0;
+ unsigned int auth_type = RPC_ANONYMOUS_AUTH_TYPE;
+
+ /* No rebinds on a bound pipe - use alter context. */
+ if (p->pipe_bound) {
+ DEBUG(2,("api_pipe_bind_req: rejecting bind request on bound pipe %s.\n", p->pipe_srv_name));
+ return setup_bind_nak(p);
+ }
+
+ prs_init_empty( &outgoing_rpc, p->mem_ctx, MARSHALL);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Setup the memory to marshall the ba header, and the
+ * auth footers.
+ */
+
+ if(!prs_init(&out_hdr_ba, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("api_pipe_bind_req: malloc out_hdr_ba failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ if(!prs_init(&out_auth, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("api_pipe_bind_req: malloc out_auth failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ return False;
+ }
+
+ DEBUG(5,("api_pipe_bind_req: decode request. %d\n", __LINE__));
+
+ ZERO_STRUCT(hdr_rb);
+
+ /* decode the bind request */
+
+ if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_RB "
+ "struct.\n"));
+ goto err_exit;
+ }
+
+ if (hdr_rb.num_contexts == 0) {
+ DEBUG(0, ("api_pipe_bind_req: no rpc contexts around\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Try and find the correct pipe name to ensure
+ * that this is a pipe name we support.
+ */
+
+ for (i = 0; i < rpc_lookup_size; i++) {
+ if (ndr_syntax_id_equal(&rpc_lookup[i].rpc_interface,
+ &hdr_rb.rpc_context[0].abstract)) {
+ DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+ rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+ fstrcpy(p->name, rpc_lookup[i].pipe.clnt);
+ fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ if (NT_STATUS_IS_ERR(smb_probe_module("rpc", p->name))) {
+ DEBUG(3,("api_pipe_bind_req: Unknown pipe name %s in bind request.\n",
+ p->name ));
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+
+ return setup_bind_nak(p);
+ }
+
+ for (i = 0; i < rpc_lookup_size; i++) {
+ if (strequal(rpc_lookup[i].pipe.clnt, p->name)) {
+ DEBUG(3, ("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n",
+ rpc_lookup[i].pipe.clnt, rpc_lookup[i].pipe.srv));
+ fstrcpy(p->pipe_srv_name, rpc_lookup[i].pipe.srv);
+ break;
+ }
+ }
+
+ if (i == rpc_lookup_size) {
+ DEBUG(0, ("module %s doesn't provide functions for pipe %s!\n", p->name, p->name));
+ goto err_exit;
+ }
+ }
+
+ /* name has to be \PIPE\xxxxx */
+ fstrcpy(ack_pipe_name, "\\PIPE\\");
+ fstrcat(ack_pipe_name, p->pipe_srv_name);
+
+ DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__));
+
+ /*
+ * Check if this is an authenticated bind request.
+ */
+
+ if (p->hdr.auth_len) {
+ /*
+ * Decode the authentication verifier.
+ */
+
+ if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n"));
+ goto err_exit;
+ }
+
+ auth_type = auth_info.auth_type;
+
+ /* Work out if we have to sign or seal etc. */
+ switch (auth_info.auth_level) {
+ case RPC_AUTH_LEVEL_INTEGRITY:
+ p->auth.auth_level = PIPE_AUTH_LEVEL_INTEGRITY;
+ break;
+ case RPC_AUTH_LEVEL_PRIVACY:
+ p->auth.auth_level = PIPE_AUTH_LEVEL_PRIVACY;
+ break;
+ default:
+ DEBUG(0,("api_pipe_bind_req: unexpected auth level (%u).\n",
+ (unsigned int)auth_info.auth_level ));
+ goto err_exit;
+ }
+ } else {
+ ZERO_STRUCT(auth_info);
+ }
+
+ assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0;
+
+ switch(auth_type) {
+ case RPC_NTLMSSP_AUTH_TYPE:
+ if (!pipe_ntlmssp_auth_bind(p, rpc_in_p, &auth_info, &out_auth)) {
+ goto err_exit;
+ }
+ assoc_gid = 0x7a77;
+ break;
+
+ case RPC_SCHANNEL_AUTH_TYPE:
+ if (!pipe_schannel_auth_bind(p, rpc_in_p, &auth_info, &out_auth)) {
+ goto err_exit;
+ }
+ break;
+
+ case RPC_SPNEGO_AUTH_TYPE:
+ if (!pipe_spnego_auth_bind_negotiate(p, rpc_in_p, &auth_info, &out_auth)) {
+ goto err_exit;
+ }
+ break;
+
+ case RPC_ANONYMOUS_AUTH_TYPE:
+ /* Unauthenticated bind request. */
+ /* Get the authenticated pipe user from current_user */
+ if (!copy_current_user(&p->pipe_user, &current_user)) {
+ DEBUG(10, ("Could not copy current user\n"));
+ goto err_exit;
+ }
+ /* We're finished - no more packets. */
+ p->auth.auth_type = PIPE_AUTH_TYPE_NONE;
+ /* We must set the pipe auth_level here also. */
+ p->auth.auth_level = PIPE_AUTH_LEVEL_NONE;
+ p->pipe_bound = True;
+ /* The session key was initialized from the SMB
+ * session in make_internal_rpc_pipe_p */
+ break;
+
+ default:
+ DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n", auth_type ));
+ goto err_exit;
+ }
+
+ /*
+ * Create the bind response struct.
+ */
+
+ /* If the requested abstract synt uuid doesn't match our client pipe,
+ reject the bind_ack & set the transfer interface synt to all 0's,
+ ver 0 (observed when NT5 attempts to bind to abstract interfaces
+ unknown to NT4)
+ Needed when adding entries to a DACL from NT5 - SK */
+
+ if(check_bind_req(p, &hdr_rb.rpc_context[0].abstract, &hdr_rb.rpc_context[0].transfer[0],
+ hdr_rb.rpc_context[0].context_id )) {
+ init_rpc_hdr_ba(&hdr_ba,
+ RPC_MAX_PDU_FRAG_LEN,
+ RPC_MAX_PDU_FRAG_LEN,
+ assoc_gid,
+ ack_pipe_name,
+ 0x1, 0x0, 0x0,
+ &hdr_rb.rpc_context[0].transfer[0]);
+ } else {
+ RPC_IFACE null_interface;
+ ZERO_STRUCT(null_interface);
+ /* Rejection reason: abstract syntax not supported */
+ init_rpc_hdr_ba(&hdr_ba, RPC_MAX_PDU_FRAG_LEN,
+ RPC_MAX_PDU_FRAG_LEN, assoc_gid,
+ ack_pipe_name, 0x1, 0x2, 0x1,
+ &null_interface);
+ p->pipe_bound = False;
+ }
+
+ /*
+ * and marshall it.
+ */
+
+ if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Create the header, now we know the length.
+ */
+
+ if (prs_offset(&out_auth)) {
+ auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+ }
+
+ init_rpc_hdr(&p->hdr, RPC_BINDACK, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id,
+ RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + prs_offset(&out_auth),
+ auth_len);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Now add the RPC_HDR_BA and any auth needed.
+ */
+
+ if(!prs_append_prs_data( &outgoing_rpc, &out_hdr_ba)) {
+ DEBUG(0,("api_pipe_bind_req: append of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ if (auth_len && !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
+ DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Setup the lengths for the initial reply.
+ */
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+
+ return True;
+
+ err_exit:
+
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+ return setup_bind_nak(p);
+}
+
+/****************************************************************************
+ Deal with an alter context call. Can be third part of 3 leg auth request for
+ SPNEGO calls.
+****************************************************************************/
+
+bool api_pipe_alter_context(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ RPC_HDR_BA hdr_ba;
+ RPC_HDR_RB hdr_rb;
+ RPC_HDR_AUTH auth_info;
+ uint16 assoc_gid;
+ fstring ack_pipe_name;
+ prs_struct out_hdr_ba;
+ prs_struct out_auth;
+ prs_struct outgoing_rpc;
+ int auth_len = 0;
+
+ prs_init_empty( &outgoing_rpc, p->mem_ctx, MARSHALL);
+
+ /*
+ * Marshall directly into the outgoing PDU space. We
+ * must do this as we need to set to the bind response
+ * header and are never sending more than one PDU here.
+ */
+
+ prs_give_memory( &outgoing_rpc, (char *)p->out_data.current_pdu, sizeof(p->out_data.current_pdu), False);
+
+ /*
+ * Setup the memory to marshall the ba header, and the
+ * auth footers.
+ */
+
+ if(!prs_init(&out_hdr_ba, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("api_pipe_alter_context: malloc out_hdr_ba failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ return False;
+ }
+
+ if(!prs_init(&out_auth, 1024, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("api_pipe_alter_context: malloc out_auth failed.\n"));
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ return False;
+ }
+
+ DEBUG(5,("api_pipe_alter_context: decode request. %d\n", __LINE__));
+
+ /* decode the alter context request */
+ if(!smb_io_rpc_hdr_rb("", &hdr_rb, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_alter_context: unable to unmarshall RPC_HDR_RB struct.\n"));
+ goto err_exit;
+ }
+
+ /* secondary address CAN be NULL
+ * as the specs say it's ignored.
+ * It MUST be NULL to have the spoolss working.
+ */
+ fstrcpy(ack_pipe_name,"");
+
+ DEBUG(5,("api_pipe_alter_context: make response. %d\n", __LINE__));
+
+ /*
+ * Check if this is an authenticated alter context request.
+ */
+
+ if (p->hdr.auth_len != 0) {
+ /*
+ * Decode the authentication verifier.
+ */
+
+ if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) {
+ DEBUG(0,("api_pipe_alter_context: unable to unmarshall RPC_HDR_AUTH struct.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Currently only the SPNEGO auth type uses the alter ctx
+ * response in place of the NTLMSSP auth3 type.
+ */
+
+ if (auth_info.auth_type == RPC_SPNEGO_AUTH_TYPE) {
+ /* We can only finish if the pipe is unbound. */
+ if (!p->pipe_bound) {
+ if (!pipe_spnego_auth_bind_continue(p, rpc_in_p, &auth_info, &out_auth)) {
+ goto err_exit;
+ }
+ } else {
+ goto err_exit;
+ }
+ }
+ } else {
+ ZERO_STRUCT(auth_info);
+ }
+
+ assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0;
+
+ /*
+ * Create the bind response struct.
+ */
+
+ /* If the requested abstract synt uuid doesn't match our client pipe,
+ reject the bind_ack & set the transfer interface synt to all 0's,
+ ver 0 (observed when NT5 attempts to bind to abstract interfaces
+ unknown to NT4)
+ Needed when adding entries to a DACL from NT5 - SK */
+
+ if(check_bind_req(p, &hdr_rb.rpc_context[0].abstract, &hdr_rb.rpc_context[0].transfer[0],
+ hdr_rb.rpc_context[0].context_id )) {
+ init_rpc_hdr_ba(&hdr_ba,
+ RPC_MAX_PDU_FRAG_LEN,
+ RPC_MAX_PDU_FRAG_LEN,
+ assoc_gid,
+ ack_pipe_name,
+ 0x1, 0x0, 0x0,
+ &hdr_rb.rpc_context[0].transfer[0]);
+ } else {
+ RPC_IFACE null_interface;
+ ZERO_STRUCT(null_interface);
+ /* Rejection reason: abstract syntax not supported */
+ init_rpc_hdr_ba(&hdr_ba, RPC_MAX_PDU_FRAG_LEN,
+ RPC_MAX_PDU_FRAG_LEN, assoc_gid,
+ ack_pipe_name, 0x1, 0x2, 0x1,
+ &null_interface);
+ p->pipe_bound = False;
+ }
+
+ /*
+ * and marshall it.
+ */
+
+ if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) {
+ DEBUG(0,("api_pipe_alter_context: marshalling of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Create the header, now we know the length.
+ */
+
+ if (prs_offset(&out_auth)) {
+ auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN;
+ }
+
+ init_rpc_hdr(&p->hdr, RPC_ALTCONTRESP, RPC_FLG_FIRST | RPC_FLG_LAST,
+ p->hdr.call_id,
+ RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + prs_offset(&out_auth),
+ auth_len);
+
+ /*
+ * Marshall the header into the outgoing PDU.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &outgoing_rpc, 0)) {
+ DEBUG(0,("api_pipe_alter_context: marshalling of RPC_HDR failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Now add the RPC_HDR_BA and any auth needed.
+ */
+
+ if(!prs_append_prs_data( &outgoing_rpc, &out_hdr_ba)) {
+ DEBUG(0,("api_pipe_alter_context: append of RPC_HDR_BA failed.\n"));
+ goto err_exit;
+ }
+
+ if (auth_len && !prs_append_prs_data( &outgoing_rpc, &out_auth)) {
+ DEBUG(0,("api_pipe_alter_context: append of auth info failed.\n"));
+ goto err_exit;
+ }
+
+ /*
+ * Setup the lengths for the initial reply.
+ */
+
+ p->out_data.data_sent_length = 0;
+ p->out_data.current_pdu_len = prs_offset(&outgoing_rpc);
+ p->out_data.current_pdu_sent = 0;
+
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+
+ return True;
+
+ err_exit:
+
+ prs_mem_free(&outgoing_rpc);
+ prs_mem_free(&out_hdr_ba);
+ prs_mem_free(&out_auth);
+ return setup_bind_nak(p);
+}
+
+/****************************************************************************
+ Deal with NTLMSSP sign & seal processing on an RPC request.
+****************************************************************************/
+
+bool api_pipe_ntlmssp_auth_process(pipes_struct *p, prs_struct *rpc_in,
+ uint32 *p_ss_padding_len, NTSTATUS *pstatus)
+{
+ RPC_HDR_AUTH auth_info;
+ uint32 auth_len = p->hdr.auth_len;
+ uint32 save_offset = prs_offset(rpc_in);
+ AUTH_NTLMSSP_STATE *a = p->auth.a_u.auth_ntlmssp_state;
+ unsigned char *data = NULL;
+ size_t data_len;
+ unsigned char *full_packet_data = NULL;
+ size_t full_packet_data_len;
+ DATA_BLOB auth_blob;
+
+ *pstatus = NT_STATUS_OK;
+
+ if (p->auth.auth_level == PIPE_AUTH_LEVEL_NONE || p->auth.auth_level == PIPE_AUTH_LEVEL_CONNECT) {
+ return True;
+ }
+
+ if (!a) {
+ *pstatus = NT_STATUS_INVALID_PARAMETER;
+ return False;
+ }
+
+ /* Ensure there's enough data for an authenticated request. */
+ if ((auth_len > RPC_MAX_SIGN_SIZE) ||
+ (RPC_HEADER_LEN + RPC_HDR_REQ_LEN + RPC_HDR_AUTH_LEN + auth_len > p->hdr.frag_len)) {
+ DEBUG(0,("api_pipe_ntlmssp_auth_process: auth_len %u is too large.\n",
+ (unsigned int)auth_len ));
+ *pstatus = NT_STATUS_INVALID_PARAMETER;
+ return False;
+ }
+
+ /*
+ * We need the full packet data + length (minus auth stuff) as well as the packet data + length
+ * after the RPC header.
+ * We need to pass in the full packet (minus auth len) to the NTLMSSP sign and check seal
+ * functions as NTLMv2 checks the rpc headers also.
+ */
+
+ data = (unsigned char *)(prs_data_p(rpc_in) + RPC_HDR_REQ_LEN);
+ data_len = (size_t)(p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - RPC_HDR_AUTH_LEN - auth_len);
+
+ full_packet_data = p->in_data.current_in_pdu;
+ full_packet_data_len = p->hdr.frag_len - auth_len;
+
+ /* Pull the auth header and the following data into a blob. */
+ if(!prs_set_offset(rpc_in, RPC_HDR_REQ_LEN + data_len)) {
+ DEBUG(0,("api_pipe_ntlmssp_auth_process: cannot move offset to %u.\n",
+ (unsigned int)RPC_HDR_REQ_LEN + (unsigned int)data_len ));
+ *pstatus = NT_STATUS_INVALID_PARAMETER;
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+ DEBUG(0,("api_pipe_ntlmssp_auth_process: failed to unmarshall RPC_HDR_AUTH.\n"));
+ *pstatus = NT_STATUS_INVALID_PARAMETER;
+ return False;
+ }
+
+ auth_blob.data = (unsigned char *)prs_data_p(rpc_in) + prs_offset(rpc_in);
+ auth_blob.length = auth_len;
+
+ switch (p->auth.auth_level) {
+ case PIPE_AUTH_LEVEL_PRIVACY:
+ /* Data is encrypted. */
+ *pstatus = ntlmssp_unseal_packet(a->ntlmssp_state,
+ data, data_len,
+ full_packet_data,
+ full_packet_data_len,
+ &auth_blob);
+ if (!NT_STATUS_IS_OK(*pstatus)) {
+ return False;
+ }
+ break;
+ case PIPE_AUTH_LEVEL_INTEGRITY:
+ /* Data is signed. */
+ *pstatus = ntlmssp_check_packet(a->ntlmssp_state,
+ data, data_len,
+ full_packet_data,
+ full_packet_data_len,
+ &auth_blob);
+ if (!NT_STATUS_IS_OK(*pstatus)) {
+ return False;
+ }
+ break;
+ default:
+ *pstatus = NT_STATUS_INVALID_PARAMETER;
+ return False;
+ }
+
+ /*
+ * Return the current pointer to the data offset.
+ */
+
+ if(!prs_set_offset(rpc_in, save_offset)) {
+ DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n",
+ (unsigned int)save_offset ));
+ *pstatus = NT_STATUS_INVALID_PARAMETER;
+ return False;
+ }
+
+ /*
+ * Remember the padding length. We must remove it from the real data
+ * stream once the sign/seal is done.
+ */
+
+ *p_ss_padding_len = auth_info.auth_pad_len;
+
+ return True;
+}
+
+/****************************************************************************
+ Deal with schannel processing on an RPC request.
+****************************************************************************/
+
+bool api_pipe_schannel_process(pipes_struct *p, prs_struct *rpc_in, uint32 *p_ss_padding_len)
+{
+ uint32 data_len;
+ uint32 auth_len;
+ uint32 save_offset = prs_offset(rpc_in);
+ RPC_HDR_AUTH auth_info;
+ RPC_AUTH_SCHANNEL_CHK schannel_chk;
+
+ auth_len = p->hdr.auth_len;
+
+ if (auth_len != RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN) {
+ DEBUG(0,("Incorrect auth_len %u.\n", (unsigned int)auth_len ));
+ return False;
+ }
+
+ /*
+ * The following is that length of the data we must verify or unseal.
+ * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN
+ * preceeding the auth_data.
+ */
+
+ if (p->hdr.frag_len < RPC_HEADER_LEN + RPC_HDR_REQ_LEN + RPC_HDR_AUTH_LEN + auth_len) {
+ DEBUG(0,("Incorrect frag %u, auth %u.\n",
+ (unsigned int)p->hdr.frag_len,
+ (unsigned int)auth_len ));
+ return False;
+ }
+
+ data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ RPC_HDR_AUTH_LEN - auth_len;
+
+ DEBUG(5,("data %d auth %d\n", data_len, auth_len));
+
+ if(!prs_set_offset(rpc_in, RPC_HDR_REQ_LEN + data_len)) {
+ DEBUG(0,("cannot move offset to %u.\n",
+ (unsigned int)RPC_HDR_REQ_LEN + data_len ));
+ return False;
+ }
+
+ if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) {
+ DEBUG(0,("failed to unmarshall RPC_HDR_AUTH.\n"));
+ return False;
+ }
+
+ if (auth_info.auth_type != RPC_SCHANNEL_AUTH_TYPE) {
+ DEBUG(0,("Invalid auth info %d on schannel\n",
+ auth_info.auth_type));
+ return False;
+ }
+
+ if(!smb_io_rpc_auth_schannel_chk("", RPC_AUTH_SCHANNEL_SIGN_OR_SEAL_CHK_LEN, &schannel_chk, rpc_in, 0)) {
+ DEBUG(0,("failed to unmarshal RPC_AUTH_SCHANNEL_CHK.\n"));
+ return False;
+ }
+
+ if (!schannel_decode(p->auth.a_u.schannel_auth,
+ p->auth.auth_level,
+ SENDER_IS_INITIATOR,
+ &schannel_chk,
+ prs_data_p(rpc_in)+RPC_HDR_REQ_LEN, data_len)) {
+ DEBUG(3,("failed to decode PDU\n"));
+ return False;
+ }
+
+ /*
+ * Return the current pointer to the data offset.
+ */
+
+ if(!prs_set_offset(rpc_in, save_offset)) {
+ DEBUG(0,("failed to set offset back to %u\n",
+ (unsigned int)save_offset ));
+ return False;
+ }
+
+ /* The sequence number gets incremented on both send and receive. */
+ p->auth.a_u.schannel_auth->seq_num++;
+
+ /*
+ * Remember the padding length. We must remove it from the real data
+ * stream once the sign/seal is done.
+ */
+
+ *p_ss_padding_len = auth_info.auth_pad_len;
+
+ return True;
+}
+
+/****************************************************************************
+ Return a user struct for a pipe user.
+****************************************************************************/
+
+struct current_user *get_current_user(struct current_user *user, pipes_struct *p)
+{
+ if (p->pipe_bound &&
+ (p->auth.auth_type == PIPE_AUTH_TYPE_NTLMSSP ||
+ (p->auth.auth_type == PIPE_AUTH_TYPE_SPNEGO_NTLMSSP))) {
+ memcpy(user, &p->pipe_user, sizeof(struct current_user));
+ } else {
+ memcpy(user, &current_user, sizeof(struct current_user));
+ }
+
+ return user;
+}
+
+/****************************************************************************
+ Find the set of RPC functions associated with this context_id
+****************************************************************************/
+
+static PIPE_RPC_FNS* find_pipe_fns_by_context( PIPE_RPC_FNS *list, uint32 context_id )
+{
+ PIPE_RPC_FNS *fns = NULL;
+ PIPE_RPC_FNS *tmp = NULL;
+
+ if ( !list ) {
+ DEBUG(0,("find_pipe_fns_by_context: ERROR! No context list for pipe!\n"));
+ return NULL;
+ }
+
+ for (tmp=list; tmp; tmp=tmp->next ) {
+ if ( tmp->context_id == context_id )
+ break;
+ }
+
+ fns = tmp;
+
+ return fns;
+}
+
+/****************************************************************************
+ Memory cleanup.
+****************************************************************************/
+
+void free_pipe_rpc_context( PIPE_RPC_FNS *list )
+{
+ PIPE_RPC_FNS *tmp = list;
+ PIPE_RPC_FNS *tmp2;
+
+ while (tmp) {
+ tmp2 = tmp->next;
+ SAFE_FREE(tmp);
+ tmp = tmp2;
+ }
+
+ return;
+}
+
+static bool api_rpcTNP(pipes_struct *p, const char *rpc_name,
+ const struct api_struct *api_rpc_cmds, int n_cmds);
+
+/****************************************************************************
+ Find the correct RPC function to call for this request.
+ If the pipe is authenticated then become the correct UNIX user
+ before doing the call.
+****************************************************************************/
+
+bool api_pipe_request(pipes_struct *p)
+{
+ bool ret = False;
+ bool changed_user = False;
+ PIPE_RPC_FNS *pipe_fns;
+
+ if (p->pipe_bound &&
+ ((p->auth.auth_type == PIPE_AUTH_TYPE_NTLMSSP) ||
+ (p->auth.auth_type == PIPE_AUTH_TYPE_SPNEGO_NTLMSSP))) {
+ if(!become_authenticated_pipe_user(p)) {
+ prs_mem_free(&p->out_data.rdata);
+ return False;
+ }
+ changed_user = True;
+ }
+
+ DEBUG(5, ("Requested \\PIPE\\%s\n", p->name));
+
+ /* get the set of RPC functions for this context */
+
+ pipe_fns = find_pipe_fns_by_context(p->contexts, p->hdr_req.context_id);
+
+ if ( pipe_fns ) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ ret = api_rpcTNP(p, p->name, pipe_fns->cmds, pipe_fns->n_cmds);
+ TALLOC_FREE(frame);
+ }
+ else {
+ DEBUG(0,("api_pipe_request: No rpc function table associated with context [%d] on pipe [%s]\n",
+ p->hdr_req.context_id, p->name));
+ }
+
+ if (changed_user) {
+ unbecome_authenticated_pipe_user();
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Calls the underlying RPC function for a named pipe.
+ ********************************************************************/
+
+static bool api_rpcTNP(pipes_struct *p, const char *rpc_name,
+ const struct api_struct *api_rpc_cmds, int n_cmds)
+{
+ int fn_num;
+ fstring name;
+ uint32 offset1, offset2;
+
+ /* interpret the command */
+ DEBUG(4,("api_rpcTNP: %s op 0x%x - ", rpc_name, p->hdr_req.opnum));
+
+ slprintf(name, sizeof(name)-1, "in_%s", rpc_name);
+ prs_dump(name, p->hdr_req.opnum, &p->in_data.data);
+
+ for (fn_num = 0; fn_num < n_cmds; fn_num++) {
+ if (api_rpc_cmds[fn_num].opnum == p->hdr_req.opnum && api_rpc_cmds[fn_num].fn != NULL) {
+ DEBUG(3,("api_rpcTNP: rpc command: %s\n", api_rpc_cmds[fn_num].name));
+ break;
+ }
+ }
+
+ if (fn_num == n_cmds) {
+ /*
+ * For an unknown RPC just return a fault PDU but
+ * return True to allow RPC's on the pipe to continue
+ * and not put the pipe into fault state. JRA.
+ */
+ DEBUG(4, ("unknown\n"));
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ return True;
+ }
+
+ offset1 = prs_offset(&p->out_data.rdata);
+
+ DEBUG(6, ("api_rpc_cmds[%d].fn == %p\n",
+ fn_num, api_rpc_cmds[fn_num].fn));
+ /* do the actual command */
+ if(!api_rpc_cmds[fn_num].fn(p)) {
+ DEBUG(0,("api_rpcTNP: %s: %s failed.\n", rpc_name, api_rpc_cmds[fn_num].name));
+ prs_mem_free(&p->out_data.rdata);
+ return False;
+ }
+
+ if (p->bad_handle_fault_state) {
+ DEBUG(4,("api_rpcTNP: bad handle fault return.\n"));
+ p->bad_handle_fault_state = False;
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_CONTEXT_MISMATCH));
+ return True;
+ }
+
+ if (p->rng_fault_state) {
+ DEBUG(4, ("api_rpcTNP: rng fault return\n"));
+ p->rng_fault_state = False;
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ return True;
+ }
+
+ slprintf(name, sizeof(name)-1, "out_%s", rpc_name);
+ offset2 = prs_offset(&p->out_data.rdata);
+ prs_set_offset(&p->out_data.rdata, offset1);
+ prs_dump(name, p->hdr_req.opnum, &p->out_data.rdata);
+ prs_set_offset(&p->out_data.rdata, offset2);
+
+ DEBUG(5,("api_rpcTNP: called %s successfully\n", rpc_name));
+
+ /* Check for buffer underflow in rpc parsing */
+
+ if ((DEBUGLEVEL >= 10) &&
+ (prs_offset(&p->in_data.data) != prs_data_size(&p->in_data.data))) {
+ size_t data_len = prs_data_size(&p->in_data.data) - prs_offset(&p->in_data.data);
+ char *data = (char *)SMB_MALLOC(data_len);
+
+ DEBUG(10, ("api_rpcTNP: rpc input buffer underflow (parse error?)\n"));
+ if (data) {
+ prs_uint8s(False, "", &p->in_data.data, 0, (unsigned char *)data, (uint32)data_len);
+ SAFE_FREE(data);
+ }
+
+ }
+
+ return True;
+}
diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c
new file mode 100644
index 0000000000..3968d41168
--- /dev/null
+++ b/source3/rpc_server/srv_pipe_hnd.c
@@ -0,0 +1,1249 @@
+/*
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define PIPE "\\PIPE\\"
+#define PIPELEN strlen(PIPE)
+
+static smb_np_struct *chain_p;
+static int pipes_open;
+
+/*
+ * Sometimes I can't decide if I hate Windows printer driver
+ * writers more than I hate the Windows spooler service driver
+ * writers. This gets around a combination of bugs in the spooler
+ * and the HP 8500 PCL driver that causes a spooler spin. JRA.
+ *
+ * bumped up from 20 -> 64 after viewing traffic from WordPerfect
+ * 2002 running on NT 4.- SP6
+ * bumped up from 64 -> 256 after viewing traffic from con2prt
+ * for lots of printers on a WinNT 4.x SP6 box.
+ */
+
+#ifndef MAX_OPEN_SPOOLSS_PIPES
+#define MAX_OPEN_SPOOLSS_PIPES 256
+#endif
+static int current_spoolss_pipes_open;
+
+static smb_np_struct *Pipes;
+static pipes_struct *InternalPipes;
+static struct bitmap *bmap;
+
+/* TODO
+ * the following prototypes are declared here to avoid
+ * code being moved about too much for a patch to be
+ * disrupted / less obvious.
+ *
+ * these functions, and associated functions that they
+ * call, should be moved behind a .so module-loading
+ * system _anyway_. so that's the next step...
+ */
+
+static int close_internal_rpc_pipe_hnd(struct pipes_struct *p);
+
+/****************************************************************************
+ Internal Pipe iterator functions.
+****************************************************************************/
+
+pipes_struct *get_first_internal_pipe(void)
+{
+ return InternalPipes;
+}
+
+pipes_struct *get_next_internal_pipe(pipes_struct *p)
+{
+ return p->next;
+}
+
+/* this must be larger than the sum of the open files and directories */
+static int pipe_handle_offset;
+
+/****************************************************************************
+ Set the pipe_handle_offset. Called from smbd/files.c
+****************************************************************************/
+
+void set_pipe_handle_offset(int max_open_files)
+{
+ if(max_open_files < 0x7000) {
+ pipe_handle_offset = 0x7000;
+ } else {
+ pipe_handle_offset = max_open_files + 10; /* For safety. :-) */
+ }
+}
+
+/****************************************************************************
+ Reset pipe chain handle number.
+****************************************************************************/
+
+void reset_chain_p(void)
+{
+ chain_p = NULL;
+}
+
+/****************************************************************************
+ Initialise pipe handle states.
+****************************************************************************/
+
+void init_rpc_pipe_hnd(void)
+{
+ bmap = bitmap_allocate(MAX_OPEN_PIPES);
+ if (!bmap) {
+ exit_server("out of memory in init_rpc_pipe_hnd");
+ }
+}
+
+/****************************************************************************
+ Initialise an outgoing packet.
+****************************************************************************/
+
+static bool pipe_init_outgoing_data(pipes_struct *p)
+{
+ output_data *o_data = &p->out_data;
+
+ /* Reset the offset counters. */
+ o_data->data_sent_length = 0;
+ o_data->current_pdu_len = 0;
+ o_data->current_pdu_sent = 0;
+
+ memset(o_data->current_pdu, '\0', sizeof(o_data->current_pdu));
+
+ /* Free any memory in the current return data buffer. */
+ prs_mem_free(&o_data->rdata);
+
+ /*
+ * Initialize the outgoing RPC data buffer.
+ * we will use this as the raw data area for replying to rpc requests.
+ */
+ if(!prs_init(&o_data->rdata, RPC_MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("pipe_init_outgoing_data: malloc fail.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Find first available pipe slot.
+****************************************************************************/
+
+smb_np_struct *open_rpc_pipe_p(const char *pipe_name,
+ connection_struct *conn, uint16 vuid)
+{
+ int i;
+ smb_np_struct *p, *p_it;
+ static int next_pipe;
+ bool is_spoolss_pipe = False;
+
+ DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n",
+ pipe_name, pipes_open));
+
+ if (strstr(pipe_name, "spoolss")) {
+ is_spoolss_pipe = True;
+ }
+
+ if (is_spoolss_pipe && current_spoolss_pipes_open >= MAX_OPEN_SPOOLSS_PIPES) {
+ DEBUG(10,("open_rpc_pipe_p: spooler bug workaround. Denying open on pipe %s\n",
+ pipe_name ));
+ return NULL;
+ }
+
+ /* not repeating pipe numbers makes it easier to track things in
+ log files and prevents client bugs where pipe numbers are reused
+ over connection restarts */
+
+ if (next_pipe == 0) {
+ next_pipe = (sys_getpid() ^ time(NULL)) % MAX_OPEN_PIPES;
+ }
+
+ i = bitmap_find(bmap, next_pipe);
+
+ if (i == -1) {
+ DEBUG(0,("ERROR! Out of pipe structures\n"));
+ return NULL;
+ }
+
+ next_pipe = (i+1) % MAX_OPEN_PIPES;
+
+ for (p = Pipes; p; p = p->next) {
+ DEBUG(5,("open_rpc_pipe_p: name %s pnum=%x\n", p->name, p->pnum));
+ }
+
+ p = talloc(NULL, smb_np_struct);
+ if (!p) {
+ DEBUG(0,("ERROR! no memory for smb_np_struct!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(p);
+
+ p->name = talloc_strdup(p, pipe_name);
+ if (p->name == NULL) {
+ TALLOC_FREE(p);
+ DEBUG(0,("ERROR! no memory for pipe name!\n"));
+ return NULL;
+ }
+
+ /* add a dso mechanism instead of this, here */
+
+ p->namedpipe_create = make_internal_rpc_pipe_p;
+ p->namedpipe_read = read_from_internal_pipe;
+ p->namedpipe_write = write_to_internal_pipe;
+
+ p->np_state = p->namedpipe_create(pipe_name, conn->client_address,
+ conn->server_info, vuid);
+
+ if (p->np_state == NULL) {
+ DEBUG(0,("open_rpc_pipe_p: make_internal_rpc_pipe_p failed.\n"));
+ TALLOC_FREE(p);
+ return NULL;
+ }
+
+ DLIST_ADD(Pipes, p);
+
+ /*
+ * Initialize the incoming RPC data buffer with one PDU worth of memory.
+ * We cheat here and say we're marshalling, as we intend to add incoming
+ * data directly into the prs_struct and we want it to auto grow. We will
+ * change the type to UNMARSALLING before processing the stream.
+ */
+
+ bitmap_set(bmap, i);
+ i += pipe_handle_offset;
+
+ pipes_open++;
+
+ p->pnum = i;
+
+ p->open = True;
+ p->device_state = 0;
+ p->priority = 0;
+ p->conn = conn;
+ p->vuid = vuid;
+
+ p->max_trans_reply = 0;
+
+ DEBUG(4,("Opened pipe %s with handle %x (pipes_open=%d)\n",
+ pipe_name, i, pipes_open));
+
+ chain_p = p;
+
+ /* Iterate over p_it as a temp variable, to display all open pipes */
+ for (p_it = Pipes; p_it; p_it = p_it->next) {
+ DEBUG(5,("open pipes: name %s pnum=%x\n", p_it->name, p_it->pnum));
+ }
+
+ return chain_p;
+}
+
+/****************************************************************************
+ Make an internal namedpipes structure
+****************************************************************************/
+
+struct pipes_struct *make_internal_rpc_pipe_p(const char *pipe_name,
+ const char *client_address,
+ struct auth_serversupplied_info *server_info,
+ uint16_t vuid)
+{
+ pipes_struct *p;
+
+ DEBUG(4,("Create pipe requested %s\n", pipe_name));
+
+ p = TALLOC_ZERO_P(NULL, pipes_struct);
+
+ if (!p) {
+ DEBUG(0,("ERROR! no memory for pipes_struct!\n"));
+ return NULL;
+ }
+
+ if ((p->mem_ctx = talloc_init("pipe %s %p", pipe_name, p)) == NULL) {
+ DEBUG(0,("open_rpc_pipe_p: talloc_init failed.\n"));
+ TALLOC_FREE(p);
+ return NULL;
+ }
+
+ if (!init_pipe_handle_list(p, pipe_name)) {
+ DEBUG(0,("open_rpc_pipe_p: init_pipe_handles failed.\n"));
+ talloc_destroy(p->mem_ctx);
+ TALLOC_FREE(p);
+ return NULL;
+ }
+
+ /*
+ * Initialize the incoming RPC data buffer with one PDU worth of memory.
+ * We cheat here and say we're marshalling, as we intend to add incoming
+ * data directly into the prs_struct and we want it to auto grow. We will
+ * change the type to UNMARSALLING before processing the stream.
+ */
+
+ if(!prs_init(&p->in_data.data, RPC_MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) {
+ DEBUG(0,("open_rpc_pipe_p: malloc fail for in_data struct.\n"));
+ talloc_destroy(p->mem_ctx);
+ close_policy_by_pipe(p);
+ TALLOC_FREE(p);
+ return NULL;
+ }
+
+ p->server_info = copy_serverinfo(p, server_info);
+ if (p->server_info == NULL) {
+ DEBUG(0, ("open_rpc_pipe_p: copy_serverinfo failed\n"));
+ talloc_destroy(p->mem_ctx);
+ close_policy_by_pipe(p);
+ TALLOC_FREE(p);
+ return NULL;
+ }
+
+ DLIST_ADD(InternalPipes, p);
+
+ memcpy(p->client_address, client_address, sizeof(p->client_address));
+
+ p->endian = RPC_LITTLE_ENDIAN;
+
+ ZERO_STRUCT(p->pipe_user);
+
+ p->pipe_user.vuid = vuid;
+ p->pipe_user.ut.uid = (uid_t)-1;
+ p->pipe_user.ut.gid = (gid_t)-1;
+ p->pipe_user.nt_user_token = dup_nt_token(NULL, server_info->ptok);
+
+ /*
+ * Initialize the outgoing RPC data buffer with no memory.
+ */
+ prs_init_empty(&p->out_data.rdata, p->mem_ctx, MARSHALL);
+
+ fstrcpy(p->name, pipe_name);
+
+ DEBUG(4,("Created internal pipe %s (pipes_open=%d)\n",
+ pipe_name, pipes_open));
+
+ talloc_set_destructor(p, close_internal_rpc_pipe_hnd);
+
+ return p;
+}
+
+/****************************************************************************
+ Sets the fault state on incoming packets.
+****************************************************************************/
+
+static void set_incoming_fault(pipes_struct *p)
+{
+ prs_mem_free(&p->in_data.data);
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+ p->fault_state = True;
+ DEBUG(10, ("set_incoming_fault: Setting fault state on pipe %s\n",
+ p->name));
+}
+
+/****************************************************************************
+ Ensures we have at least RPC_HEADER_LEN amount of data in the incoming buffer.
+****************************************************************************/
+
+static ssize_t fill_rpc_header(pipes_struct *p, char *data, size_t data_to_copy)
+{
+ size_t len_needed_to_complete_hdr = MIN(data_to_copy, RPC_HEADER_LEN - p->in_data.pdu_received_len);
+
+ DEBUG(10,("fill_rpc_header: data_to_copy = %u, len_needed_to_complete_hdr = %u, receive_len = %u\n",
+ (unsigned int)data_to_copy, (unsigned int)len_needed_to_complete_hdr,
+ (unsigned int)p->in_data.pdu_received_len ));
+
+ memcpy((char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, len_needed_to_complete_hdr);
+ p->in_data.pdu_received_len += len_needed_to_complete_hdr;
+
+ return (ssize_t)len_needed_to_complete_hdr;
+}
+
+/****************************************************************************
+ Unmarshalls a new PDU header. Assumes the raw header data is in current_in_pdu.
+****************************************************************************/
+
+static ssize_t unmarshall_rpc_header(pipes_struct *p)
+{
+ /*
+ * Unmarshall the header to determine the needed length.
+ */
+
+ prs_struct rpc_in;
+
+ if(p->in_data.pdu_received_len != RPC_HEADER_LEN) {
+ DEBUG(0,("unmarshall_rpc_header: assert on rpc header length failed.\n"));
+ set_incoming_fault(p);
+ return -1;
+ }
+
+ prs_init_empty( &rpc_in, p->mem_ctx, UNMARSHALL);
+ prs_set_endian_data( &rpc_in, p->endian);
+
+ prs_give_memory( &rpc_in, (char *)&p->in_data.current_in_pdu[0],
+ p->in_data.pdu_received_len, False);
+
+ /*
+ * Unmarshall the header as this will tell us how much
+ * data we need to read to get the complete pdu.
+ * This also sets the endian flag in rpc_in.
+ */
+
+ if(!smb_io_rpc_hdr("", &p->hdr, &rpc_in, 0)) {
+ DEBUG(0,("unmarshall_rpc_header: failed to unmarshall RPC_HDR.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * Validate the RPC header.
+ */
+
+ if(p->hdr.major != 5 && p->hdr.minor != 0) {
+ DEBUG(0,("unmarshall_rpc_header: invalid major/minor numbers in RPC_HDR.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * If there's not data in the incoming buffer this should be the start of a new RPC.
+ */
+
+ if(prs_offset(&p->in_data.data) == 0) {
+
+ /*
+ * AS/U doesn't set FIRST flag in a BIND packet it seems.
+ */
+
+ if ((p->hdr.pkt_type == RPC_REQUEST) && !(p->hdr.flags & RPC_FLG_FIRST)) {
+ /*
+ * Ensure that the FIRST flag is set. If not then we have
+ * a stream missmatch.
+ */
+
+ DEBUG(0,("unmarshall_rpc_header: FIRST flag not set in first PDU !\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ /*
+ * If this is the first PDU then set the endianness
+ * flag in the pipe. We will need this when parsing all
+ * data in this RPC.
+ */
+
+ p->endian = rpc_in.bigendian_data;
+
+ DEBUG(5,("unmarshall_rpc_header: using %sendian RPC\n",
+ p->endian == RPC_LITTLE_ENDIAN ? "little-" : "big-" ));
+
+ } else {
+
+ /*
+ * If this is *NOT* the first PDU then check the endianness
+ * flag in the pipe is the same as that in the PDU.
+ */
+
+ if (p->endian != rpc_in.bigendian_data) {
+ DEBUG(0,("unmarshall_rpc_header: FIRST endianness flag (%d) different in next PDU !\n", (int)p->endian));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+ }
+
+ /*
+ * Ensure that the pdu length is sane.
+ */
+
+ if((p->hdr.frag_len < RPC_HEADER_LEN) || (p->hdr.frag_len > RPC_MAX_PDU_FRAG_LEN)) {
+ DEBUG(0,("unmarshall_rpc_header: assert on frag length failed.\n"));
+ set_incoming_fault(p);
+ prs_mem_free(&rpc_in);
+ return -1;
+ }
+
+ DEBUG(10,("unmarshall_rpc_header: type = %u, flags = %u\n", (unsigned int)p->hdr.pkt_type,
+ (unsigned int)p->hdr.flags ));
+
+ p->in_data.pdu_needed_len = (uint32)p->hdr.frag_len - RPC_HEADER_LEN;
+
+ prs_mem_free(&rpc_in);
+
+ return 0; /* No extra data processed. */
+}
+
+/****************************************************************************
+ Call this to free any talloc'ed memory. Do this before and after processing
+ a complete PDU.
+****************************************************************************/
+
+static void free_pipe_context(pipes_struct *p)
+{
+ if (p->mem_ctx) {
+ DEBUG(3,("free_pipe_context: destroying talloc pool of size "
+ "%lu\n", (unsigned long)talloc_total_size(p->mem_ctx) ));
+ talloc_free_children(p->mem_ctx);
+ } else {
+ p->mem_ctx = talloc_init("pipe %s %p", p->name, p);
+ if (p->mem_ctx == NULL) {
+ p->fault_state = True;
+ }
+ }
+}
+
+/****************************************************************************
+ Processes a request pdu. This will do auth processing if needed, and
+ appends the data into the complete stream if the LAST flag is not set.
+****************************************************************************/
+
+static bool process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p)
+{
+ uint32 ss_padding_len = 0;
+ size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN -
+ (p->hdr.auth_len ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len;
+
+ if(!p->pipe_bound) {
+ DEBUG(0,("process_request_pdu: rpc request with no bind.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Check if we need to do authentication processing.
+ * This is only done on requests, not binds.
+ */
+
+ /*
+ * Read the RPC request header.
+ */
+
+ if(!smb_io_rpc_hdr_req("req", &p->hdr_req, rpc_in_p, 0)) {
+ DEBUG(0,("process_request_pdu: failed to unmarshall RPC_HDR_REQ.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ switch(p->auth.auth_type) {
+ case PIPE_AUTH_TYPE_NONE:
+ break;
+
+ case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
+ case PIPE_AUTH_TYPE_NTLMSSP:
+ {
+ NTSTATUS status;
+ if(!api_pipe_ntlmssp_auth_process(p, rpc_in_p, &ss_padding_len, &status)) {
+ DEBUG(0,("process_request_pdu: failed to do auth processing.\n"));
+ DEBUG(0,("process_request_pdu: error was %s.\n", nt_errstr(status) ));
+ set_incoming_fault(p);
+ return False;
+ }
+ break;
+ }
+
+ case PIPE_AUTH_TYPE_SCHANNEL:
+ if (!api_pipe_schannel_process(p, rpc_in_p, &ss_padding_len)) {
+ DEBUG(3,("process_request_pdu: failed to do schannel processing.\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+ break;
+
+ default:
+ DEBUG(0,("process_request_pdu: unknown auth type %u set.\n", (unsigned int)p->auth.auth_type ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /* Now we've done the sign/seal we can remove any padding data. */
+ if (data_len > ss_padding_len) {
+ data_len -= ss_padding_len;
+ }
+
+ /*
+ * Check the data length doesn't go over the 15Mb limit.
+ * increased after observing a bug in the Windows NT 4.0 SP6a
+ * spoolsv.exe when the response to a GETPRINTERDRIVER2 RPC
+ * will not fit in the initial buffer of size 0x1068 --jerry 22/01/2002
+ */
+
+ if(prs_offset(&p->in_data.data) + data_len > 15*1024*1024) {
+ DEBUG(0,("process_request_pdu: rpc data buffer too large (%u) + (%u)\n",
+ (unsigned int)prs_data_size(&p->in_data.data), (unsigned int)data_len ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Append the data portion into the buffer and return.
+ */
+
+ if(!prs_append_some_prs_data(&p->in_data.data, rpc_in_p, prs_offset(rpc_in_p), data_len)) {
+ DEBUG(0,("process_request_pdu: Unable to append data size %u to parse buffer of size %u.\n",
+ (unsigned int)data_len, (unsigned int)prs_data_size(&p->in_data.data) ));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ if(p->hdr.flags & RPC_FLG_LAST) {
+ bool ret = False;
+ /*
+ * Ok - we finally have a complete RPC stream.
+ * Call the rpc command to process it.
+ */
+
+ /*
+ * Ensure the internal prs buffer size is *exactly* the same
+ * size as the current offset.
+ */
+
+ if(!prs_set_buffer_size(&p->in_data.data, prs_offset(&p->in_data.data))) {
+ DEBUG(0,("process_request_pdu: Call to prs_set_buffer_size failed!\n"));
+ set_incoming_fault(p);
+ return False;
+ }
+
+ /*
+ * Set the parse offset to the start of the data and set the
+ * prs_struct to UNMARSHALL.
+ */
+
+ prs_set_offset(&p->in_data.data, 0);
+ prs_switch_type(&p->in_data.data, UNMARSHALL);
+
+ /*
+ * Process the complete data stream here.
+ */
+
+ free_pipe_context(p);
+
+ if(pipe_init_outgoing_data(p)) {
+ ret = api_pipe_request(p);
+ }
+
+ free_pipe_context(p);
+
+ /*
+ * We have consumed the whole data stream. Set back to
+ * marshalling and set the offset back to the start of
+ * the buffer to re-use it (we could also do a prs_mem_free()
+ * and then re_init on the next start of PDU. Not sure which
+ * is best here.... JRA.
+ */
+
+ prs_switch_type(&p->in_data.data, MARSHALL);
+ prs_set_offset(&p->in_data.data, 0);
+ return ret;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Processes a finished PDU stored in current_in_pdu. The RPC_HEADER has
+ already been parsed and stored in p->hdr.
+****************************************************************************/
+
+static void process_complete_pdu(pipes_struct *p)
+{
+ prs_struct rpc_in;
+ size_t data_len = p->in_data.pdu_received_len - RPC_HEADER_LEN;
+ char *data_p = (char *)&p->in_data.current_in_pdu[RPC_HEADER_LEN];
+ bool reply = False;
+
+ if(p->fault_state) {
+ DEBUG(10,("process_complete_pdu: pipe %s in fault state.\n",
+ p->name ));
+ set_incoming_fault(p);
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ return;
+ }
+
+ prs_init_empty( &rpc_in, p->mem_ctx, UNMARSHALL);
+
+ /*
+ * Ensure we're using the corrent endianness for both the
+ * RPC header flags and the raw data we will be reading from.
+ */
+
+ prs_set_endian_data( &rpc_in, p->endian);
+ prs_set_endian_data( &p->in_data.data, p->endian);
+
+ prs_give_memory( &rpc_in, data_p, (uint32)data_len, False);
+
+ DEBUG(10,("process_complete_pdu: processing packet type %u\n",
+ (unsigned int)p->hdr.pkt_type ));
+
+ switch (p->hdr.pkt_type) {
+ case RPC_REQUEST:
+ reply = process_request_pdu(p, &rpc_in);
+ break;
+
+ case RPC_PING: /* CL request - ignore... */
+ DEBUG(0,("process_complete_pdu: Error. Connectionless packet type %u received on pipe %s.\n",
+ (unsigned int)p->hdr.pkt_type, p->name));
+ break;
+
+ case RPC_RESPONSE: /* No responses here. */
+ DEBUG(0,("process_complete_pdu: Error. RPC_RESPONSE received from client on pipe %s.\n",
+ p->name ));
+ break;
+
+ case RPC_FAULT:
+ case RPC_WORKING: /* CL request - reply to a ping when a call in process. */
+ case RPC_NOCALL: /* CL - server reply to a ping call. */
+ case RPC_REJECT:
+ case RPC_ACK:
+ case RPC_CL_CANCEL:
+ case RPC_FACK:
+ case RPC_CANCEL_ACK:
+ DEBUG(0,("process_complete_pdu: Error. Connectionless packet type %u received on pipe %s.\n",
+ (unsigned int)p->hdr.pkt_type, p->name));
+ break;
+
+ case RPC_BIND:
+ /*
+ * We assume that a pipe bind is only in one pdu.
+ */
+ if(pipe_init_outgoing_data(p)) {
+ reply = api_pipe_bind_req(p, &rpc_in);
+ }
+ break;
+
+ case RPC_BINDACK:
+ case RPC_BINDNACK:
+ DEBUG(0,("process_complete_pdu: Error. RPC_BINDACK/RPC_BINDNACK packet type %u received on pipe %s.\n",
+ (unsigned int)p->hdr.pkt_type, p->name));
+ break;
+
+
+ case RPC_ALTCONT:
+ /*
+ * We assume that a pipe bind is only in one pdu.
+ */
+ if(pipe_init_outgoing_data(p)) {
+ reply = api_pipe_alter_context(p, &rpc_in);
+ }
+ break;
+
+ case RPC_ALTCONTRESP:
+ DEBUG(0,("process_complete_pdu: Error. RPC_ALTCONTRESP on pipe %s: Should only be server -> client.\n",
+ p->name));
+ break;
+
+ case RPC_AUTH3:
+ /*
+ * The third packet in an NTLMSSP auth exchange.
+ */
+ if(pipe_init_outgoing_data(p)) {
+ reply = api_pipe_bind_auth3(p, &rpc_in);
+ }
+ break;
+
+ case RPC_SHUTDOWN:
+ DEBUG(0,("process_complete_pdu: Error. RPC_SHUTDOWN on pipe %s: Should only be server -> client.\n",
+ p->name));
+ break;
+
+ case RPC_CO_CANCEL:
+ /* For now just free all client data and continue processing. */
+ DEBUG(3,("process_complete_pdu: RPC_ORPHANED. Abandoning rpc call.\n"));
+ /* As we never do asynchronous RPC serving, we can never cancel a
+ call (as far as I know). If we ever did we'd have to send a cancel_ack
+ reply. For now, just free all client data and continue processing. */
+ reply = True;
+ break;
+#if 0
+ /* Enable this if we're doing async rpc. */
+ /* We must check the call-id matches the outstanding callid. */
+ if(pipe_init_outgoing_data(p)) {
+ /* Send a cancel_ack PDU reply. */
+ /* We should probably check the auth-verifier here. */
+ reply = setup_cancel_ack_reply(p, &rpc_in);
+ }
+ break;
+#endif
+
+ case RPC_ORPHANED:
+ /* We should probably check the auth-verifier here.
+ For now just free all client data and continue processing. */
+ DEBUG(3,("process_complete_pdu: RPC_ORPHANED. Abandoning rpc call.\n"));
+ reply = True;
+ break;
+
+ default:
+ DEBUG(0,("process_complete_pdu: Unknown rpc type = %u received.\n", (unsigned int)p->hdr.pkt_type ));
+ break;
+ }
+
+ /* Reset to little endian. Probably don't need this but it won't hurt. */
+ prs_set_endian_data( &p->in_data.data, RPC_LITTLE_ENDIAN);
+
+ if (!reply) {
+ DEBUG(3,("process_complete_pdu: DCE/RPC fault sent on pipe %s\n", p->pipe_srv_name));
+ set_incoming_fault(p);
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_OP_RNG_ERROR));
+ prs_mem_free(&rpc_in);
+ } else {
+ /*
+ * Reset the lengths. We're ready for a new pdu.
+ */
+ p->in_data.pdu_needed_len = 0;
+ p->in_data.pdu_received_len = 0;
+ }
+
+ prs_mem_free(&rpc_in);
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe. Processes the data in pdu sized units.
+****************************************************************************/
+
+static ssize_t process_incoming_data(pipes_struct *p, char *data, size_t n)
+{
+ size_t data_to_copy = MIN(n, RPC_MAX_PDU_FRAG_LEN - p->in_data.pdu_received_len);
+
+ DEBUG(10,("process_incoming_data: Start: pdu_received_len = %u, pdu_needed_len = %u, incoming data = %u\n",
+ (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len,
+ (unsigned int)n ));
+
+ if(data_to_copy == 0) {
+ /*
+ * This is an error - data is being received and there is no
+ * space in the PDU. Free the received data and go into the fault state.
+ */
+ DEBUG(0,("process_incoming_data: No space in incoming pdu buffer. Current size = %u \
+incoming data size = %u\n", (unsigned int)p->in_data.pdu_received_len, (unsigned int)n ));
+ set_incoming_fault(p);
+ return -1;
+ }
+
+ /*
+ * If we have no data already, wait until we get at least a RPC_HEADER_LEN
+ * number of bytes before we can do anything.
+ */
+
+ if((p->in_data.pdu_needed_len == 0) && (p->in_data.pdu_received_len < RPC_HEADER_LEN)) {
+ /*
+ * Always return here. If we have more data then the RPC_HEADER
+ * will be processed the next time around the loop.
+ */
+ return fill_rpc_header(p, data, data_to_copy);
+ }
+
+ /*
+ * At this point we know we have at least an RPC_HEADER_LEN amount of data
+ * stored in current_in_pdu.
+ */
+
+ /*
+ * If pdu_needed_len is zero this is a new pdu.
+ * Unmarshall the header so we know how much more
+ * data we need, then loop again.
+ */
+
+ if(p->in_data.pdu_needed_len == 0) {
+ ssize_t rret = unmarshall_rpc_header(p);
+ if (rret == -1 || p->in_data.pdu_needed_len > 0) {
+ return rret;
+ }
+ /* If rret == 0 and pdu_needed_len == 0 here we have a PDU that consists
+ of an RPC_HEADER only. This is a RPC_SHUTDOWN, RPC_CO_CANCEL or RPC_ORPHANED
+ pdu type. Deal with this in process_complete_pdu(). */
+ }
+
+ /*
+ * Ok - at this point we have a valid RPC_HEADER in p->hdr.
+ * Keep reading until we have a full pdu.
+ */
+
+ data_to_copy = MIN(data_to_copy, p->in_data.pdu_needed_len);
+
+ /*
+ * Copy as much of the data as we need into the current_in_pdu buffer.
+ * pdu_needed_len becomes zero when we have a complete pdu.
+ */
+
+ memcpy( (char *)&p->in_data.current_in_pdu[p->in_data.pdu_received_len], data, data_to_copy);
+ p->in_data.pdu_received_len += data_to_copy;
+ p->in_data.pdu_needed_len -= data_to_copy;
+
+ /*
+ * Do we have a complete PDU ?
+ * (return the number of bytes handled in the call)
+ */
+
+ if(p->in_data.pdu_needed_len == 0) {
+ process_complete_pdu(p);
+ return data_to_copy;
+ }
+
+ DEBUG(10,("process_incoming_data: not a complete PDU yet. pdu_received_len = %u, pdu_needed_len = %u\n",
+ (unsigned int)p->in_data.pdu_received_len, (unsigned int)p->in_data.pdu_needed_len ));
+
+ return (ssize_t)data_to_copy;
+}
+
+/****************************************************************************
+ Accepts incoming data on an rpc pipe.
+****************************************************************************/
+
+ssize_t write_to_pipe(smb_np_struct *p, char *data, size_t n)
+{
+ DEBUG(6,("write_to_pipe: %x", p->pnum));
+
+ DEBUG(6,(" name: %s open: %s len: %d\n",
+ p->name, BOOLSTR(p->open), (int)n));
+
+ dump_data(50, (uint8 *)data, n);
+
+ return p->namedpipe_write(p->np_state, data, n);
+}
+
+/****************************************************************************
+ Accepts incoming data on an internal rpc pipe.
+****************************************************************************/
+
+ssize_t write_to_internal_pipe(struct pipes_struct *p, char *data, size_t n)
+{
+ size_t data_left = n;
+
+ while(data_left) {
+ ssize_t data_used;
+
+ DEBUG(10,("write_to_pipe: data_left = %u\n", (unsigned int)data_left ));
+
+ data_used = process_incoming_data(p, data, data_left);
+
+ DEBUG(10,("write_to_pipe: data_used = %d\n", (int)data_used ));
+
+ if(data_used < 0) {
+ return -1;
+ }
+
+ data_left -= data_used;
+ data += data_used;
+ }
+
+ return n;
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+ssize_t read_from_pipe(smb_np_struct *p, char *data, size_t n,
+ bool *is_data_outstanding)
+{
+ if (!p || !p->open) {
+ DEBUG(0,("read_from_pipe: pipe not open\n"));
+ return -1;
+ }
+
+ DEBUG(6,("read_from_pipe: %x", p->pnum));
+
+ return p->namedpipe_read(p->np_state, data, n, is_data_outstanding);
+}
+
+/****************************************************************************
+ Replies to a request to read data from a pipe.
+
+ Headers are interspersed with the data at PDU intervals. By the time
+ this function is called, the start of the data could possibly have been
+ read by an SMBtrans (file_offset != 0).
+
+ Calling create_rpc_reply() here is a hack. The data should already
+ have been prepared into arrays of headers + data stream sections.
+****************************************************************************/
+
+ssize_t read_from_internal_pipe(struct pipes_struct *p, char *data, size_t n,
+ bool *is_data_outstanding)
+{
+ uint32 pdu_remaining = 0;
+ ssize_t data_returned = 0;
+
+ if (!p) {
+ DEBUG(0,("read_from_pipe: pipe not open\n"));
+ return -1;
+ }
+
+ DEBUG(6,(" name: %s len: %u\n", p->name, (unsigned int)n));
+
+ /*
+ * We cannot return more than one PDU length per
+ * read request.
+ */
+
+ /*
+ * This condition should result in the connection being closed.
+ * Netapp filers seem to set it to 0xffff which results in domain
+ * authentications failing. Just ignore it so things work.
+ */
+
+ if(n > RPC_MAX_PDU_FRAG_LEN) {
+ DEBUG(5,("read_from_pipe: too large read (%u) requested on \
+pipe %s. We can only service %d sized reads.\n", (unsigned int)n, p->name, RPC_MAX_PDU_FRAG_LEN ));
+ n = RPC_MAX_PDU_FRAG_LEN;
+ }
+
+ /*
+ * Determine if there is still data to send in the
+ * pipe PDU buffer. Always send this first. Never
+ * send more than is left in the current PDU. The
+ * client should send a new read request for a new
+ * PDU.
+ */
+
+ if((pdu_remaining = p->out_data.current_pdu_len - p->out_data.current_pdu_sent) > 0) {
+ data_returned = (ssize_t)MIN(n, pdu_remaining);
+
+ DEBUG(10,("read_from_pipe: %s: current_pdu_len = %u, current_pdu_sent = %u \
+returning %d bytes.\n", p->name, (unsigned int)p->out_data.current_pdu_len,
+ (unsigned int)p->out_data.current_pdu_sent, (int)data_returned));
+
+ memcpy( data, &p->out_data.current_pdu[p->out_data.current_pdu_sent], (size_t)data_returned);
+ p->out_data.current_pdu_sent += (uint32)data_returned;
+ goto out;
+ }
+
+ /*
+ * At this point p->current_pdu_len == p->current_pdu_sent (which
+ * may of course be zero if this is the first return fragment.
+ */
+
+ DEBUG(10,("read_from_pipe: %s: fault_state = %d : data_sent_length \
+= %u, prs_offset(&p->out_data.rdata) = %u.\n",
+ p->name, (int)p->fault_state, (unsigned int)p->out_data.data_sent_length, (unsigned int)prs_offset(&p->out_data.rdata) ));
+
+ if(p->out_data.data_sent_length >= prs_offset(&p->out_data.rdata)) {
+ /*
+ * We have sent all possible data, return 0.
+ */
+ data_returned = 0;
+ goto out;
+ }
+
+ /*
+ * We need to create a new PDU from the data left in p->rdata.
+ * Create the header/data/footers. This also sets up the fields
+ * p->current_pdu_len, p->current_pdu_sent, p->data_sent_length
+ * and stores the outgoing PDU in p->current_pdu.
+ */
+
+ if(!create_next_pdu(p)) {
+ DEBUG(0,("read_from_pipe: %s: create_next_pdu failed.\n", p->name));
+ return -1;
+ }
+
+ data_returned = MIN(n, p->out_data.current_pdu_len);
+
+ memcpy( data, p->out_data.current_pdu, (size_t)data_returned);
+ p->out_data.current_pdu_sent += (uint32)data_returned;
+
+ out:
+
+ (*is_data_outstanding) = p->out_data.current_pdu_len > n;
+ return data_returned;
+}
+
+/****************************************************************************
+ Wait device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+bool wait_rpc_pipe_hnd_state(smb_np_struct *p, uint16 priority)
+{
+ if (p == NULL) {
+ return False;
+ }
+
+ if (p->open) {
+ DEBUG(3,("wait_rpc_pipe_hnd_state: Setting pipe wait state priority=%x on pipe (name=%s)\n",
+ priority, p->name));
+
+ p->priority = priority;
+
+ return True;
+ }
+
+ DEBUG(3,("wait_rpc_pipe_hnd_state: Error setting pipe wait state priority=%x (name=%s)\n",
+ priority, p->name));
+ return False;
+}
+
+
+/****************************************************************************
+ Set device state on a pipe. Exactly what this is for is unknown...
+****************************************************************************/
+
+bool set_rpc_pipe_hnd_state(smb_np_struct *p, uint16 device_state)
+{
+ if (p == NULL) {
+ return False;
+ }
+
+ if (p->open) {
+ DEBUG(3,("set_rpc_pipe_hnd_state: Setting pipe device state=%x on pipe (name=%s)\n",
+ device_state, p->name));
+
+ p->device_state = device_state;
+
+ return True;
+ }
+
+ DEBUG(3,("set_rpc_pipe_hnd_state: Error setting pipe device state=%x (name=%s)\n",
+ device_state, p->name));
+ return False;
+}
+
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+bool close_rpc_pipe_hnd(smb_np_struct *p)
+{
+ if (!p) {
+ DEBUG(0,("Invalid pipe in close_rpc_pipe_hnd\n"));
+ return False;
+ }
+
+ TALLOC_FREE(p->np_state);
+
+ bitmap_clear(bmap, p->pnum - pipe_handle_offset);
+
+ pipes_open--;
+
+ DEBUG(4,("closed pipe name %s pnum=%x (pipes_open=%d)\n",
+ p->name, p->pnum, pipes_open));
+
+ DLIST_REMOVE(Pipes, p);
+
+ /* TODO: Remove from pipe open db */
+
+ if ( !delete_pipe_opendb( p ) ) {
+ DEBUG(3,("close_rpc_pipe_hnd: failed to delete %s "
+ "pipe from open db.\n", p->name));
+ }
+
+ TALLOC_FREE(p);
+
+ return True;
+}
+
+/****************************************************************************
+ Close all pipes on a connection.
+****************************************************************************/
+
+void pipe_close_conn(connection_struct *conn)
+{
+ smb_np_struct *p, *next;
+
+ for (p=Pipes;p;p=next) {
+ next = p->next;
+ if (p->conn == conn) {
+ close_rpc_pipe_hnd(p);
+ }
+ }
+}
+
+/****************************************************************************
+ Close an rpc pipe.
+****************************************************************************/
+
+static int close_internal_rpc_pipe_hnd(struct pipes_struct *p)
+{
+ if (!p) {
+ DEBUG(0,("Invalid pipe in close_internal_rpc_pipe_hnd\n"));
+ return False;
+ }
+
+ prs_mem_free(&p->out_data.rdata);
+ prs_mem_free(&p->in_data.data);
+
+ if (p->auth.auth_data_free_func) {
+ (*p->auth.auth_data_free_func)(&p->auth);
+ }
+
+ if (p->mem_ctx) {
+ talloc_destroy(p->mem_ctx);
+ }
+
+ free_pipe_rpc_context( p->contexts );
+
+ /* Free the handles database. */
+ close_policy_by_pipe(p);
+
+ TALLOC_FREE(p->pipe_user.nt_user_token);
+ SAFE_FREE(p->pipe_user.ut.groups);
+
+ DLIST_REMOVE(InternalPipes, p);
+
+ ZERO_STRUCTP(p);
+
+ TALLOC_FREE(p);
+
+ return True;
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle in a buffer and an offset.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe_p(uint16 pnum)
+{
+ if (chain_p) {
+ return chain_p;
+ }
+
+ return get_rpc_pipe(pnum);
+}
+
+/****************************************************************************
+ Find an rpc pipe given a pipe handle.
+****************************************************************************/
+
+smb_np_struct *get_rpc_pipe(int pnum)
+{
+ smb_np_struct *p;
+
+ DEBUG(4,("search for pipe pnum=%x\n", pnum));
+
+ for (p=Pipes;p;p=p->next) {
+ DEBUG(5,("pipe name %s pnum=%x (pipes_open=%d)\n",
+ p->name, p->pnum, pipes_open));
+ }
+
+ for (p=Pipes;p;p=p->next) {
+ if (p->pnum == pnum) {
+ chain_p = p;
+ return p;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c
new file mode 100644
index 0000000000..1b1e98c049
--- /dev/null
+++ b/source3/rpc_server/srv_samr_nt.c
@@ -0,0 +1,5956 @@
+/*
+ * 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-2005,
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#define SAMR_USR_RIGHTS_WRITE_PW \
+ ( READ_CONTROL_ACCESS | \
+ SA_RIGHT_USER_CHANGE_PASSWORD | \
+ SA_RIGHT_USER_SET_LOC_COM )
+#define SAMR_USR_RIGHTS_CANT_WRITE_PW \
+ ( READ_CONTROL_ACCESS | SA_RIGHT_USER_SET_LOC_COM )
+
+#define DISP_INFO_CACHE_TIMEOUT 10
+
+typedef struct disp_info {
+ DOM_SID sid; /* identify which domain this is. */
+ bool builtin_domain; /* Quick flag to check if this is the builtin domain. */
+ 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 */
+
+ uint16 enum_acb_mask;
+ struct pdb_search *enum_users; /* enumusers with a mask */
+
+ struct timed_event *cache_timeout_event; /* cache idle timeout
+ * handler. */
+} DISP_INFO;
+
+/* We keep a static list of these by SID as modern clients close down
+ all resources between each request in a complete enumeration. */
+
+struct samr_info {
+ /* for use by the \PIPE\samr policy */
+ DOM_SID sid;
+ bool builtin_domain; /* Quick flag to check if this is the builtin domain. */
+ uint32 status; /* some sort of flag. best to record it. comes from opnum 0x39 */
+ uint32 acc_granted;
+ DISP_INFO *disp_info;
+ TALLOC_CTX *mem_ctx;
+};
+
+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 & ~SA_RIGHT_USER_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 make_samr_object_sd( TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size,
+ const struct generic_mapping *map,
+ DOM_SID *sid, uint32 sid_access )
+{
+ DOM_SID domadmin_sid;
+ SEC_ACE ace[5]; /* at most 5 entries */
+ SEC_ACCESS mask;
+ size_t i = 0;
+
+ SEC_ACL *psa = NULL;
+
+ /* basic access for Everyone */
+
+ init_sec_access(&mask, map->generic_execute | map->generic_read );
+ init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /* add Full Access 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */
+
+ init_sec_access(&mask, map->generic_all);
+
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Account_Operators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /* Add Full Access for Domain Admins if we are a DC */
+
+ if ( IS_DC ) {
+ sid_copy( &domadmin_sid, get_global_sam_sid() );
+ sid_append_rid( &domadmin_sid, DOMAIN_GROUP_RID_ADMINS );
+ init_sec_ace(&ace[i++], &domadmin_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+ }
+
+ /* if we have a sid, give it some special access */
+
+ if ( sid ) {
+ init_sec_access( &mask, sid_access );
+ init_sec_ace(&ace[i++], sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 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;
+}
+
+/*******************************************************************
+ Checks if access to an object should be granted, and returns that
+ level of access for further checks.
+********************************************************************/
+
+static NTSTATUS access_check_samr_object( SEC_DESC *psd, NT_USER_TOKEN *token,
+ SE_PRIV *rights, uint32 rights_mask,
+ uint32 des_access, uint32 *acc_granted,
+ const char *debug )
+{
+ NTSTATUS status = NT_STATUS_ACCESS_DENIED;
+ uint32 saved_mask = 0;
+
+ /* check privileges; certain SAM access bits should be overridden
+ by privileges (mostly having to do with creating/modifying/deleting
+ users and groups) */
+
+ if ( rights && user_has_any_privilege( token, rights ) ) {
+
+ saved_mask = (des_access & rights_mask);
+ des_access &= ~saved_mask;
+
+ DEBUG(4,("access_check_samr_object: user rights access mask [0x%x]\n",
+ rights_mask));
+ }
+
+
+ /* check the security descriptor first */
+
+ if ( se_access_check(psd, token, des_access, acc_granted, &status) )
+ goto done;
+
+ /* give root a free pass */
+
+ if ( geteuid() == sec_initial_uid() ) {
+
+ DEBUG(4,("%s: ACCESS should be DENIED (requested: %#010x)\n", debug, des_access));
+ DEBUGADD(4,("but overritten by euid == sec_initial_uid()\n"));
+
+ *acc_granted = des_access;
+
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+
+done:
+ /* add in any bits saved during the privilege check (only
+ matters is 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;
+}
+
+/*******************************************************************
+ Checks if access to a function can be granted
+********************************************************************/
+
+static NTSTATUS access_check_samr_function(uint32 acc_granted, uint32 acc_required, const char *debug)
+{
+ DEBUG(5,("%s: access check ((granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+
+ /* check the security descriptor first */
+
+ if ( (acc_granted&acc_required) == acc_required )
+ return NT_STATUS_OK;
+
+ /* give root a free pass */
+
+ if (geteuid() == sec_initial_uid()) {
+
+ DEBUG(4,("%s: ACCESS should be DENIED (granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+ DEBUGADD(4,("but overwritten by euid == 0\n"));
+
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2,("%s: ACCESS DENIED (granted: %#010x; required: %#010x)\n",
+ debug, acc_granted, acc_required));
+
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*******************************************************************
+ Fetch or create a dispinfo struct.
+********************************************************************/
+
+static DISP_INFO *get_samr_dispinfo_by_sid(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.
+ */
+ sid_copy(&builtin_dispinfo.sid, &global_sid_Builtin);
+
+ return &builtin_dispinfo;
+ }
+
+ if (sid_check_is_domain(psid) || sid_check_is_in_our_domain(psid)) {
+ /*
+ * Necessary only once, but it does not really hurt.
+ */
+ sid_copy(&domain_dispinfo.sid, get_global_sam_sid());
+
+ return &domain_dispinfo;
+ }
+
+ return NULL;
+}
+
+/*******************************************************************
+ Create a samr_info struct.
+********************************************************************/
+
+static struct samr_info *get_samr_info_by_sid(DOM_SID *psid)
+{
+ struct samr_info *info;
+ fstring sid_str;
+ TALLOC_CTX *mem_ctx;
+
+ if (psid) {
+ sid_to_fstring(sid_str, psid);
+ } else {
+ fstrcpy(sid_str,"(NULL)");
+ }
+
+ mem_ctx = talloc_init("samr_info for domain sid %s", sid_str);
+
+ if ((info = TALLOC_ZERO_P(mem_ctx, struct samr_info)) == NULL)
+ return NULL;
+
+ DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_str));
+ if (psid) {
+ sid_copy( &info->sid, psid);
+ info->builtin_domain = sid_check_is_builtin(psid);
+ } else {
+ DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n"));
+ info->builtin_domain = False;
+ }
+ info->mem_ctx = mem_ctx;
+
+ info->disp_info = get_samr_dispinfo_by_sid(psid);
+
+ return info;
+}
+
+/*******************************************************************
+ Function to free the per SID data.
+ ********************************************************************/
+
+static void free_samr_cache(DISP_INFO *disp_info)
+{
+ DEBUG(10, ("free_samr_cache: deleting cache for SID %s\n",
+ sid_string_dbg(&disp_info->sid)));
+
+ /* 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();
+
+ if (disp_info->users) {
+ DEBUG(10,("free_samr_cache: deleting users cache\n"));
+ pdb_search_destroy(disp_info->users);
+ disp_info->users = NULL;
+ }
+ if (disp_info->machines) {
+ DEBUG(10,("free_samr_cache: deleting machines cache\n"));
+ pdb_search_destroy(disp_info->machines);
+ disp_info->machines = NULL;
+ }
+ if (disp_info->groups) {
+ DEBUG(10,("free_samr_cache: deleting groups cache\n"));
+ pdb_search_destroy(disp_info->groups);
+ disp_info->groups = NULL;
+ }
+ if (disp_info->aliases) {
+ DEBUG(10,("free_samr_cache: deleting aliases cache\n"));
+ pdb_search_destroy(disp_info->aliases);
+ disp_info->aliases = NULL;
+ }
+ if (disp_info->enum_users) {
+ DEBUG(10,("free_samr_cache: deleting enum_users cache\n"));
+ pdb_search_destroy(disp_info->enum_users);
+ disp_info->enum_users = NULL;
+ }
+ disp_info->enum_acb_mask = 0;
+
+ unbecome_root();
+}
+
+/*******************************************************************
+ Function to free the per handle data.
+ ********************************************************************/
+
+static void free_samr_info(void *ptr)
+{
+ struct samr_info *info=(struct samr_info *) ptr;
+
+ /* Only free the dispinfo cache if no one bothered to set up
+ a timeout. */
+
+ if (info->disp_info && info->disp_info->cache_timeout_event == NULL) {
+ free_samr_cache(info->disp_info);
+ }
+
+ talloc_destroy(info->mem_ctx);
+}
+
+/*******************************************************************
+ Idle event handler. Throw away the disp info cache.
+ ********************************************************************/
+
+static void disp_info_cache_idle_timeout_handler(struct event_context *ev_ctx,
+ struct timed_event *te,
+ const 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)
+{
+ /* 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", sid_string_dbg(&disp_info->sid),
+ (unsigned int)secs_fromnow ));
+
+ disp_info->cache_timeout_event = event_add_timed(
+ smbd_event_context(), NULL,
+ timeval_current_ofs(secs_fromnow, 0),
+ "disp_info_cache_idle_timeout_handler",
+ 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(DISP_INFO *disp_info)
+{
+ 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 count_sam_users(struct disp_info *info, uint32 acct_flags)
+{
+ struct samr_displayentry *entry;
+
+ if (info->builtin_domain) {
+ /* No users in builtin. */
+ return 0;
+ }
+
+ if (info->users == NULL) {
+ info->users = pdb_search_users(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 count_sam_groups(struct disp_info *info)
+{
+ struct samr_displayentry *entry;
+
+ if (info->builtin_domain) {
+ /* No groups in builtin. */
+ return 0;
+ }
+
+ if (info->groups == NULL) {
+ info->groups = pdb_search_groups();
+ 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 count_sam_aliases(struct disp_info *info)
+{
+ struct samr_displayentry *entry;
+
+ if (info->aliases == NULL) {
+ info->aliases = pdb_search_aliases(&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(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(pipes_struct *p,
+ struct samr_OpenDomain *r)
+{
+ struct samr_info *info;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ NTSTATUS status;
+ size_t sd_size;
+ SE_PRIV se_rights;
+
+ /* find the connection policy handle. */
+
+ if ( !find_policy_by_hnd(p, r->in.connect_handle, (void**)(void *)&info) )
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_SAM_OPEN_DOMAIN,
+ "_samr_OpenDomain" );
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ /*check if access can be granted as requested by client. */
+
+ make_samr_object_sd( p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0 );
+ se_map_generic( &des_access, &dom_generic_mapping );
+
+ se_priv_copy( &se_rights, &se_machine_account );
+ se_priv_add( &se_rights, &se_add_users );
+
+ status = access_check_samr_object( psd, p->pipe_user.nt_user_token,
+ &se_rights, GENERIC_RIGHTS_DOMAIN_WRITE, des_access,
+ &acc_granted, "_samr_OpenDomain" );
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ if (!sid_check_is_domain(r->in.sid) &&
+ !sid_check_is_builtin(r->in.sid)) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* associate the domain SID with the (unique) handle. */
+ if ((info = get_samr_info_by_sid(r->in.sid))==NULL)
+ return NT_STATUS_NO_MEMORY;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.domain_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_OpenDomain: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_GetUserPwInfo
+ ********************************************************************/
+
+NTSTATUS _samr_GetUserPwInfo(pipes_struct *p,
+ struct samr_GetUserPwInfo *r)
+{
+ struct samr_info *info = NULL;
+ 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__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.user_handle, (void **)(void *)&info)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = access_check_samr_function(info->acc_granted,
+ SAMR_USER_ACCESS_GET_ATTRIBUTES,
+ "_samr_GetUserPwInfo" );
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_check_is_in_our_domain(&info->sid)) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ become_root();
+ ret = lookup_sid(p->mem_ctx, &info->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(AP_MIN_PASSWORD_LEN,
+ &min_password_length);
+ pdb_get_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS,
+ &password_properties);
+ unbecome_root();
+
+ if (lp_check_password_script() && *lp_check_password_script()) {
+ 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;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static bool get_lsa_policy_samr_sid( pipes_struct *p, POLICY_HND *pol,
+ DOM_SID *sid, uint32 *acc_granted,
+ DISP_INFO **ppdisp_info)
+{
+ struct samr_info *info = NULL;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, pol, (void **)(void *)&info))
+ return False;
+
+ if (!info)
+ return False;
+
+ *sid = info->sid;
+ *acc_granted = info->acc_granted;
+ if (ppdisp_info) {
+ *ppdisp_info = info->disp_info;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ _samr_SetSecurity
+ ********************************************************************/
+
+NTSTATUS _samr_SetSecurity(pipes_struct *p,
+ struct samr_SetSecurity *r)
+{
+ DOM_SID pol_sid;
+ uint32 acc_granted, i;
+ SEC_ACL *dacl;
+ bool ret;
+ struct samu *sampass=NULL;
+ NTSTATUS status;
+
+ if (!get_lsa_policy_samr_sid(p, r->in.handle, &pol_sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ 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, &pol_sid);
+ unbecome_root();
+
+ if (!ret) {
+ DEBUG(4, ("User %s not found\n", sid_string_dbg(&pol_sid)));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ dacl = r->in.sdbuf->sd->dacl;
+ for (i=0; i < dacl->num_aces; i++) {
+ if (sid_equal(&pol_sid, &dacl->aces[i].trustee)) {
+ ret = pdb_set_pass_can_change(sampass,
+ (dacl->aces[i].access_mask &
+ SA_RIGHT_USER_CHANGE_PASSWORD) ?
+ True: False);
+ break;
+ }
+ }
+
+ if (!ret) {
+ TALLOC_FREE(sampass);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_USER_SET_ATTRIBUTES,
+ "_samr_SetSecurity");
+ if (NT_STATUS_IS_OK(status)) {
+ 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, 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) {
+ DEBUG(4,("User %s not found\n", sid_string_dbg(user_sid)));
+ 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(pipes_struct *p,
+ struct samr_QuerySecurity *r)
+{
+ NTSTATUS status;
+ DOM_SID pol_sid;
+ SEC_DESC * psd = NULL;
+ uint32 acc_granted;
+ size_t sd_size;
+
+ /* Get the SID. */
+ if (!get_lsa_policy_samr_sid(p, r->in.handle, &pol_sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(10,("_samr_QuerySecurity: querying security on SID: %s\n",
+ sid_string_dbg(&pol_sid)));
+
+ /* Check what typ of SID is beeing queried (e.g Domain SID, User SID, Group SID) */
+
+ /* To query the security of the SAM it self an invalid SID with S-0-0 is passed to this function */
+ if (pol_sid.sid_rev_num == 0) {
+ 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);
+ } else if (sid_equal(&pol_sid,get_global_sam_sid())) {
+ /* check if it is our domain SID */
+ DEBUG(5,("_samr_QuerySecurity: querying security on Domain "
+ "with SID: %s\n", sid_string_dbg(&pol_sid)));
+ status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0);
+ } else if (sid_equal(&pol_sid,&global_sid_Builtin)) {
+ /* check if it is the Builtin Domain */
+ /* TODO: Builtin probably needs a different SD with restricted write access*/
+ DEBUG(5,("_samr_QuerySecurity: querying security on Builtin "
+ "Domain with SID: %s\n", sid_string_dbg(&pol_sid)));
+ status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &dom_generic_mapping, NULL, 0);
+ } else if (sid_check_is_in_our_domain(&pol_sid) ||
+ sid_check_is_in_builtin(&pol_sid)) {
+ /* 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 Object "
+ "with SID: %s\n", sid_string_dbg(&pol_sid)));
+ if (check_change_pw_access(p->mem_ctx, &pol_sid)) {
+ status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_generic_mapping,
+ &pol_sid, SAMR_USR_RIGHTS_WRITE_PW);
+ } else {
+ status = make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &usr_nopwchange_generic_mapping,
+ &pol_sid, SAMR_USR_RIGHTS_CANT_WRITE_PW);
+ }
+ } else {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ 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(pipes_struct *p,
+ struct samr_EnumDomainUsers *r)
+{
+ NTSTATUS status;
+ struct samr_info *info = NULL;
+ int num_account;
+ uint32 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 max_entries = max_sam_entries;
+ struct samr_displayentry *entries = NULL;
+ struct samr_SamArray *samr_array = NULL;
+ struct samr_SamEntry *samr_entries = NULL;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+ "_samr_EnumDomainUsers");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_EnumDomainUsers: %d\n", __LINE__));
+
+ if (info->builtin_domain) {
+ /* No users in builtin. */
+ *r->out.resume_handle = *r->in.resume_handle;
+ DEBUG(5,("_samr_EnumDomainUsers: No users in BUILTIN\n"));
+ return status;
+ }
+
+ samr_array = TALLOC_ZERO_P(p->mem_ctx, struct samr_SamArray);
+ if (!samr_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+
+ /* AS ROOT !!!! */
+
+ if ((info->disp_info->enum_users != NULL) &&
+ (info->disp_info->enum_acb_mask != r->in.acct_flags)) {
+ pdb_search_destroy(info->disp_info->enum_users);
+ info->disp_info->enum_users = NULL;
+ }
+
+ if (info->disp_info->enum_users == NULL) {
+ info->disp_info->enum_users = pdb_search_users(r->in.acct_flags);
+ info->disp_info->enum_acb_mask = r->in.acct_flags;
+ }
+
+ if (info->disp_info->enum_users == NULL) {
+ /* END AS ROOT !!!! */
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ num_account = pdb_search_entries(info->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(info->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.sam = samr_array;
+ *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(pipes_struct *p,
+ struct samr_EnumDomainGroups *r)
+{
+ NTSTATUS status;
+ struct samr_info *info = NULL;
+ struct samr_displayentry *groups;
+ uint32 num_groups;
+ struct samr_SamArray *samr_array = NULL;
+ struct samr_SamEntry *samr_entries = NULL;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+ "_samr_EnumDomainGroups");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_EnumDomainGroups: %d\n", __LINE__));
+
+ if (info->builtin_domain) {
+ /* No groups in builtin. */
+ *r->out.resume_handle = *r->in.resume_handle;
+ DEBUG(5,("_samr_EnumDomainGroups: No groups in BUILTIN\n"));
+ return status;
+ }
+
+ samr_array = TALLOC_ZERO_P(p->mem_ctx, struct samr_SamArray);
+ if (!samr_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* the domain group array is being allocated in the function below */
+
+ become_root();
+
+ if (info->disp_info->groups == NULL) {
+ info->disp_info->groups = pdb_search_groups();
+
+ if (info->disp_info->groups == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ num_groups = pdb_search_entries(info->disp_info->groups,
+ *r->in.resume_handle,
+ MAX_SAM_ENTRIES, &groups);
+ unbecome_root();
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ make_group_sam_entry_list(p->mem_ctx, &samr_entries,
+ num_groups, groups);
+
+ samr_array->count = num_groups;
+ samr_array->entries = samr_entries;
+
+ *r->out.sam = samr_array;
+ *r->out.num_entries = num_groups;
+ /* this was missing, IMHO:
+ *r->out.resume_handle = num_groups + *r->in.resume_handle;
+ */
+
+ DEBUG(5,("_samr_EnumDomainGroups: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_EnumDomainAliases
+ ********************************************************************/
+
+NTSTATUS _samr_EnumDomainAliases(pipes_struct *p,
+ struct samr_EnumDomainAliases *r)
+{
+ NTSTATUS status;
+ struct samr_info *info;
+ struct samr_displayentry *aliases;
+ uint32 num_aliases = 0;
+ struct samr_SamArray *samr_array = NULL;
+ struct samr_SamEntry *samr_entries = NULL;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+ "_samr_EnumDomainAliases");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5,("_samr_EnumDomainAliases: sid %s\n",
+ sid_string_dbg(&info->sid)));
+
+ samr_array = TALLOC_ZERO_P(p->mem_ctx, struct samr_SamArray);
+ if (!samr_array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+
+ if (info->disp_info->aliases == NULL) {
+ info->disp_info->aliases = pdb_search_aliases(&info->sid);
+ if (info->disp_info->aliases == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ num_aliases = pdb_search_entries(info->disp_info->aliases,
+ *r->in.resume_handle,
+ MAX_SAM_ENTRIES, &aliases);
+ unbecome_root();
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(info->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__));
+
+ 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 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(pipes_struct *p,
+ struct samr_QueryDisplayInfo *r)
+{
+ NTSTATUS status;
+ struct samr_info *info = NULL;
+ uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */
+
+ uint32 max_entries = r->in.max_entries;
+ uint32 enum_context = r->in.start_idx;
+ uint32 max_size = r->in.buf_size;
+
+ union samr_DispInfo *disp_info = r->out.info;
+
+ uint32 temp_size=0, total_data_size=0;
+ NTSTATUS disp_ret = NT_STATUS_UNSUCCESSFUL;
+ uint32 num_account = 0;
+ 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;
+ struct samr_displayentry *entries = NULL;
+
+ DEBUG(5,("_samr_QueryDisplayInfo: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /*
+ * 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(max_entries > max_sam_entries) {
+ DEBUG(5, ("_samr_QueryDisplayInfo: client requested %d "
+ "entries, limiting to %d\n", 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>max_size) {
+ max_entries=MIN((max_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 0x1:
+ case 0x4:
+ if (info->disp_info->users == NULL) {
+ info->disp_info->users = pdb_search_users(ACB_NORMAL);
+ if (info->disp_info->users == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_QueryDisplayInfo: starting user enumeration at index %u\n",
+ (unsigned int)enum_context ));
+ } else {
+ DEBUG(10,("_samr_QueryDisplayInfo: using cached user enumeration at index %u\n",
+ (unsigned int)enum_context ));
+ }
+
+ num_account = pdb_search_entries(info->disp_info->users,
+ enum_context, max_entries,
+ &entries);
+ break;
+ case 0x2:
+ if (info->disp_info->machines == NULL) {
+ info->disp_info->machines =
+ pdb_search_users(ACB_WSTRUST|ACB_SVRTRUST);
+ if (info->disp_info->machines == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_QueryDisplayInfo: starting machine enumeration at index %u\n",
+ (unsigned int)enum_context ));
+ } else {
+ DEBUG(10,("_samr_QueryDisplayInfo: using cached machine enumeration at index %u\n",
+ (unsigned int)enum_context ));
+ }
+
+ num_account = pdb_search_entries(info->disp_info->machines,
+ enum_context, max_entries,
+ &entries);
+ break;
+ case 0x3:
+ case 0x5:
+ if (info->disp_info->groups == NULL) {
+ info->disp_info->groups = pdb_search_groups();
+ if (info->disp_info->groups == NULL) {
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ DEBUG(10,("_samr_QueryDisplayInfo: starting group enumeration at index %u\n",
+ (unsigned int)enum_context ));
+ } else {
+ DEBUG(10,("_samr_QueryDisplayInfo: using cached group enumeration at index %u\n",
+ (unsigned int)enum_context ));
+ }
+
+ num_account = pdb_search_entries(info->disp_info->groups,
+ enum_context, max_entries,
+ &entries);
+ break;
+ default:
+ unbecome_root();
+ smb_panic("info class changed");
+ break;
+ }
+ unbecome_root();
+
+
+ /* Now create reply structure */
+ switch (r->in.level) {
+ case 0x1:
+ disp_ret = init_samr_dispinfo_1(p->mem_ctx, &disp_info->info1,
+ num_account, enum_context,
+ entries);
+ break;
+ case 0x2:
+ disp_ret = init_samr_dispinfo_2(p->mem_ctx, &disp_info->info2,
+ num_account, enum_context,
+ entries);
+ break;
+ case 0x3:
+ disp_ret = init_samr_dispinfo_3(p->mem_ctx, &disp_info->info3,
+ num_account, enum_context,
+ entries);
+ break;
+ case 0x4:
+ disp_ret = init_samr_dispinfo_4(p->mem_ctx, &disp_info->info4,
+ num_account, enum_context,
+ entries);
+ break;
+ case 0x5:
+ disp_ret = init_samr_dispinfo_5(p->mem_ctx, &disp_info->info5,
+ num_account, enum_context,
+ entries);
+ break;
+ default:
+ smb_panic("info class changed");
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(disp_ret))
+ return disp_ret;
+
+ /* calculate the total size */
+ total_data_size=num_account*struct_size;
+
+ if (num_account) {
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ /* Ensure we cache this enumeration. */
+ set_disp_info_cache_timeout(info->disp_info, DISP_INFO_CACHE_TIMEOUT);
+
+ DEBUG(5, ("_samr_QueryDisplayInfo: %d\n", __LINE__));
+
+ *r->out.total_size = total_data_size;
+ *r->out.returned_size = temp_size;
+
+ return status;
+}
+
+/****************************************************************
+ _samr_QueryDisplayInfo2
+****************************************************************/
+
+NTSTATUS _samr_QueryDisplayInfo2(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(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(pipes_struct *p,
+ struct samr_QueryAliasInfo *r)
+{
+ DOM_SID sid;
+ struct acct_info info;
+ uint32 acc_granted;
+ 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__));
+
+ alias_info = TALLOC_ZERO_P(p->mem_ctx, union samr_AliasInfo);
+ if (!alias_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.alias_handle, &sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_ALIAS_LOOKUP_INFO,
+ "_samr_QueryAliasInfo");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ become_root();
+ status = pdb_get_aliasinfo(&sid, &info);
+ unbecome_root();
+
+ if ( !NT_STATUS_IS_OK(status))
+ return status;
+
+ /* FIXME: info contains fstrings */
+ alias_name = talloc_strdup(r, info.acct_name);
+ alias_description = talloc_strdup(r, info.acct_desc);
+
+ switch (r->in.level) {
+ case ALIASINFOALL:
+ init_samr_alias_info1(&alias_info->all,
+ alias_name,
+ 1,
+ alias_description);
+ break;
+ case ALIASINFODESCRIPTION:
+ init_samr_alias_info3(&alias_info->description,
+ 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;
+}
+
+#if 0
+/*******************************************************************
+ samr_reply_lookup_ids
+ ********************************************************************/
+
+ uint32 _samr_lookup_ids(pipes_struct *p, SAMR_Q_LOOKUP_IDS *q_u, SAMR_R_LOOKUP_IDS *r_u)
+{
+ uint32 rid[MAX_SAM_ENTRIES];
+ int num_rids = q_u->num_sids1;
+
+ r_u->status = NT_STATUS_OK;
+
+ DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+ if (num_rids > MAX_SAM_ENTRIES) {
+ num_rids = MAX_SAM_ENTRIES;
+ DEBUG(5,("_samr_lookup_ids: truncating entries to %d\n", num_rids));
+ }
+
+#if 0
+ int i;
+ SMB_ASSERT_ARRAY(q_u->uni_user_name, num_rids);
+
+ for (i = 0; i < num_rids && status == 0; i++)
+ {
+ struct sam_passwd *sam_pass;
+ fstring user_name;
+
+
+ fstrcpy(user_name, unistrn2(q_u->uni_user_name[i].buffer,
+ q_u->uni_user_name[i].uni_str_len));
+
+ /* find the user account */
+ become_root();
+ sam_pass = get_smb21pwd_entry(user_name, 0);
+ unbecome_root();
+
+ if (sam_pass == NULL)
+ {
+ status = 0xC0000000 | NT_STATUS_NO_SUCH_USER;
+ rid[i] = 0;
+ }
+ else
+ {
+ rid[i] = sam_pass->user_rid;
+ }
+ }
+#endif
+
+ num_rids = 1;
+ rid[0] = BUILTIN_ALIAS_RID_USERS;
+
+ init_samr_r_lookup_ids(&r_u, num_rids, rid, NT_STATUS_OK);
+
+ DEBUG(5,("_samr_lookup_ids: %d\n", __LINE__));
+
+ return r_u->status;
+}
+#endif
+
+/*******************************************************************
+ _samr_LookupNames
+ ********************************************************************/
+
+NTSTATUS _samr_LookupNames(pipes_struct *p,
+ struct samr_LookupNames *r)
+{
+ NTSTATUS status;
+ uint32 *rid;
+ enum lsa_SidType *type;
+ int i;
+ int num_rids = r->in.num_names;
+ DOM_SID pol_sid;
+ uint32 acc_granted;
+ struct samr_Ids rids, types;
+
+ DEBUG(5,("_samr_LookupNames: %d\n", __LINE__));
+
+ if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &pol_sid, &acc_granted, NULL)) {
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ status = access_check_samr_function(acc_granted,
+ 0, /* Don't know the acc_bits yet */
+ "_samr_LookupNames");
+ 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, 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",
+ sid_string_dbg(&pol_sid)));
+
+ 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(&pol_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) {
+ status = NT_STATUS_OK;
+ }
+ }
+
+ rids.count = num_rids;
+ rids.ids = rid;
+
+ types.count = num_rids;
+ types.ids = type;
+
+ *r->out.rids = rids;
+ *r->out.types = types;
+
+ DEBUG(5,("_samr_LookupNames: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_ChangePasswordUser2
+ ********************************************************************/
+
+NTSTATUS _samr_ChangePasswordUser2(pipes_struct *p,
+ struct samr_ChangePasswordUser2 *r)
+{
+ NTSTATUS status;
+ fstring user_name;
+ fstring wks;
+
+ DEBUG(5,("_samr_ChangePasswordUser2: %d\n", __LINE__));
+
+ fstrcpy(user_name, r->in.account->string);
+ fstrcpy(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(user_name);
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ status = pass_oem_change(user_name,
+ 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__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_ChangePasswordUser3
+ ********************************************************************/
+
+NTSTATUS _samr_ChangePasswordUser3(pipes_struct *p,
+ struct samr_ChangePasswordUser3 *r)
+{
+ NTSTATUS status;
+ fstring user_name;
+ const char *wks = NULL;
+ uint32 reject_reason;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+
+ DEBUG(5,("_samr_ChangePasswordUser3: %d\n", __LINE__));
+
+ fstrcpy(user_name, r->in.account->string);
+ 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(user_name);
+
+ /*
+ * UNIX username case mangling not required, pass_oem_change
+ * is case insensitive.
+ */
+
+ status = pass_oem_change(user_name,
+ 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_PASSWORD_RESTRICTION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_RESTRICTION)) {
+
+ uint32 min_pass_len,pass_hist,password_properties;
+ time_t u_expire, u_min_age;
+ NTTIME nt_expire, nt_min_age;
+ uint32 account_policy_temp;
+
+ dominfo = TALLOC_ZERO_P(p->mem_ctx, struct samr_DomInfo1);
+ if (!dominfo) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ reject = TALLOC_ZERO_P(p->mem_ctx, struct samr_ChangeReject);
+ if (!reject) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+ min_pass_len = account_policy_temp;
+
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &account_policy_temp);
+ pass_hist = account_policy_temp;
+
+ pdb_get_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+ password_properties = account_policy_temp;
+
+ pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&nt_expire, u_expire);
+ unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+ if (lp_check_password_script() && *lp_check_password_script()) {
+ password_properties |= DOMAIN_PASSWORD_COMPLEX;
+ }
+
+ init_samr_DomInfo1(dominfo,
+ min_pass_len,
+ pass_hist,
+ password_properties,
+ u_expire,
+ u_min_age);
+
+ reject->reason = 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 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(pipes_struct *p,
+ struct samr_LookupRids *r)
+{
+ NTSTATUS status;
+ const char **names;
+ enum lsa_SidType *attrs = NULL;
+ uint32 *wire_attrs = NULL;
+ DOM_SID pol_sid;
+ int num_rids = (int)r->in.num_rids;
+ uint32 acc_granted;
+ 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__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &pol_sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ 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, 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(&pol_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 for wire format. */
+ for (i = 0; i < num_rids; i++) {
+ wire_attrs[i] = (uint32)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(pipes_struct *p,
+ struct samr_OpenUser *r)
+{
+ struct samu *sampass=NULL;
+ DOM_SID sid;
+ POLICY_HND domain_pol = *r->in.domain_handle;
+ POLICY_HND *user_pol = r->out.user_handle;
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ size_t sd_size;
+ bool ret;
+ NTSTATUS nt_status;
+ SE_PRIV se_rights;
+
+ /* find the domain policy handle and get domain SID / access bits in the domain policy. */
+
+ if ( !get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted, NULL) )
+ return NT_STATUS_INVALID_HANDLE;
+
+ nt_status = access_check_samr_function(acc_granted,
+ SA_RIGHT_DOMAIN_OPEN_ACCOUNT,
+ "_samr_OpenUser" );
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ if ( !(sampass = samu_new( p->mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* append the user's RID to it */
+
+ if (!sid_append_rid(&sid, r->in.rid))
+ return NT_STATUS_NO_SUCH_USER;
+
+ /* check if access can be granted as requested by client. */
+
+ 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);
+
+ se_priv_copy( &se_rights, &se_machine_account );
+ se_priv_add( &se_rights, &se_add_users );
+
+ nt_status = access_check_samr_object(psd, p->pipe_user.nt_user_token,
+ &se_rights, GENERIC_RIGHTS_USER_WRITE, des_access,
+ &acc_granted, "_samr_OpenUser");
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ become_root();
+ ret=pdb_getsampwsid(sampass, &sid);
+ unbecome_root();
+
+ /* check that the SID exists in our domain. */
+ if (ret == False) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ TALLOC_FREE(sampass);
+
+ /* associate the user's SID and access bits with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ 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_P(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;
+}
+
+/*************************************************************************
+ get_user_info_7. Safe. Only gives out account_name.
+ *************************************************************************/
+
+static NTSTATUS get_user_info_7(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo7 *r,
+ DOM_SID *user_sid)
+{
+ struct samu *smbpass=NULL;
+ bool ret;
+ const char *account_name = NULL;
+
+ ZERO_STRUCTP(r);
+
+ if ( !(smbpass = samu_new( mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(smbpass, user_sid);
+ unbecome_root();
+
+ if ( !ret ) {
+ DEBUG(4,("User %s not found\n", sid_string_dbg(user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ account_name = talloc_strdup(mem_ctx, pdb_get_username(smbpass));
+ if (!account_name) {
+ TALLOC_FREE(smbpass);
+ return NT_STATUS_NO_MEMORY;
+ }
+ TALLOC_FREE(smbpass);
+
+ DEBUG(3,("User:[%s]\n", account_name));
+
+ init_samr_user_info7(r, account_name);
+
+ 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,
+ DOM_SID *user_sid)
+{
+ struct samu *smbpass=NULL;
+ bool ret;
+
+ ZERO_STRUCTP(r);
+
+ if ( !(smbpass = samu_new( mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(smbpass, user_sid);
+ unbecome_root();
+
+ if (ret==False) {
+ DEBUG(4,("User %s not found\n", sid_string_dbg(user_sid)));
+ TALLOC_FREE(smbpass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) ));
+
+ init_samr_user_info9(r, pdb_get_group_rid(smbpass));
+
+ TALLOC_FREE(smbpass);
+
+ 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,
+ DOM_SID *user_sid)
+{
+ struct samu *smbpass=NULL;
+ bool ret;
+
+ ZERO_STRUCTP(r);
+
+ if ( !(smbpass = samu_new( mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(smbpass, user_sid);
+ unbecome_root();
+
+ if (ret==False) {
+ DEBUG(4,("User %s not found\n", sid_string_dbg(user_sid)));
+ TALLOC_FREE(smbpass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) ));
+
+ init_samr_user_info16(r, pdb_get_acct_ctrl(smbpass));
+
+ TALLOC_FREE(smbpass);
+
+ 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(pipes_struct *p,
+ TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo18 *r,
+ DOM_SID *user_sid)
+{
+ struct samu *smbpass=NULL;
+ bool ret;
+
+ ZERO_STRUCTP(r);
+
+ if (p->auth.auth_type != PIPE_AUTH_TYPE_NTLMSSP || p->auth.auth_type != PIPE_AUTH_TYPE_SPNEGO_NTLMSSP) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (p->auth.auth_level != PIPE_AUTH_LEVEL_PRIVACY) {
+ 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) {
+ DEBUG(4, ("User %s not found\n", sid_string_dbg(user_sid)));
+ TALLOC_FREE(smbpass);
+ return (geteuid() == (uid_t)0) ? 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;
+ }
+
+ init_samr_user_info18(r, pdb_get_lanman_passwd(smbpass),
+ pdb_get_nt_passwd(smbpass));
+
+ 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,
+ DOM_SID *user_sid)
+{
+ struct samu *sampass=NULL;
+ bool ret;
+ const char *munged_dial = NULL;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct lsa_BinaryString *parameters = NULL;
+
+ ZERO_STRUCTP(r);
+
+ if ( !(sampass = samu_new( mem_ctx )) ) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(sampass, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(4,("User %s not found\n", sid_string_dbg(user_sid)));
+ TALLOC_FREE(sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ munged_dial = pdb_get_munged_dial(sampass);
+
+ samr_clear_sam_passwd(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("");
+ }
+
+ status = init_samr_parameters_string(mem_ctx, &blob, &parameters);
+ data_blob_free(&blob);
+ TALLOC_FREE(sampass);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ init_samr_user_info20(r, parameters);
+
+ return NT_STATUS_OK;
+}
+
+
+/*************************************************************************
+ get_user_info_21
+ *************************************************************************/
+
+static NTSTATUS get_user_info_21(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo21 *r,
+ DOM_SID *user_sid,
+ DOM_SID *domain_sid)
+{
+ NTSTATUS status;
+ struct samu *pw = NULL;
+ bool ret;
+ const DOM_SID *sid_user, *sid_group;
+ uint32_t rid, primary_gid;
+ NTTIME last_logon, last_logoff, last_password_change,
+ acct_expiry, allow_password_change, force_password_change;
+ time_t must_change_time;
+ uint8_t password_expired;
+ const char *account_name, *full_name, *home_directory, *home_drive,
+ *logon_script, *profile_path, *description,
+ *workstations, *comment;
+ struct samr_LogonHours logon_hours;
+ struct lsa_BinaryString *parameters = NULL;
+ const char *munged_dial = NULL;
+ DATA_BLOB blob;
+
+ ZERO_STRUCTP(r);
+
+ if (!(pw = samu_new(mem_ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(pw, user_sid);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(4,("User %s not found\n", sid_string_dbg(user_sid)));
+ TALLOC_FREE(pw);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ samr_clear_sam_passwd(pw);
+
+ DEBUG(3,("User:[%s]\n", pdb_get_username(pw)));
+
+ 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), sid_string_dbg(sid_user),
+ sid_string_dbg(domain_sid)));
+ TALLOC_FREE(pw);
+ 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), sid_string_dbg(sid_group),
+ sid_string_dbg(domain_sid)));
+ TALLOC_FREE(pw);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ unix_to_nt_time(&last_logon, pdb_get_logon_time(pw));
+ unix_to_nt_time(&last_logoff, pdb_get_logoff_time(pw));
+ unix_to_nt_time(&acct_expiry, pdb_get_kickoff_time(pw));
+ unix_to_nt_time(&last_password_change, pdb_get_pass_last_set_time(pw));
+ unix_to_nt_time(&allow_password_change, pdb_get_pass_can_change_time(pw));
+
+ must_change_time = pdb_get_pass_must_change_time(pw);
+ if (must_change_time == get_time_t_max()) {
+ unix_to_nt_time_abs(&force_password_change, must_change_time);
+ } else {
+ unix_to_nt_time(&force_password_change, must_change_time);
+ }
+
+ if (pdb_get_pass_must_change_time(pw) == 0) {
+ password_expired = PASS_MUST_CHANGE_AT_NEXT_LOGON;
+ } else {
+ password_expired = 0;
+ }
+
+ munged_dial = pdb_get_munged_dial(pw);
+ if (munged_dial) {
+ blob = base64_decode_data_blob(munged_dial);
+ } else {
+ blob = data_blob_string_const("");
+ }
+
+ status = init_samr_parameters_string(mem_ctx, &blob, &parameters);
+ data_blob_free(&blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(pw);
+ return status;
+ }
+
+ account_name = talloc_strdup(mem_ctx, pdb_get_username(pw));
+ full_name = talloc_strdup(mem_ctx, pdb_get_fullname(pw));
+ home_directory = talloc_strdup(mem_ctx, pdb_get_homedir(pw));
+ home_drive = talloc_strdup(mem_ctx, pdb_get_dir_drive(pw));
+ logon_script = talloc_strdup(mem_ctx, pdb_get_logon_script(pw));
+ profile_path = talloc_strdup(mem_ctx, pdb_get_profile_path(pw));
+ description = talloc_strdup(mem_ctx, pdb_get_acct_desc(pw));
+ workstations = talloc_strdup(mem_ctx, pdb_get_workstations(pw));
+ comment = talloc_strdup(mem_ctx, pdb_get_comment(pw));
+
+ logon_hours = get_logon_hours_from_pdb(mem_ctx, pw);
+#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
+
+ init_samr_user_info21(r,
+ last_logon,
+ last_logoff,
+ last_password_change,
+ acct_expiry,
+ allow_password_change,
+ force_password_change,
+ account_name,
+ full_name,
+ home_directory,
+ home_drive,
+ logon_script,
+ profile_path,
+ description,
+ workstations,
+ comment,
+ parameters,
+ rid,
+ primary_gid,
+ pdb_get_acct_ctrl(pw),
+ pdb_build_fields_present(pw),
+ logon_hours,
+ pdb_get_bad_password_count(pw),
+ pdb_get_logon_count(pw),
+ 0, /* country_code */
+ 0, /* code_page */
+ 0, /* nt_password_set */
+ 0, /* lm_password_set */
+ password_expired);
+ TALLOC_FREE(pw);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_QueryUserInfo
+ ********************************************************************/
+
+NTSTATUS _samr_QueryUserInfo(pipes_struct *p,
+ struct samr_QueryUserInfo *r)
+{
+ NTSTATUS status;
+ union samr_UserInfo *user_info = NULL;
+ struct samr_info *info = NULL;
+ DOM_SID domain_sid;
+ uint32 rid;
+
+ /* search for the handle */
+ if (!find_policy_by_hnd(p, r->in.user_handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ domain_sid = info->sid;
+
+ sid_split_rid(&domain_sid, &rid);
+
+ if (!sid_check_is_in_our_domain(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ DEBUG(5,("_samr_QueryUserInfo: sid:%s\n",
+ sid_string_dbg(&info->sid)));
+
+ user_info = TALLOC_ZERO_P(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));
+
+ switch (r->in.level) {
+ case 7:
+ status = get_user_info_7(p->mem_ctx, &user_info->info7, &info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ case 9:
+ status = get_user_info_9(p->mem_ctx, &user_info->info9, &info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+ case 16:
+ status = get_user_info_16(p->mem_ctx, &user_info->info16, &info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+
+ case 18:
+ status = get_user_info_18(p, p->mem_ctx, &user_info->info18, &info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+
+ case 20:
+ status = get_user_info_20(p->mem_ctx, &user_info->info20, &info->sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+
+ case 21:
+ status = get_user_info_21(p->mem_ctx, &user_info->info21,
+ &info->sid, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = user_info;
+
+ DEBUG(5,("_samr_QueryUserInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_GetGroupsForUser
+ ********************************************************************/
+
+NTSTATUS _samr_GetGroupsForUser(pipes_struct *p,
+ struct samr_GetGroupsForUser *r)
+{
+ struct samu *sam_pass=NULL;
+ DOM_SID sid;
+ DOM_SID *sids;
+ struct samr_RidWithAttribute dom_gid;
+ struct samr_RidWithAttribute *gids = NULL;
+ uint32 primary_group_rid;
+ size_t num_groups = 0;
+ gid_t *unix_gids;
+ size_t i, num_gids;
+ uint32 acc_granted;
+ bool ret;
+ NTSTATUS result;
+ bool success = False;
+
+ 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__));
+
+ rids = TALLOC_ZERO_P(p->mem_ctx, struct samr_RidWithAttributeArray);
+ if (!rids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.user_handle, &sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ result = access_check_samr_function(acc_granted,
+ SA_RIGHT_USER_GET_GROUPS,
+ "_samr_GetGroupsForUser");
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (!sid_check_is_in_our_domain(&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, &sid);
+ unbecome_root();
+
+ if (!ret) {
+ DEBUG(10, ("pdb_getsampwsid failed for %s\n",
+ sid_string_dbg(&sid)));
+ 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",
+ sid_string_dbg(&sid)));
+ return result;
+ }
+
+ if ( !success ) {
+ DEBUG(5, ("Group sid %s for user %s not in our domain\n",
+ sid_string_dbg(pdb_get_group_sid(sam_pass)),
+ 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_MANDATORY|SE_GROUP_ENABLED_BY_DEFAULT|
+ SE_GROUP_ENABLED);
+ 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",
+ sid_string_dbg(&sids[i])));
+ 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;
+}
+
+/*******************************************************************
+ samr_QueryDomainInfo_internal
+ ********************************************************************/
+
+static NTSTATUS samr_QueryDomainInfo_internal(const char *fn_name,
+ pipes_struct *p,
+ struct policy_handle *handle,
+ uint32_t level,
+ union samr_DomainInfo **dom_info_ptr)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct samr_info *info = NULL;
+ union samr_DomainInfo *dom_info;
+ uint32 min_pass_len,pass_hist,password_properties;
+ time_t u_expire, u_min_age;
+ NTTIME nt_expire, nt_min_age;
+
+ time_t u_lock_duration, u_reset_time;
+ NTTIME nt_lock_duration, nt_reset_time;
+ uint32 lockout;
+ time_t u_logout;
+ NTTIME nt_logout;
+
+ uint32 account_policy_temp;
+
+ time_t seq_num;
+ uint32 server_role;
+
+ uint32 num_users=0, num_groups=0, num_aliases=0;
+
+ DEBUG(5,("%s: %d\n", fn_name, __LINE__));
+
+ dom_info = TALLOC_ZERO_P(p->mem_ctx, union samr_DomainInfo);
+ if (!dom_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *dom_info_ptr = dom_info;
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, handle, (void **)(void *)&info)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (level) {
+ case 0x01:
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &account_policy_temp);
+ min_pass_len = account_policy_temp;
+
+ pdb_get_account_policy(AP_PASSWORD_HISTORY, &account_policy_temp);
+ pass_hist = account_policy_temp;
+
+ pdb_get_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS, &account_policy_temp);
+ password_properties = account_policy_temp;
+
+ pdb_get_account_policy(AP_MAX_PASSWORD_AGE, &account_policy_temp);
+ u_expire = account_policy_temp;
+
+ pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &account_policy_temp);
+ u_min_age = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&nt_expire, u_expire);
+ unix_to_nt_time_abs(&nt_min_age, u_min_age);
+
+ init_samr_DomInfo1(&dom_info->info1,
+ (uint16)min_pass_len,
+ (uint16)pass_hist,
+ password_properties,
+ nt_expire,
+ nt_min_age);
+ break;
+ case 0x02:
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ num_users = count_sam_users(info->disp_info, ACB_NORMAL);
+ num_groups = count_sam_groups(info->disp_info);
+ num_aliases = count_sam_aliases(info->disp_info);
+
+ pdb_get_account_policy(AP_TIME_TO_LOGOUT, &account_policy_temp);
+ u_logout = account_policy_temp;
+
+ unix_to_nt_time_abs(&nt_logout, u_logout);
+
+ if (!pdb_get_seq_num(&seq_num))
+ seq_num = time(NULL);
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ server_role = ROLE_DOMAIN_PDC;
+ if (lp_server_role() == ROLE_DOMAIN_BDC)
+ server_role = ROLE_DOMAIN_BDC;
+
+ init_samr_DomInfo2(&dom_info->info2,
+ nt_logout,
+ lp_serverstring(),
+ lp_workgroup(),
+ global_myname(),
+ seq_num,
+ 1,
+ server_role,
+ 1,
+ num_users,
+ num_groups,
+ num_aliases);
+ break;
+ case 0x03:
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ {
+ uint32 ul;
+ pdb_get_account_policy(AP_TIME_TO_LOGOUT, &ul);
+ u_logout = (time_t)ul;
+ }
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&nt_logout, u_logout);
+
+ init_samr_DomInfo3(&dom_info->info3,
+ nt_logout);
+
+ break;
+ case 0x04:
+ init_samr_DomInfo4(&dom_info->info4,
+ lp_serverstring());
+ break;
+ case 0x05:
+ init_samr_DomInfo5(&dom_info->info5,
+ get_global_sam_name());
+ break;
+ case 0x06:
+ /* NT returns its own name when a PDC. win2k and later
+ * only the name of the PDC if itself is a BDC (samba4
+ * idl) */
+ init_samr_DomInfo6(&dom_info->info6,
+ global_myname());
+ break;
+ case 0x07:
+ server_role = ROLE_DOMAIN_PDC;
+ if (lp_server_role() == ROLE_DOMAIN_BDC)
+ server_role = ROLE_DOMAIN_BDC;
+
+ init_samr_DomInfo7(&dom_info->info7,
+ server_role);
+ break;
+ case 0x08:
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ if (!pdb_get_seq_num(&seq_num)) {
+ seq_num = time(NULL);
+ }
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ init_samr_DomInfo8(&dom_info->info8,
+ seq_num,
+ 0);
+ break;
+ case 0x0c:
+
+ become_root();
+
+ /* AS ROOT !!! */
+
+ pdb_get_account_policy(AP_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(AP_RESET_COUNT_TIME, &account_policy_temp);
+ u_reset_time = account_policy_temp * 60;
+
+ pdb_get_account_policy(AP_BAD_ATTEMPT_LOCKOUT, &account_policy_temp);
+ lockout = account_policy_temp;
+
+ /* !AS ROOT */
+
+ unbecome_root();
+
+ unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration);
+ unix_to_nt_time_abs(&nt_reset_time, u_reset_time);
+
+ init_samr_DomInfo12(&dom_info->info12,
+ nt_lock_duration,
+ nt_reset_time,
+ (uint16)lockout);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ DEBUG(5,("%s: %d\n", fn_name, __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_QueryDomainInfo
+ ********************************************************************/
+
+NTSTATUS _samr_QueryDomainInfo(pipes_struct *p,
+ struct samr_QueryDomainInfo *r)
+{
+ return samr_QueryDomainInfo_internal("_samr_QueryDomainInfo",
+ p,
+ r->in.domain_handle,
+ r->in.level,
+ r->out.info);
+}
+
+/* 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(pipes_struct *p,
+ struct samr_CreateUser2 *r)
+{
+ const char *account = NULL;
+ DOM_SID sid;
+ POLICY_HND dom_pol = *r->in.domain_handle;
+ uint32_t acb_info = r->in.acct_flags;
+ POLICY_HND *user_pol = r->out.user_handle;
+ struct samr_info *info = NULL;
+ NTSTATUS nt_status;
+ uint32 acc_granted;
+ SEC_DESC *psd;
+ size_t sd_size;
+ /* check this, when giving away 'add computer to domain' privs */
+ uint32 des_access = GENERIC_RIGHTS_USER_ALL_ACCESS;
+ bool can_add_account = False;
+ SE_PRIV se_rights;
+ DISP_INFO *disp_info = NULL;
+
+ /* Get the domain SID stored in the domain policy */
+ if (!get_lsa_policy_samr_sid(p, &dom_pol, &sid, &acc_granted,
+ &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ nt_status = access_check_samr_function(acc_granted,
+ SA_RIGHT_DOMAIN_CREATE_USER,
+ "_samr_CreateUser2");
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ 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 ( acb_info & ACB_WSTRUST )
+ {
+ se_priv_copy( &se_rights, &se_machine_account );
+ can_add_account = user_has_privileges(
+ p->pipe_user.nt_user_token, &se_rights );
+ }
+ /* usrmgr.exe (and net rpc trustdom grant) creates a normal user
+ account for domain trusts and changes the ACB flags later */
+ else if ( acb_info & ACB_NORMAL &&
+ (account[strlen(account)-1] != '$') )
+ {
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_account = user_has_privileges(
+ p->pipe_user.nt_user_token, &se_rights );
+ }
+ else /* implicit assumption of a BDC or domain trust account here
+ * (we already check the flags earlier) */
+ {
+ if ( lp_enable_privileges() ) {
+ /* only Domain Admins can add a BDC or domain trust */
+ se_priv_copy( &se_rights, &se_priv_none );
+ can_add_account = nt_token_check_domain_rid(
+ p->pipe_user.nt_user_token,
+ DOMAIN_GROUP_RID_ADMINS );
+ }
+ }
+
+ DEBUG(5, ("_samr_CreateUser2: %s can add this account : %s\n",
+ uidtoname(p->pipe_user.ut.uid),
+ can_add_account ? "True":"False" ));
+
+ /********** BEGIN Admin BLOCK **********/
+
+ if ( can_add_account )
+ become_root();
+
+ nt_status = pdb_create_user(p->mem_ctx, account, acb_info,
+ r->out.rid);
+
+ if ( can_add_account )
+ unbecome_root();
+
+ /********** 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);
+
+ 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);
+
+ nt_status = access_check_samr_object(psd, p->pipe_user.nt_user_token,
+ &se_rights, GENERIC_RIGHTS_USER_WRITE, des_access,
+ &acc_granted, "_samr_CreateUser2");
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ return nt_status;
+ }
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(info);
+ info->sid = sid;
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* After a "set" ensure we have no cached display info. */
+ force_flush_samr_cache(info->disp_info);
+
+ *r->out.access_granted = acc_granted;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_Connect
+ ********************************************************************/
+
+NTSTATUS _samr_Connect(pipes_struct *p,
+ struct samr_Connect *r)
+{
+ struct samr_info *info = NULL;
+ uint32 des_access = r->in.access_mask;
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _samr_Connect\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* set up the SAMR connect_anon response */
+
+ /* associate the user's SID with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* don't give away the farm but this is probably ok. The SA_RIGHT_SAM_ENUM_DOMAINS
+ was observed from a win98 client trying to enumerate users (when configured
+ user level access control on shares) --jerry */
+
+ if (des_access == MAXIMUM_ALLOWED_ACCESS) {
+ /* Map to max possible knowing we're filtered below. */
+ des_access = GENERIC_ALL_ACCESS;
+ }
+
+ se_map_generic( &des_access, &sam_generic_mapping );
+ info->acc_granted = des_access & (SA_RIGHT_SAM_ENUM_DOMAINS|SA_RIGHT_SAM_OPEN_DOMAIN);
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.connect_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_Connect2
+ ********************************************************************/
+
+NTSTATUS _samr_Connect2(pipes_struct *p,
+ struct samr_Connect2 *r)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ NTSTATUS nt_status;
+ size_t sd_size;
+
+
+ DEBUG(5,("_samr_Connect2: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to _samr_Connect2\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ 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_samr_object(psd, p->pipe_user.nt_user_token,
+ NULL, 0, des_access, &acc_granted, "_samr_Connect2");
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = r->in.access_mask; /* this looks so wrong... - gd */
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.connect_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_Connect2: %d\n", __LINE__));
+
+ return nt_status;
+}
+
+/*******************************************************************
+ _samr_Connect4
+ ********************************************************************/
+
+NTSTATUS _samr_Connect4(pipes_struct *p,
+ struct samr_Connect4 *r)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ NTSTATUS nt_status;
+ size_t sd_size;
+
+
+ DEBUG(5,("_samr_Connect4: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_Connect4\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ 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_samr_object(psd, p->pipe_user.nt_user_token,
+ NULL, 0, des_access, &acc_granted, "_samr_Connect4");
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = r->in.access_mask; /* ??? */
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.connect_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_Connect4: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ _samr_Connect5
+ ********************************************************************/
+
+NTSTATUS _samr_Connect5(pipes_struct *p,
+ struct samr_Connect5 *r)
+{
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ NTSTATUS nt_status;
+ size_t sd_size;
+ struct samr_ConnectInfo1 info1;
+
+ DEBUG(5,("_samr_Connect5: %d\n", __LINE__));
+
+ /* Access check */
+
+ if (!pipe_access_check(p)) {
+ DEBUG(3, ("access denied to samr_Connect5\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ 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_samr_object(psd, p->pipe_user.nt_user_token,
+ NULL, 0, des_access, &acc_granted, "_samr_Connect5");
+
+ if ( !NT_STATUS_IS_OK(nt_status) )
+ return nt_status;
+
+ /* associate the user's SID and access granted with the new handle. */
+ if ((info = get_samr_info_by_sid(NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+ info->status = r->in.access_mask; /* ??? */
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.connect_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ DEBUG(5,("_samr_Connect5: %d\n", __LINE__));
+
+ info1.client_version = SAMR_CONNECT_AFTER_W2K;
+ info1.unknown2 = 0;
+
+ *r->out.level_out = 1;
+ r->out.info_out->info1 = info1;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ _samr_LookupDomain
+ **********************************************************************/
+
+NTSTATUS _samr_LookupDomain(pipes_struct *p,
+ struct samr_LookupDomain *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ struct samr_info *info;
+ const char *domain_name;
+ DOM_SID *sid = NULL;
+
+ if (!find_policy_by_hnd(p, r->in.connect_handle, (void**)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* win9x user manager likes to use SA_RIGHT_SAM_ENUM_DOMAINS here.
+ Reverted that change so we will work with RAS servers again */
+
+ status = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_SAM_OPEN_DOMAIN,
+ "_samr_LookupDomain");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ domain_name = r->in.domain_name->string;
+
+ sid = TALLOC_ZERO_P(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,
+ sid_string_dbg(sid)));
+
+ *r->out.sid = sid;
+
+ return status;
+}
+
+/**********************************************************************
+ _samr_EnumDomains
+ **********************************************************************/
+
+NTSTATUS _samr_EnumDomains(pipes_struct *p,
+ struct samr_EnumDomains *r)
+{
+ NTSTATUS status;
+ struct samr_info *info;
+ uint32_t num_entries = 2;
+ struct samr_SamEntry *entry_array = NULL;
+ struct samr_SamArray *sam;
+
+ if (!find_policy_by_hnd(p, r->in.connect_handle, (void**)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_SAM_ENUM_DOMAINS,
+ "_samr_EnumDomains");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sam = TALLOC_ZERO_P(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(pipes_struct *p,
+ struct samr_OpenAlias *r)
+{
+ DOM_SID sid;
+ POLICY_HND domain_pol = *r->in.domain_handle;
+ uint32 alias_rid = r->in.rid;
+ POLICY_HND *alias_pol = r->out.alias_handle;
+ struct samr_info *info = NULL;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+ SE_PRIV se_rights;
+
+ /* find the domain policy and get the SID / access bits stored in the domain policy */
+
+ if ( !get_lsa_policy_samr_sid(p, &domain_pol, &sid, &acc_granted, NULL) )
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_DOMAIN_OPEN_ACCOUNT,
+ "_samr_OpenAlias");
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ /* append the alias' RID to it */
+
+ if (!sid_append_rid(&sid, alias_rid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ /*check if access can be granted as requested by client. */
+
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &ali_generic_mapping, NULL, 0);
+ se_map_generic(&des_access,&ali_generic_mapping);
+
+ se_priv_copy( &se_rights, &se_add_users );
+
+
+ status = access_check_samr_object(psd, p->pipe_user.nt_user_token,
+ &se_rights, GENERIC_RIGHTS_ALIAS_WRITE, 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;
+ }
+
+ }
+
+ /* associate the alias SID with the new handle. */
+ if ((info = get_samr_info_by_sid(&sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, alias_pol, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ 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"));
+ TALLOC_FREE(pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!id7->account_name.string) {
+ DEBUG(5, ("set_user_info_7: failed to get new username\n"));
+ TALLOC_FREE(pwd);
+ 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);
+ if (!NT_STATUS_IS_OK(rc)) {
+ return rc;
+ }
+
+ rc = pdb_rename_sam_account(pwd, id7->account_name.string);
+
+ TALLOC_FREE(pwd);
+ return rc;
+}
+
+/*******************************************************************
+ set_user_info_16
+ ********************************************************************/
+
+static bool set_user_info_16(struct samr_UserInfo16 *id16,
+ struct samu *pwd)
+{
+ if (id16 == NULL) {
+ DEBUG(5, ("set_user_info_16: NULL id16\n"));
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ /* FIX ME: check if the value is really changed --metze */
+ if (!pdb_set_acct_ctrl(pwd, id16->acct_flags, PDB_CHANGED)) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ if(!NT_STATUS_IS_OK(pdb_update_sam_account(pwd))) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ TALLOC_FREE(pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_18
+ ********************************************************************/
+
+static bool set_user_info_18(struct samr_UserInfo18 *id18,
+ struct samu *pwd)
+{
+ if (id18 == NULL) {
+ DEBUG(2, ("set_user_info_18: id18 is NULL\n"));
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ if (!pdb_set_lanman_passwd (pwd, id18->lm_pwd.hash, PDB_CHANGED)) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+ if (!pdb_set_nt_passwd (pwd, id18->nt_pwd.hash, PDB_CHANGED)) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+ if (!pdb_set_pass_last_set_time (pwd, time(NULL), PDB_CHANGED)) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ if(!NT_STATUS_IS_OK(pdb_update_sam_account(pwd))) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ TALLOC_FREE(pwd);
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_20
+ ********************************************************************/
+
+static bool set_user_info_20(struct samr_UserInfo20 *id20,
+ struct samu *pwd)
+{
+ if (id20 == NULL) {
+ DEBUG(5, ("set_user_info_20: NULL id20\n"));
+ return False;
+ }
+
+ copy_id20_to_sam_passwd(pwd, id20);
+
+ /* write the change out */
+ if(!NT_STATUS_IS_OK(pdb_update_sam_account(pwd))) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ TALLOC_FREE(pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_21
+ ********************************************************************/
+
+static NTSTATUS set_user_info_21(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo21 *id21,
+ struct samu *pwd)
+{
+ NTSTATUS status;
+
+ if (id21 == NULL) {
+ DEBUG(5, ("set_user_info_21: NULL id21\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* 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)));
+ TALLOC_FREE(pwd);
+ 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))) {
+ TALLOC_FREE(pwd);
+ return status;
+ }
+
+ TALLOC_FREE(pwd);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_23
+ ********************************************************************/
+
+static NTSTATUS set_user_info_23(TALLOC_CTX *mem_ctx,
+ struct samr_UserInfo23 *id23,
+ struct samu *pwd)
+{
+ char *plaintext_buf = NULL;
+ uint32 len = 0;
+ uint16 acct_ctrl;
+ NTSTATUS status;
+
+ if (id23 == NULL) {
+ DEBUG(5, ("set_user_info_23: NULL id23\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5, ("Attempting administrator password change (level 23) for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+
+ if (!decode_pw_buffer(mem_ctx,
+ id23->password.data,
+ &plaintext_buf,
+ &len,
+ STR_UNICODE)) {
+ TALLOC_FREE(pwd);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ TALLOC_FREE(pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ copy_id23_to_sam_passwd(pwd, id23);
+
+ /* 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 {
+ /* 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"));
+ TALLOC_FREE(pwd);
+ 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), passwd, "", plaintext_buf, True)) {
+ TALLOC_FREE(pwd);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ TALLOC_FREE(passwd);
+ }
+ }
+
+ memset(plaintext_buf, '\0', strlen(plaintext_buf));
+
+ if (IS_SAM_CHANGED(pwd, PDB_GROUPSID) &&
+ (!NT_STATUS_IS_OK(status = pdb_set_unix_primary_group(mem_ctx,
+ pwd)))) {
+ TALLOC_FREE(pwd);
+ return status;
+ }
+
+ if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
+ TALLOC_FREE(pwd);
+ return status;
+ }
+
+ TALLOC_FREE(pwd);
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ set_user_info_pw
+ ********************************************************************/
+
+static bool set_user_info_pw(uint8 *pass, struct samu *pwd,
+ int level)
+{
+ uint32 len = 0;
+ char *plaintext_buf = NULL;
+ uint32 acct_ctrl;
+ time_t last_set_time;
+ enum pdb_value_state last_set_state;
+
+ DEBUG(5, ("Attempting administrator password change for user %s\n",
+ pdb_get_username(pwd)));
+
+ acct_ctrl = pdb_get_acct_ctrl(pwd);
+ /* we need to know if it's expired, because this is an admin change, not a
+ user change, so it's still expired when we're done */
+ last_set_state = pdb_get_init_flags(pwd, PDB_PASSLASTSET);
+ last_set_time = pdb_get_pass_last_set_time(pwd);
+
+ if (!decode_pw_buffer(talloc_tos(),
+ pass,
+ &plaintext_buf,
+ &len,
+ STR_UNICODE)) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) {
+ TALLOC_FREE(pwd);
+ 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"));
+ TALLOC_FREE(pwd);
+ 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), passwd, "", plaintext_buf, True)) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+ TALLOC_FREE(passwd);
+ }
+ }
+
+ memset(plaintext_buf, '\0', strlen(plaintext_buf));
+
+ /*
+ * A level 25 change does reset the pwdlastset field, a level 24
+ * change does not. I know this is probably not the full story, but
+ * it is needed to make XP join LDAP correctly, without it the later
+ * auth2 check can fail with PWD_MUST_CHANGE.
+ */
+ if (level != 25) {
+ /*
+ * restore last set time as this is an admin change, not a
+ * user pw change
+ */
+ pdb_set_pass_last_set_time (pwd, last_set_time,
+ last_set_state);
+ }
+
+ DEBUG(5,("set_user_info_pw: pdb_update_pwd()\n"));
+
+ /* update the SAMBA password */
+ if(!NT_STATUS_IS_OK(pdb_update_sam_account(pwd))) {
+ TALLOC_FREE(pwd);
+ return False;
+ }
+
+ TALLOC_FREE(pwd);
+
+ return True;
+}
+
+/*******************************************************************
+ set_user_info_25
+ ********************************************************************/
+
+static NTSTATUS set_user_info_25(TALLOC_CTX *mem_ctx,
+ 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;
+ }
+
+ copy_id25_to_sam_passwd(pwd, id25);
+
+ /* write the change out */
+ if(!NT_STATUS_IS_OK(status = pdb_update_sam_account(pwd))) {
+ TALLOC_FREE(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;
+ }
+ }
+
+ /* WARNING: No TALLOC_FREE(pwd), we are about to set the password
+ * hereafter! */
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ samr_SetUserInfo_internal
+ ********************************************************************/
+
+static NTSTATUS samr_SetUserInfo_internal(const char *fn_name,
+ pipes_struct *p,
+ struct policy_handle *user_handle,
+ uint16_t level,
+ union samr_UserInfo *info)
+{
+ NTSTATUS status;
+ struct samu *pwd = NULL;
+ DOM_SID sid;
+ POLICY_HND *pol = user_handle;
+ uint16_t switch_value = level;
+ uint32_t acc_granted;
+ uint32_t acc_required;
+ bool ret;
+ bool has_enough_rights = False;
+ uint32_t acb_info;
+ DISP_INFO *disp_info = NULL;
+
+ DEBUG(5,("%s: %d\n", fn_name, __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, pol, &sid, &acc_granted, &disp_info)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* This is tricky. A WinXP domain join sets
+ (SA_RIGHT_USER_SET_PASSWORD|SA_RIGHT_USER_SET_ATTRIBUTES|SA_RIGHT_USER_ACCT_FLAGS_EXPIRY)
+ The MMC lusrmgr plugin includes these perms and more in the SamrOpenUser(). But the
+ standard Win32 API calls just ask for SA_RIGHT_USER_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 (switch_value) {
+ case 18:
+ case 24:
+ case 25:
+ case 26:
+ acc_required = SA_RIGHT_USER_SET_PASSWORD;
+ break;
+ default:
+ acc_required = SA_RIGHT_USER_SET_PASSWORD |
+ SA_RIGHT_USER_SET_ATTRIBUTES |
+ SA_RIGHT_USER_ACCT_FLAGS_EXPIRY;
+ break;
+ }
+
+ status = access_check_samr_function(acc_granted,
+ acc_required,
+ fn_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(5, ("%s: sid:%s, level:%d\n",
+ fn_name, sid_string_dbg(&sid), switch_value));
+
+ if (info == NULL) {
+ DEBUG(5, ("%s: NULL info level\n", fn_name));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!(pwd = samu_new(NULL))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ become_root();
+ ret = pdb_getsampwsid(pwd, &sid);
+ unbecome_root();
+
+ if (!ret) {
+ TALLOC_FREE(pwd);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* deal with machine password changes differently from userinfo changes */
+ /* check to see if we have the sufficient rights */
+
+ acb_info = pdb_get_acct_ctrl(pwd);
+ if (acb_info & ACB_WSTRUST)
+ has_enough_rights = user_has_privileges(p->pipe_user.nt_user_token,
+ &se_machine_account);
+ else if (acb_info & ACB_NORMAL)
+ has_enough_rights = user_has_privileges(p->pipe_user.nt_user_token,
+ &se_add_users);
+ else if (acb_info & (ACB_SVRTRUST|ACB_DOMTRUST)) {
+ if (lp_enable_privileges()) {
+ has_enough_rights = nt_token_check_domain_rid(p->pipe_user.nt_user_token,
+ DOMAIN_GROUP_RID_ADMINS);
+ }
+ }
+
+ DEBUG(5, ("%s: %s does%s possess sufficient rights\n",
+ fn_name,
+ uidtoname(p->pipe_user.ut.uid),
+ has_enough_rights ? "" : " not"));
+
+ /* ================ BEGIN SeMachineAccountPrivilege BLOCK ================ */
+
+ if (has_enough_rights) {
+ become_root();
+ }
+
+ /* ok! user info levels (lots: see MSDEV help), off we go... */
+
+ switch (switch_value) {
+
+ case 7:
+ status = set_user_info_7(p->mem_ctx,
+ &info->info7, pwd);
+ break;
+
+ case 16:
+ if (!set_user_info_16(&info->info16, pwd)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 18:
+ /* Used by AS/U JRA. */
+ if (!set_user_info_18(&info->info18, pwd)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 20:
+ if (!set_user_info_20(&info->info20, pwd)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 21:
+ status = set_user_info_21(p->mem_ctx,
+ &info->info21, pwd);
+ break;
+
+ case 23:
+ if (!p->server_info->user_session_key.length) {
+ status = NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(info->info23.password.data, 516,
+ &p->server_info->user_session_key);
+
+ dump_data(100, info->info23.password.data, 516);
+
+ status = set_user_info_23(p->mem_ctx,
+ &info->info23, pwd);
+ break;
+
+ case 24:
+ if (!p->server_info->user_session_key.length) {
+ status = NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ SamOEMhashBlob(info->info24.password.data,
+ 516,
+ &p->server_info->user_session_key);
+
+ dump_data(100, info->info24.password.data, 516);
+
+ if (!set_user_info_pw(info->info24.password.data, pwd,
+ switch_value)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 25:
+ if (!p->server_info->user_session_key.length) {
+ status = NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ encode_or_decode_arc4_passwd_buffer(
+ info->info25.password.data,
+ &p->server_info->user_session_key);
+
+ dump_data(100, info->info25.password.data, 532);
+
+ status = set_user_info_25(p->mem_ctx,
+ &info->info25, pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!set_user_info_pw(info->info25.password.data, pwd,
+ switch_value)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ case 26:
+ if (!p->server_info->user_session_key.length) {
+ status = NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ encode_or_decode_arc4_passwd_buffer(
+ info->info26.password.data,
+ &p->server_info->user_session_key);
+
+ dump_data(100, info->info26.password.data, 516);
+
+ if (!set_user_info_pw(info->info26.password.data, pwd,
+ switch_value)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ done:
+
+ if (has_enough_rights) {
+ unbecome_root();
+ }
+
+ /* ================ END SeMachineAccountPrivilege BLOCK ================ */
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(disp_info);
+ }
+
+ return status;
+}
+
+/*******************************************************************
+ _samr_SetUserInfo
+ ********************************************************************/
+
+NTSTATUS _samr_SetUserInfo(pipes_struct *p,
+ struct samr_SetUserInfo *r)
+{
+ return samr_SetUserInfo_internal("_samr_SetUserInfo",
+ p,
+ r->in.user_handle,
+ r->in.level,
+ r->in.info);
+}
+
+/*******************************************************************
+ _samr_SetUserInfo2
+ ********************************************************************/
+
+NTSTATUS _samr_SetUserInfo2(pipes_struct *p,
+ struct samr_SetUserInfo2 *r)
+{
+ return samr_SetUserInfo_internal("_samr_SetUserInfo2",
+ p,
+ r->in.user_handle,
+ r->in.level,
+ r->in.info);
+}
+
+/*********************************************************************
+ _samr_GetAliasMembership
+*********************************************************************/
+
+NTSTATUS _samr_GetAliasMembership(pipes_struct *p,
+ struct samr_GetAliasMembership *r)
+{
+ size_t num_alias_rids;
+ uint32 *alias_rids;
+ struct samr_info *info = NULL;
+ size_t i;
+
+ NTSTATUS ntstatus1;
+ NTSTATUS ntstatus2;
+
+ DOM_SID *members;
+
+ DEBUG(5,("_samr_GetAliasMembership: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ ntstatus1 = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM,
+ "_samr_GetAliasMembership");
+ ntstatus2 = access_check_samr_function(info->acc_granted,
+ SA_RIGHT_DOMAIN_OPEN_ACCOUNT,
+ "_samr_GetAliasMembership");
+
+ if (!NT_STATUS_IS_OK(ntstatus1) || !NT_STATUS_IS_OK(ntstatus2)) {
+ if (!(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus2)) &&
+ !(NT_STATUS_EQUAL(ntstatus1,NT_STATUS_ACCESS_DENIED) && NT_STATUS_IS_OK(ntstatus1))) {
+ return (NT_STATUS_IS_OK(ntstatus1)) ? ntstatus2 : ntstatus1;
+ }
+ }
+
+ if (!sid_check_is_domain(&info->sid) &&
+ !sid_check_is_builtin(&info->sid))
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+
+ if (r->in.sids->num_sids) {
+ members = TALLOC_ARRAY(p->mem_ctx, 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();
+ ntstatus1 = pdb_enum_alias_memberships(p->mem_ctx, &info->sid, members,
+ r->in.sids->num_sids,
+ &alias_rids, &num_alias_rids);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(ntstatus1)) {
+ return ntstatus1;
+ }
+
+ r->out.rids->count = num_alias_rids;
+ r->out.rids->ids = alias_rids;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_GetMembersInAlias
+*********************************************************************/
+
+NTSTATUS _samr_GetMembersInAlias(pipes_struct *p,
+ struct samr_GetMembersInAlias *r)
+{
+ NTSTATUS status;
+ size_t i;
+ size_t num_sids = 0;
+ struct lsa_SidPtr *sids = NULL;
+ DOM_SID *pdb_sids = NULL;
+
+ DOM_SID alias_sid;
+
+ uint32 acc_granted;
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.alias_handle, &alias_sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_ALIAS_GET_MEMBERS,
+ "_samr_GetMembersInAlias");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_dbg(&alias_sid)));
+
+ become_root();
+ status = pdb_enum_aliasmem(&alias_sid, &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 = sid_dup_talloc(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(pipes_struct *p,
+ struct samr_QueryGroupMember *r)
+{
+ DOM_SID group_sid;
+ size_t i, num_members;
+
+ uint32 *rid=NULL;
+ uint32 *attr=NULL;
+
+ uint32 acc_granted;
+
+ NTSTATUS status;
+ struct samr_RidTypeArray *rids = NULL;
+
+ rids = TALLOC_ZERO_P(p->mem_ctx, struct samr_RidTypeArray);
+ if (!rids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* find the policy handle. open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.group_handle, &group_sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_GROUP_GET_MEMBERS,
+ "_samr_QueryGroupMember");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_dbg(&group_sid)));
+
+ if (!sid_check_is_in_our_domain(&group_sid)) {
+ DEBUG(3, ("sid %s is not in our domain\n",
+ sid_string_dbg(&group_sid)));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ DEBUG(10, ("lookup on Domain SID\n"));
+
+ become_root();
+ status = pdb_enum_group_members(p->mem_ctx, &group_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, num_members);
+ if (attr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ attr = NULL;
+ }
+
+ for (i=0; i<num_members; i++)
+ attr[i] = SID_NAME_USER;
+
+ rids->count = num_members;
+ rids->types = attr;
+ rids->rids = rid;
+
+ *r->out.rids = rids;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_AddAliasMember
+*********************************************************************/
+
+NTSTATUS _samr_AddAliasMember(pipes_struct *p,
+ struct samr_AddAliasMember *r)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ NTSTATUS status;
+ DISP_INFO *disp_info = NULL;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.alias_handle, &alias_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_ALIAS_ADD_MEMBER,
+ "_samr_AddAliasMember");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_dbg(&alias_sid)));
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ status = pdb_add_aliasmem(&alias_sid, r->in.sid);
+
+ if ( can_add_accounts )
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(disp_info);
+ }
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_DeleteAliasMember
+*********************************************************************/
+
+NTSTATUS _samr_DeleteAliasMember(pipes_struct *p,
+ struct samr_DeleteAliasMember *r)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ NTSTATUS status;
+ DISP_INFO *disp_info = NULL;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.alias_handle, &alias_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_ALIAS_REMOVE_MEMBER,
+ "_samr_DeleteAliasMember");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("_samr_del_aliasmem:sid is %s\n",
+ sid_string_dbg(&alias_sid)));
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ status = pdb_del_aliasmem(&alias_sid, r->in.sid);
+
+ if ( can_add_accounts )
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(disp_info);
+ }
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_AddGroupMember
+*********************************************************************/
+
+NTSTATUS _samr_AddGroupMember(pipes_struct *p,
+ struct samr_AddGroupMember *r)
+{
+ NTSTATUS status;
+ DOM_SID group_sid;
+ uint32 group_rid;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ DISP_INFO *disp_info = NULL;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.group_handle, &group_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_GROUP_ADD_MEMBER,
+ "_samr_AddGroupMember");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_dbg(&group_sid)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &group_sid,
+ &group_rid)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ status = pdb_add_groupmem(p->mem_ctx, group_rid, r->in.rid);
+
+ if ( can_add_accounts )
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ force_flush_samr_cache(disp_info);
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_DeleteGroupMember
+*********************************************************************/
+
+NTSTATUS _samr_DeleteGroupMember(pipes_struct *p,
+ struct samr_DeleteGroupMember *r)
+
+{
+ NTSTATUS status;
+ DOM_SID group_sid;
+ uint32 group_rid;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ DISP_INFO *disp_info = NULL;
+
+ /*
+ * 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.
+ */
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.group_handle, &group_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_GROUP_REMOVE_MEMBER,
+ "_samr_DeleteGroupMember");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &group_sid,
+ &group_rid)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ status = pdb_del_groupmem(p->mem_ctx, group_rid, r->in.rid);
+
+ if ( can_add_accounts )
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ force_flush_samr_cache(disp_info);
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_DeleteUser
+*********************************************************************/
+
+NTSTATUS _samr_DeleteUser(pipes_struct *p,
+ struct samr_DeleteUser *r)
+{
+ NTSTATUS status;
+ DOM_SID user_sid;
+ struct samu *sam_pass=NULL;
+ uint32 acc_granted;
+ bool can_add_accounts;
+ uint32 acb_info;
+ DISP_INFO *disp_info = NULL;
+ bool ret;
+
+ DEBUG(5, ("_samr_DeleteUser: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.user_handle, &user_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ STD_RIGHT_DELETE_ACCESS,
+ "_samr_DeleteUser");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_check_is_in_our_domain(&user_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, &user_sid);
+ unbecome_root();
+
+ if( !ret ) {
+ DEBUG(5,("_samr_DeleteUser: User %s doesn't exist.\n",
+ sid_string_dbg(&user_sid)));
+ TALLOC_FREE(sam_pass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ acb_info = pdb_get_acct_ctrl(sam_pass);
+
+ /* For machine accounts it's the SeMachineAccountPrivilege that counts. */
+ if ( acb_info & ACB_WSTRUST ) {
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_machine_account );
+ } else {
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_add_users );
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ status = pdb_delete_user(p->mem_ctx, sam_pass);
+
+ if ( can_add_accounts )
+ 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);
+
+ if (!close_policy_hnd(p, r->in.user_handle))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ force_flush_samr_cache(disp_info);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_DeleteDomainGroup
+*********************************************************************/
+
+NTSTATUS _samr_DeleteDomainGroup(pipes_struct *p,
+ struct samr_DeleteDomainGroup *r)
+{
+ NTSTATUS status;
+ DOM_SID group_sid;
+ uint32 group_rid;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ DISP_INFO *disp_info = NULL;
+
+ DEBUG(5, ("samr_DeleteDomainGroup: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.group_handle, &group_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ STD_RIGHT_DELETE_ACCESS,
+ "_samr_DeleteDomainGroup");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_dbg(&group_sid)));
+
+ if (!sid_peek_check_rid(get_global_sam_sid(), &group_sid,
+ &group_rid)) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ status = pdb_delete_dom_group(p->mem_ctx, group_rid);
+
+ if ( can_add_accounts )
+ 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",
+ sid_string_dbg(&group_sid),
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (!close_policy_hnd(p, r->in.group_handle))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ force_flush_samr_cache(disp_info);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_DeleteDomAlias
+*********************************************************************/
+
+NTSTATUS _samr_DeleteDomAlias(pipes_struct *p,
+ struct samr_DeleteDomAlias *r)
+{
+ DOM_SID alias_sid;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ NTSTATUS status;
+ DISP_INFO *disp_info = NULL;
+
+ DEBUG(5, ("_samr_DeleteDomAlias: %d\n", __LINE__));
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.alias_handle, &alias_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* copy the handle to the outgoing reply */
+
+ memcpy(r->out.alias_handle, r->in.alias_handle, sizeof(r->out.alias_handle));
+
+ status = access_check_samr_function(acc_granted,
+ STD_RIGHT_DELETE_ACCESS,
+ "_samr_DeleteDomAlias");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10, ("sid is %s\n", sid_string_dbg(&alias_sid)));
+
+ /* Don't let Windows delete builtin groups */
+
+ if ( sid_check_is_in_builtin( &alias_sid ) ) {
+ return NT_STATUS_SPECIAL_ACCOUNT;
+ }
+
+ if (!sid_check_is_in_our_domain(&alias_sid))
+ return NT_STATUS_NO_SUCH_ALIAS;
+
+ DEBUG(10, ("lookup on Local SID\n"));
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ /* Have passdb delete the alias */
+ status = pdb_delete_alias(&alias_sid);
+
+ if ( can_add_accounts )
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ if ( !NT_STATUS_IS_OK(status))
+ return status;
+
+ if (!close_policy_hnd(p, r->in.alias_handle))
+ return NT_STATUS_OBJECT_NAME_INVALID;
+
+ force_flush_samr_cache(disp_info);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_CreateDomainGroup
+*********************************************************************/
+
+NTSTATUS _samr_CreateDomainGroup(pipes_struct *p,
+ struct samr_CreateDomainGroup *r)
+
+{
+ NTSTATUS status;
+ DOM_SID dom_sid;
+ DOM_SID info_sid;
+ const char *name;
+ struct samr_info *info;
+ uint32 acc_granted;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ DISP_INFO *disp_info = NULL;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &dom_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_DOMAIN_CREATE_GROUP,
+ "_samr_CreateDomainGroup");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!sid_equal(&dom_sid, get_global_sam_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;
+ }
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ /* check that we successfully create the UNIX group */
+
+ status = pdb_create_dom_group(p->mem_ctx, name, r->out.rid);
+
+ if ( can_add_accounts )
+ unbecome_root();
+
+ /******** END SeAddUsers BLOCK *********/
+
+ /* check if we should bail out here */
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ sid_compose(&info_sid, get_global_sam_sid(), *r->out.rid);
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* they created it; let the user do what he wants with it */
+
+ info->acc_granted = GENERIC_RIGHTS_GROUP_ALL_ACCESS;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.group_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ force_flush_samr_cache(disp_info);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_CreateDomAlias
+*********************************************************************/
+
+NTSTATUS _samr_CreateDomAlias(pipes_struct *p,
+ struct samr_CreateDomAlias *r)
+{
+ DOM_SID dom_sid;
+ DOM_SID info_sid;
+ const char *name = NULL;
+ struct samr_info *info;
+ uint32 acc_granted;
+ gid_t gid;
+ NTSTATUS result;
+ SE_PRIV se_rights;
+ bool can_add_accounts;
+ DISP_INFO *disp_info = NULL;
+
+ /* Find the policy handle. Open a policy on it. */
+ if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &dom_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ result = access_check_samr_function(acc_granted,
+ SA_RIGHT_DOMAIN_CREATE_ALIAS,
+ "_samr_CreateDomAlias");
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (!sid_equal(&dom_sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ name = r->in.alias_name->string;
+
+ se_priv_copy( &se_rights, &se_add_users );
+ can_add_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_rights );
+
+ result = can_create(p->mem_ctx, name);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_add_accounts )
+ become_root();
+
+ /* Have passdb create the alias */
+ result = pdb_create_alias(name, r->out.rid);
+
+ if ( can_add_accounts )
+ 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_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_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(10, ("getgrgid(%d) of just created alias failed\n",
+ gid));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* they created it; let the user do what he wants with it */
+
+ info->acc_granted = GENERIC_RIGHTS_ALIAS_ALL_ACCESS;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.alias_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ force_flush_samr_cache(disp_info);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_QueryGroupInfo
+*********************************************************************/
+
+NTSTATUS _samr_QueryGroupInfo(pipes_struct *p,
+ struct samr_QueryGroupInfo *r)
+{
+ NTSTATUS status;
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ union samr_GroupInfo *info = NULL;
+ uint32 acc_granted;
+ bool ret;
+ uint32_t attributes = SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED;
+ const char *group_name = NULL;
+ const char *group_description = NULL;
+
+ if (!get_lsa_policy_samr_sid(p, r->in.group_handle, &group_sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_GROUP_LOOKUP_INFO,
+ "_samr_QueryGroupInfo");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ become_root();
+ ret = get_domain_group_from_sid(group_sid, &map);
+ unbecome_root();
+ if (!ret)
+ return NT_STATUS_INVALID_HANDLE;
+
+ /* FIXME: map contains fstrings */
+ group_name = talloc_strdup(r, map.nt_name);
+ group_description = talloc_strdup(r, map.comment);
+
+ info = TALLOC_ZERO_P(p->mem_ctx, union samr_GroupInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1: {
+ uint32 *members;
+ size_t num_members;
+
+ become_root();
+ status = pdb_enum_group_members(
+ p->mem_ctx, &group_sid, &members, &num_members);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ init_samr_group_info1(&info->all,
+ group_name,
+ attributes,
+ num_members,
+ group_description);
+ break;
+ }
+ case 2:
+ init_samr_group_info2(&info->name,
+ group_name);
+ break;
+ case 3:
+ init_samr_group_info3(&info->attributes,
+ attributes);
+ break;
+ case 4:
+ init_samr_group_info4(&info->description,
+ group_description);
+ break;
+ case 5: {
+ /*
+ uint32 *members;
+ size_t num_members;
+ */
+
+ /*
+ become_root();
+ status = pdb_enum_group_members(
+ p->mem_ctx, &group_sid, &members, &num_members);
+ unbecome_root();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ */
+ init_samr_group_info5(&info->all2,
+ group_name,
+ attributes,
+ 0, /* num_members - in w2k3 this is always 0 */
+ group_description);
+
+ break;
+ }
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_SetGroupInfo
+*********************************************************************/
+
+NTSTATUS _samr_SetGroupInfo(pipes_struct *p,
+ struct samr_SetGroupInfo *r)
+{
+ DOM_SID group_sid;
+ GROUP_MAP map;
+ uint32 acc_granted;
+ NTSTATUS status;
+ bool ret;
+ bool can_mod_accounts;
+ DISP_INFO *disp_info = NULL;
+
+ if (!get_lsa_policy_samr_sid(p, r->in.group_handle, &group_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_GROUP_SET_INFO,
+ "_samr_SetGroupInfo");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ become_root();
+ ret = get_domain_group_from_sid(group_sid, &map);
+ unbecome_root();
+ if (!ret)
+ return NT_STATUS_NO_SUCH_GROUP;
+
+ switch (r->in.level) {
+ case 1:
+ fstrcpy(map.comment, r->in.info->all.description.string);
+ break;
+ case 4:
+ fstrcpy(map.comment, r->in.info->description.string);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ can_mod_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_add_users );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_mod_accounts )
+ become_root();
+
+ status = pdb_update_group_mapping_entry(&map);
+
+ if ( can_mod_accounts )
+ unbecome_root();
+
+ /******** End SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status)) {
+ force_flush_samr_cache(disp_info);
+ }
+
+ return status;
+}
+
+/*********************************************************************
+ _samr_SetAliasInfo
+*********************************************************************/
+
+NTSTATUS _samr_SetAliasInfo(pipes_struct *p,
+ struct samr_SetAliasInfo *r)
+{
+ DOM_SID group_sid;
+ struct acct_info info;
+ uint32 acc_granted;
+ bool can_mod_accounts;
+ NTSTATUS status;
+ DISP_INFO *disp_info = NULL;
+
+ if (!get_lsa_policy_samr_sid(p, r->in.alias_handle, &group_sid, &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_ALIAS_SET_INFO,
+ "_samr_SetAliasInfo");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* get the current group information */
+
+ become_root();
+ status = pdb_get_aliasinfo( &group_sid, &info );
+ unbecome_root();
+
+ if ( !NT_STATUS_IS_OK(status))
+ return status;
+
+ switch (r->in.level) {
+ case ALIASINFONAME:
+ {
+ fstring 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( &group_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;
+
+ fstrcpy( info.acct_name, r->in.info->name.string);
+
+ /* make sure the name doesn't already exist as a user
+ or local group */
+
+ fstr_sprintf( group_name, "%s\\%s", global_myname(), info.acct_name );
+ status = can_create( p->mem_ctx, group_name );
+ if ( !NT_STATUS_IS_OK( status ) )
+ return status;
+ break;
+ }
+ case ALIASINFODESCRIPTION:
+ if (r->in.info->description.string) {
+ fstrcpy(info.acct_desc,
+ r->in.info->description.string);
+ } else {
+ fstrcpy( info.acct_desc, "" );
+ }
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ can_mod_accounts = user_has_privileges( p->pipe_user.nt_user_token, &se_add_users );
+
+ /******** BEGIN SeAddUsers BLOCK *********/
+
+ if ( can_mod_accounts )
+ become_root();
+
+ status = pdb_set_aliasinfo( &group_sid, &info );
+
+ if ( can_mod_accounts )
+ unbecome_root();
+
+ /******** End SeAddUsers BLOCK *********/
+
+ if (NT_STATUS_IS_OK(status))
+ force_flush_samr_cache(disp_info);
+
+ return status;
+}
+
+/****************************************************************
+ _samr_GetDomPwInfo
+****************************************************************/
+
+NTSTATUS _samr_GetDomPwInfo(pipes_struct *p,
+ struct samr_GetDomPwInfo *r)
+{
+ 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(AP_MIN_PASSWORD_LEN,
+ &min_password_length);
+ pdb_get_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS,
+ &password_properties);
+ unbecome_root();
+
+ if (lp_check_password_script() && *lp_check_password_script()) {
+ 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(pipes_struct *p,
+ struct samr_OpenGroup *r)
+
+{
+ DOM_SID sid;
+ DOM_SID info_sid;
+ GROUP_MAP map;
+ struct samr_info *info;
+ SEC_DESC *psd = NULL;
+ uint32 acc_granted;
+ uint32 des_access = r->in.access_mask;
+ size_t sd_size;
+ NTSTATUS status;
+ fstring sid_string;
+ bool ret;
+ SE_PRIV se_rights;
+
+ if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &sid, &acc_granted, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ status = access_check_samr_function(acc_granted,
+ SA_RIGHT_DOMAIN_OPEN_ACCOUNT,
+ "_samr_OpenGroup");
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ /*check if access can be granted as requested by client. */
+ make_samr_object_sd(p->mem_ctx, &psd, &sd_size, &grp_generic_mapping, NULL, 0);
+ se_map_generic(&des_access,&grp_generic_mapping);
+
+ se_priv_copy( &se_rights, &se_add_users );
+
+ status = access_check_samr_object(psd, p->pipe_user.nt_user_token,
+ &se_rights, GENERIC_RIGHTS_GROUP_WRITE, des_access,
+ &acc_granted, "_samr_OpenGroup");
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ /* this should not be hard-coded like this */
+
+ if (!sid_equal(&sid, get_global_sam_sid()))
+ return NT_STATUS_ACCESS_DENIED;
+
+ sid_copy(&info_sid, get_global_sam_sid());
+ sid_append_rid(&info_sid, r->in.rid);
+ sid_to_fstring(sid_string, &info_sid);
+
+ if ((info = get_samr_info_by_sid(&info_sid)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ info->acc_granted = acc_granted;
+
+ DEBUG(10, ("_samr_OpenGroup:Opening SID: %s\n", sid_string));
+
+ /* 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;
+
+ /* get a (unique) handle. open a policy on it. */
+ if (!create_policy_hnd(p, r->out.group_handle, free_samr_info, (void *)info))
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ _samr_RemoveMemberFromForeignDomain
+*********************************************************************/
+
+NTSTATUS _samr_RemoveMemberFromForeignDomain(pipes_struct *p,
+ struct samr_RemoveMemberFromForeignDomain *r)
+{
+ DOM_SID delete_sid, domain_sid;
+ uint32 acc_granted;
+ NTSTATUS result;
+ DISP_INFO *disp_info = NULL;
+
+ sid_copy( &delete_sid, r->in.sid );
+
+ DEBUG(5,("_samr_RemoveMemberFromForeignDomain: removing SID [%s]\n",
+ sid_string_dbg(&delete_sid)));
+
+ /* Find the policy handle. Open a policy on it. */
+
+ if (!get_lsa_policy_samr_sid(p, r->in.domain_handle, &domain_sid,
+ &acc_granted, &disp_info))
+ return NT_STATUS_INVALID_HANDLE;
+
+ result = access_check_samr_function(acc_granted,
+ STD_RIGHT_DELETE_ACCESS,
+ "_samr_RemoveMemberFromForeignDomain");
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ DEBUG(8, ("_samr_RemoveMemberFromForeignDomain: sid is %s\n",
+ sid_string_dbg(&domain_sid)));
+
+ /* 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(&domain_sid)) {
+ DEBUG(1,("_samr_RemoveMemberFromForeignDomain: domain_sid = %s, "
+ "global_sam_sid() = %s\n",
+ sid_string_dbg(&domain_sid),
+ sid_string_dbg(get_global_sam_sid())));
+ DEBUGADD(1,("please report to samba-technical@samba.org!\n"));
+ return NT_STATUS_OK;
+ }
+
+ force_flush_samr_cache(disp_info);
+
+ result = NT_STATUS_OK;
+
+ return result;
+}
+
+/*******************************************************************
+ _samr_QueryDomainInfo2
+ ********************************************************************/
+
+NTSTATUS _samr_QueryDomainInfo2(pipes_struct *p,
+ struct samr_QueryDomainInfo2 *r)
+{
+ return samr_QueryDomainInfo_internal("_samr_QueryDomainInfo2",
+ p,
+ r->in.domain_handle,
+ r->in.level,
+ r->out.info);
+}
+
+/*******************************************************************
+ _samr_SetDomainInfo
+ ********************************************************************/
+
+NTSTATUS _samr_SetDomainInfo(pipes_struct *p,
+ struct samr_SetDomainInfo *r)
+{
+ time_t u_expire, u_min_age;
+ time_t u_logout;
+ time_t u_lock_duration, u_reset_time;
+
+ DEBUG(5,("_samr_SetDomainInfo: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, NULL))
+ return NT_STATUS_INVALID_HANDLE;
+
+ DEBUG(5,("_samr_SetDomainInfo: level: %d\n", r->in.level));
+
+ switch (r->in.level) {
+ case 0x01:
+ u_expire=nt_time_to_unix_abs((NTTIME *)&r->in.info->info1.max_password_age);
+ u_min_age=nt_time_to_unix_abs((NTTIME *)&r->in.info->info1.min_password_age);
+ pdb_set_account_policy(AP_MIN_PASSWORD_LEN, (uint32)r->in.info->info1.min_password_length);
+ pdb_set_account_policy(AP_PASSWORD_HISTORY, (uint32)r->in.info->info1.password_history_length);
+ pdb_set_account_policy(AP_USER_MUST_LOGON_TO_CHG_PASS, (uint32)r->in.info->info1.password_properties);
+ pdb_set_account_policy(AP_MAX_PASSWORD_AGE, (int)u_expire);
+ pdb_set_account_policy(AP_MIN_PASSWORD_AGE, (int)u_min_age);
+ break;
+ case 0x02:
+ break;
+ case 0x03:
+ u_logout=nt_time_to_unix_abs((NTTIME *)&r->in.info->info3.force_logoff_time);
+ pdb_set_account_policy(AP_TIME_TO_LOGOUT, (int)u_logout);
+ break;
+ case 0x05:
+ break;
+ case 0x06:
+ break;
+ case 0x07:
+ break;
+ case 0x0c:
+ u_lock_duration=nt_time_to_unix_abs((NTTIME *)&r->in.info->info12.lockout_duration);
+ if (u_lock_duration != -1)
+ u_lock_duration /= 60;
+
+ u_reset_time=nt_time_to_unix_abs((NTTIME *)&r->in.info->info12.lockout_window)/60;
+
+ pdb_set_account_policy(AP_LOCK_ACCOUNT_DURATION, (int)u_lock_duration);
+ pdb_set_account_policy(AP_RESET_COUNT_TIME, (int)u_reset_time);
+ pdb_set_account_policy(AP_BAD_ATTEMPT_LOCKOUT, (uint32)r->in.info->info12.lockout_threshold);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ DEBUG(5,("_samr_SetDomainInfo: %d\n", __LINE__));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+ _samr_GetDisplayEnumerationIndex
+****************************************************************/
+
+NTSTATUS _samr_GetDisplayEnumerationIndex(pipes_struct *p,
+ struct samr_GetDisplayEnumerationIndex *r)
+{
+ struct samr_info *info = NULL;
+ uint32_t max_entries = (uint32_t) -1;
+ uint32_t enum_context = 0;
+ int i;
+ uint32_t num_account = 0;
+ struct samr_displayentry *entries = NULL;
+
+ DEBUG(5,("_samr_GetDisplayEnumerationIndex: %d\n", __LINE__));
+
+ /* find the policy handle. open a policy on it. */
+ if (!find_policy_by_hnd(p, r->in.domain_handle, (void **)(void *)&info)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ 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 (info->disp_info->users == NULL) {
+ info->disp_info->users = pdb_search_users(ACB_NORMAL);
+ if (info->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(info->disp_info->users,
+ enum_context, max_entries,
+ &entries);
+ break;
+ case 2:
+ if (info->disp_info->machines == NULL) {
+ info->disp_info->machines =
+ pdb_search_users(ACB_WSTRUST|ACB_SVRTRUST);
+ if (info->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(info->disp_info->machines,
+ enum_context, max_entries,
+ &entries);
+ break;
+ case 3:
+ if (info->disp_info->groups == NULL) {
+ info->disp_info->groups = pdb_search_groups();
+ if (info->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(info->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(info->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(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);
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_Shutdown(pipes_struct *p,
+ struct samr_Shutdown *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_CreateUser(pipes_struct *p,
+ struct samr_CreateUser *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_SetMemberAttributesOfGroup(pipes_struct *p,
+ struct samr_SetMemberAttributesOfGroup *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_ChangePasswordUser(pipes_struct *p,
+ struct samr_ChangePasswordUser *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_TestPrivateFunctionsDomain(pipes_struct *p,
+ struct samr_TestPrivateFunctionsDomain *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_TestPrivateFunctionsUser(pipes_struct *p,
+ struct samr_TestPrivateFunctionsUser *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_QueryUserInfo2(pipes_struct *p,
+ struct samr_QueryUserInfo2 *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_AddMultipleMembersToAlias(pipes_struct *p,
+ struct samr_AddMultipleMembersToAlias *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_RemoveMultipleMembersFromAlias(pipes_struct *p,
+ struct samr_RemoveMultipleMembersFromAlias *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_OemChangePasswordUser2(pipes_struct *p,
+ struct samr_OemChangePasswordUser2 *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_SetBootKeyInformation(pipes_struct *p,
+ struct samr_SetBootKeyInformation *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_GetBootKeyInformation(pipes_struct *p,
+ struct samr_GetBootKeyInformation *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_Connect3(pipes_struct *p,
+ struct samr_Connect3 *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_RidToSid(pipes_struct *p,
+ struct samr_RidToSid *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_SetDsrmPassword(pipes_struct *p,
+ struct samr_SetDsrmPassword *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS _samr_ValidatePassword(pipes_struct *p,
+ struct samr_ValidatePassword *r)
+{
+ p->rng_fault_state = true;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
diff --git a/source3/rpc_server/srv_samr_util.c b/source3/rpc_server/srv_samr_util.c
new file mode 100644
index 0000000000..ef588aed1a
--- /dev/null
+++ b/source3/rpc_server/srv_samr_util.c
@@ -0,0 +1,382 @@
+/*
+ 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"
+
+#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_UserInfo20 to a struct samu
+**************************************************************/
+
+void copy_id20_to_sam_passwd(struct samu *to,
+ struct samr_UserInfo20 *from)
+{
+ const char *old_string;
+ char *new_string;
+ DATA_BLOB mung;
+
+ if (from == NULL || to == NULL) {
+ return;
+ }
+
+ if (from->parameters.array) {
+ old_string = pdb_get_munged_dial(to);
+ mung = data_blob_const(from->parameters.array,
+ from->parameters.length);
+ new_string = (mung.length == 0) ?
+ NULL : base64_encode_data_blob(talloc_tos(), mung);
+ 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;
+ DATA_BLOB mung;
+ old_string = pdb_get_munged_dial(to);
+
+ mung = data_blob_const(from->parameters.array,
+ from->parameters.length);
+ newstr = (mung.length == 0) ?
+ NULL : base64_encode_data_blob(talloc_tos(), mung);
+ 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)) {
+ 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, 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 == PASS_MUST_CHANGE_AT_NEXT_LOGON) {
+ pdb_set_pass_last_set_time(to, 0, PDB_CHANGED);
+ } 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 */
+ stored_time = pdb_get_pass_last_set_time(to);
+ if (stored_time == 0)
+ pdb_set_pass_last_set_time(to, time(NULL),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_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);
+}
diff --git a/source3/rpc_server/srv_spoolss.c b/source3/rpc_server/srv_spoolss.c
new file mode 100644
index 0000000000..22b3a7607e
--- /dev/null
+++ b/source3/rpc_server/srv_spoolss.c
@@ -0,0 +1,1639 @@
+/*
+ * 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,
+ * Copyright (C) Gerald Carter 2001-2002,
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/********************************************************************
+ * api_spoolss_open_printer_ex (rarely seen - older call)
+ ********************************************************************/
+
+static bool api_spoolss_open_printer(pipes_struct *p)
+{
+ SPOOL_Q_OPEN_PRINTER q_u;
+ SPOOL_R_OPEN_PRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_open_printer("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_open_printer: unable to unmarshall SPOOL_Q_OPEN_PRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_open_printer( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_open_printer("",&r_u,rdata,0)){
+ DEBUG(0,("spoolss_io_r_open_printer: unable to marshall SPOOL_R_OPEN_PRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_open_printer_ex
+ ********************************************************************/
+
+static bool api_spoolss_open_printer_ex(pipes_struct *p)
+{
+ SPOOL_Q_OPEN_PRINTER_EX q_u;
+ SPOOL_R_OPEN_PRINTER_EX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_open_printer_ex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_open_printer_ex: unable to unmarshall SPOOL_Q_OPEN_PRINTER_EX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_open_printer_ex( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_open_printer_ex("",&r_u,rdata,0)){
+ DEBUG(0,("spoolss_io_r_open_printer_ex: unable to marshall SPOOL_R_OPEN_PRINTER_EX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_getprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDATA q_u;
+ SPOOL_R_GETPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_getprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdata: unable to unmarshall SPOOL_Q_GETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdata( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdata: unable to marshall SPOOL_R_GETPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinterdata
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_deleteprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDATA q_u;
+ SPOOL_R_DELETEPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_deleteprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdata: unable to unmarshall SPOOL_Q_DELETEPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdata( p, &q_u, &r_u );
+
+ if (!spoolss_io_r_deleteprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdata: unable to marshall SPOOL_R_DELETEPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_closeprinter(pipes_struct *p)
+{
+ SPOOL_Q_CLOSEPRINTER q_u;
+ SPOOL_R_CLOSEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_closeprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_closeprinter: unable to unmarshall SPOOL_Q_CLOSEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_closeprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_closeprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_closeprinter: unable to marshall SPOOL_R_CLOSEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_abortprinter(pipes_struct *p)
+{
+ SPOOL_Q_ABORTPRINTER q_u;
+ SPOOL_R_ABORTPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_abortprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_abortprinter: unable to unmarshall SPOOL_Q_ABORTPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_abortprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_abortprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_abortprinter: unable to marshall SPOOL_R_ABORTPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_deleteprinter(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTER q_u;
+ SPOOL_R_DELETEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_deleteprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinter: unable to unmarshall SPOOL_Q_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinter(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_deleteprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_deleteprinterdriver
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_deleteprinterdriver(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDRIVER q_u;
+ SPOOL_R_DELETEPRINTERDRIVER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_deleteprinterdriver("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdriver: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdriver(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_deleteprinterdriver("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinter: unable to marshall SPOOL_R_DELETEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ ********************************************************************/
+
+static bool api_spoolss_rffpcnex(pipes_struct *p)
+{
+ SPOOL_Q_RFFPCNEX q_u;
+ SPOOL_R_RFFPCNEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_rffpcnex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_rffpcnex: unable to unmarshall SPOOL_Q_RFFPCNEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_rffpcnex(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_rffpcnex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_rffpcnex: unable to marshall SPOOL_R_RFFPCNEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_rfnpcnex
+ * ReplyFindNextPrinterChangeNotifyEx
+ * called from the spoolss dispatcher
+
+ * Note - this is the *ONLY* function that breaks the RPC call
+ * symmetry in all the other calls. We need to do this to fix
+ * the massive memory allocation problem with thousands of jobs...
+ * JRA.
+ ********************************************************************/
+
+static bool api_spoolss_rfnpcnex(pipes_struct *p)
+{
+ SPOOL_Q_RFNPCNEX q_u;
+ SPOOL_R_RFNPCNEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_rfnpcnex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_rfnpcnex: unable to unmarshall SPOOL_Q_RFNPCNEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_rfnpcnex(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_rfnpcnex("", &r_u, rdata, 0)) {
+ SAFE_FREE(r_u.info.data);
+ DEBUG(0,("spoolss_io_r_rfnpcnex: unable to marshall SPOOL_R_RFNPCNEX.\n"));
+ return False;
+ }
+
+ SAFE_FREE(r_u.info.data);
+
+ return True;
+}
+
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static bool api_spoolss_enumprinters(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERS q_u;
+ SPOOL_R_ENUMPRINTERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprinters("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinters: unable to unmarshall SPOOL_Q_ENUMPRINTERS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinters( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprinters("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinters: unable to marshall SPOOL_R_ENUMPRINTERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static bool api_spoolss_getprinter(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTER q_u;
+ SPOOL_R_GETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinter: unable to unmarshall SPOOL_Q_GETPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getprinter: unable to marshall SPOOL_R_GETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static bool api_spoolss_getprinterdriver2(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDRIVER2 q_u;
+ SPOOL_R_GETPRINTERDRIVER2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinterdriver2("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdriver2: unable to unmarshall SPOOL_Q_GETPRINTERDRIVER2.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdriver2(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinterdriver2("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdriver2: unable to marshall SPOOL_R_GETPRINTERDRIVER2.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static bool api_spoolss_startpageprinter(pipes_struct *p)
+{
+ SPOOL_Q_STARTPAGEPRINTER q_u;
+ SPOOL_R_STARTPAGEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_startpageprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_startpageprinter: unable to unmarshall SPOOL_Q_STARTPAGEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_startpageprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_startpageprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_startpageprinter: unable to marshall SPOOL_R_STARTPAGEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static bool api_spoolss_endpageprinter(pipes_struct *p)
+{
+ SPOOL_Q_ENDPAGEPRINTER q_u;
+ SPOOL_R_ENDPAGEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_endpageprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_endpageprinter: unable to unmarshall SPOOL_Q_ENDPAGEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_endpageprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_endpageprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_endpageprinter: unable to marshall SPOOL_R_ENDPAGEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool api_spoolss_startdocprinter(pipes_struct *p)
+{
+ SPOOL_Q_STARTDOCPRINTER q_u;
+ SPOOL_R_STARTDOCPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_startdocprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_startdocprinter: unable to unmarshall SPOOL_Q_STARTDOCPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_startdocprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_startdocprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_startdocprinter: unable to marshall SPOOL_R_STARTDOCPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool api_spoolss_enddocprinter(pipes_struct *p)
+{
+ SPOOL_Q_ENDDOCPRINTER q_u;
+ SPOOL_R_ENDDOCPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enddocprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enddocprinter: unable to unmarshall SPOOL_Q_ENDDOCPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enddocprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enddocprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enddocprinter: unable to marshall SPOOL_R_ENDDOCPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool api_spoolss_writeprinter(pipes_struct *p)
+{
+ SPOOL_Q_WRITEPRINTER q_u;
+ SPOOL_R_WRITEPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_writeprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_writeprinter: unable to unmarshall SPOOL_Q_WRITEPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_writeprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_writeprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_writeprinter: unable to marshall SPOOL_R_WRITEPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+
+****************************************************************************/
+
+static bool api_spoolss_setprinter(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTER q_u;
+ SPOOL_R_SETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinter: unable to unmarshall SPOOL_Q_SETPRINTER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinter("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_setprinter: unable to marshall SPOOL_R_SETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_fcpn(pipes_struct *p)
+{
+ SPOOL_Q_FCPN q_u;
+ SPOOL_R_FCPN r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_fcpn("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_fcpn: unable to unmarshall SPOOL_Q_FCPN.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_fcpn(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_fcpn("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_fcpn: unable to marshall SPOOL_R_FCPN.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_addjob(pipes_struct *p)
+{
+ SPOOL_Q_ADDJOB q_u;
+ SPOOL_R_ADDJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addjob: unable to unmarshall SPOOL_Q_ADDJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_addjob: unable to marshall SPOOL_R_ADDJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumjobs(pipes_struct *p)
+{
+ SPOOL_Q_ENUMJOBS q_u;
+ SPOOL_R_ENUMJOBS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumjobs("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumjobs: unable to unmarshall SPOOL_Q_ENUMJOBS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumjobs(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumjobs("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumjobs: unable to marshall SPOOL_R_ENUMJOBS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_schedulejob(pipes_struct *p)
+{
+ SPOOL_Q_SCHEDULEJOB q_u;
+ SPOOL_R_SCHEDULEJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_schedulejob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_schedulejob: unable to unmarshall SPOOL_Q_SCHEDULEJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_schedulejob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_schedulejob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_schedulejob: unable to marshall SPOOL_R_SCHEDULEJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_setjob(pipes_struct *p)
+{
+ SPOOL_Q_SETJOB q_u;
+ SPOOL_R_SETJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setjob: unable to unmarshall SPOOL_Q_SETJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_setjob: unable to marshall SPOOL_R_SETJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprinterdrivers(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDRIVERS q_u;
+ SPOOL_R_ENUMPRINTERDRIVERS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprinterdrivers("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdrivers: unable to unmarshall SPOOL_Q_ENUMPRINTERDRIVERS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdrivers(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprinterdrivers("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdrivers: unable to marshall SPOOL_R_ENUMPRINTERDRIVERS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_getform(pipes_struct *p)
+{
+ SPOOL_Q_GETFORM q_u;
+ SPOOL_R_GETFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_getform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getform: unable to unmarshall SPOOL_Q_GETFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getform(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getform("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getform: unable to marshall SPOOL_R_GETFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumforms(pipes_struct *p)
+{
+ SPOOL_Q_ENUMFORMS q_u;
+ SPOOL_R_ENUMFORMS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumforms("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumforms: unable to unmarshall SPOOL_Q_ENUMFORMS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumforms(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumforms("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumforms: unable to marshall SPOOL_R_ENUMFORMS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumports(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPORTS q_u;
+ SPOOL_R_ENUMPORTS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumports("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumports: unable to unmarshall SPOOL_Q_ENUMPORTS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumports(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumports("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_enumports: unable to marshall SPOOL_R_ENUMPORTS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_addprinterex(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTEREX q_u;
+ SPOOL_R_ADDPRINTEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprinterex: unable to unmarshall SPOOL_Q_ADDPRINTEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterex: unable to marshall SPOOL_R_ADDPRINTEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_addprinterdriver(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTERDRIVER q_u;
+ SPOOL_R_ADDPRINTERDRIVER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterdriver("", &q_u, data, 0)) {
+ if (q_u.level != 3 && q_u.level != 6) {
+ /* Clever hack from Martin Zielinski <mz@seh.de>
+ * to allow downgrade from level 8 (Vista).
+ */
+ DEBUG(3,("api_spoolss_addprinterdriver: unknown SPOOL_Q_ADDPRINTERDRIVER level %u.\n",
+ (unsigned int)q_u.level ));
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_INVALID_TAG));
+ return True;
+ }
+ DEBUG(0,("spoolss_io_q_addprinterdriver: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterdriver(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterdriver("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterdriver: unable to marshall SPOOL_R_ADDPRINTERDRIVER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_getprinterdriverdirectory(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDRIVERDIR q_u;
+ SPOOL_R_GETPRINTERDRIVERDIR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprinterdriverdir("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdriverdir: unable to unmarshall SPOOL_Q_GETPRINTERDRIVERDIR.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdriverdirectory(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprinterdriverdir("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdriverdir: unable to marshall SPOOL_R_GETPRINTERDRIVERDIR.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDATA q_u;
+ SPOOL_R_ENUMPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdata: unable to unmarshall SPOOL_Q_ENUMPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdata(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdata: unable to marshall SPOOL_R_ENUMPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_setprinterdata(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTERDATA q_u;
+ SPOOL_R_SETPRINTERDATA r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinterdata("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinterdata(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinterdata("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static bool api_spoolss_reset_printer(pipes_struct *p)
+{
+ SPOOL_Q_RESETPRINTER q_u;
+ SPOOL_R_RESETPRINTER r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_resetprinter("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdata: unable to unmarshall SPOOL_Q_SETPRINTERDATA.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_resetprinter(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_resetprinter("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdata: unable to marshall SPOOL_R_RESETPRINTER.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static bool api_spoolss_addform(pipes_struct *p)
+{
+ SPOOL_Q_ADDFORM q_u;
+ SPOOL_R_ADDFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addform: unable to unmarshall SPOOL_Q_ADDFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addform: unable to marshall SPOOL_R_ADDFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_deleteform(pipes_struct *p)
+{
+ SPOOL_Q_DELETEFORM q_u;
+ SPOOL_R_DELETEFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteform: unable to unmarshall SPOOL_Q_DELETEFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteform: unable to marshall SPOOL_R_DELETEFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_setform(pipes_struct *p)
+{
+ SPOOL_Q_SETFORM q_u;
+ SPOOL_R_SETFORM r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setform("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setform: unable to unmarshall SPOOL_Q_SETFORM.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setform(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setform("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setform: unable to marshall SPOOL_R_SETFORM.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprintprocessors(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTPROCESSORS q_u;
+ SPOOL_R_ENUMPRINTPROCESSORS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprintprocessors("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintprocessors: unable to unmarshall SPOOL_Q_ENUMPRINTPROCESSORS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintprocessors(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprintprocessors("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintprocessors: unable to marshall SPOOL_R_ENUMPRINTPROCESSORS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_addprintprocessor(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTPROCESSOR q_u;
+ SPOOL_R_ADDPRINTPROCESSOR r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprintprocessor("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_addprintprocessor: unable to unmarshall SPOOL_Q_ADDPRINTPROCESSOR.\n"));
+ return False;
+ }
+
+ /* 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 */
+ r_u.status = WERR_OK;
+
+ if(!spoolss_io_r_addprintprocessor("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprintprocessor: unable to marshall SPOOL_R_ADDPRINTPROCESSOR.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprintprocdatatypes(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTPROCDATATYPES q_u;
+ SPOOL_R_ENUMPRINTPROCDATATYPES r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprintprocdatatypes("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintprocdatatypes: unable to unmarshall SPOOL_Q_ENUMPRINTPROCDATATYPES.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintprocdatatypes(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprintprocdatatypes("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintprocdatatypes: unable to marshall SPOOL_R_ENUMPRINTPROCDATATYPES.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprintmonitors(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTMONITORS q_u;
+ SPOOL_R_ENUMPRINTMONITORS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if (!spoolss_io_q_enumprintmonitors("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprintmonitors: unable to unmarshall SPOOL_Q_ENUMPRINTMONITORS.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprintmonitors(p, &q_u, &r_u);
+
+ if (!spoolss_io_r_enumprintmonitors("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprintmonitors: unable to marshall SPOOL_R_ENUMPRINTMONITORS.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_getjob(pipes_struct *p)
+{
+ SPOOL_Q_GETJOB q_u;
+ SPOOL_R_GETJOB r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getjob("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getjob: unable to unmarshall SPOOL_Q_GETJOB.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getjob(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getjob("",&r_u,rdata,0)) {
+ DEBUG(0,("spoolss_io_r_getjob: unable to marshall SPOOL_R_GETJOB.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ * api_spoolss_getprinterdataex
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+static bool api_spoolss_getprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTERDATAEX q_u;
+ SPOOL_R_GETPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ /* read the stream and fill the struct */
+ if (!spoolss_io_q_getprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprinterdataex: unable to unmarshall SPOOL_Q_GETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprinterdataex( p, &q_u, &r_u);
+
+ if (!spoolss_io_r_getprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprinterdataex: unable to marshall SPOOL_R_GETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_setprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_SETPRINTERDATAEX q_u;
+ SPOOL_R_SETPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_setprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterdataex: unable to unmarshall SPOOL_Q_SETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_setprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_setprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_setprinterdataex: unable to marshall SPOOL_R_SETPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprinterkey(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERKEY q_u;
+ SPOOL_R_ENUMPRINTERKEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterkey("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_setprinterkey: unable to unmarshall SPOOL_Q_ENUMPRINTERKEY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterkey(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterkey("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterkey: unable to marshall SPOOL_R_ENUMPRINTERKEY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_enumprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_ENUMPRINTERDATAEX q_u;
+ SPOOL_R_ENUMPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_enumprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_enumprinterdataex: unable to unmarshall SPOOL_Q_ENUMPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_enumprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_enumprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_enumprinterdataex: unable to marshall SPOOL_R_ENUMPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_getprintprocessordirectory(pipes_struct *p)
+{
+ SPOOL_Q_GETPRINTPROCESSORDIRECTORY q_u;
+ SPOOL_R_GETPRINTPROCESSORDIRECTORY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_getprintprocessordirectory("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_getprintprocessordirectory: unable to unmarshall SPOOL_Q_GETPRINTPROCESSORDIRECTORY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_getprintprocessordirectory(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_getprintprocessordirectory("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_getprintprocessordirectory: unable to marshall SPOOL_R_GETPRINTPROCESSORDIRECTORY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_deleteprinterdataex(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDATAEX q_u;
+ SPOOL_R_DELETEPRINTERDATAEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterdataex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdataex: unable to unmarshall SPOOL_Q_DELETEPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdataex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterdataex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdataex: unable to marshall SPOOL_R_DELETEPRINTERDATAEX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_deleteprinterkey(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERKEY q_u;
+ SPOOL_R_DELETEPRINTERKEY r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterkey("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterkey: unable to unmarshall SPOOL_Q_DELETEPRINTERKEY.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterkey(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterkey("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterkey: unable to marshall SPOOL_R_DELETEPRINTERKEY.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_addprinterdriverex(pipes_struct *p)
+{
+ SPOOL_Q_ADDPRINTERDRIVEREX q_u;
+ SPOOL_R_ADDPRINTERDRIVEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_addprinterdriverex("", &q_u, data, 0)) {
+ if (q_u.level != 3 && q_u.level != 6) {
+ /* Clever hack from Martin Zielinski <mz@seh.de>
+ * to allow downgrade from level 8 (Vista).
+ */
+ DEBUG(3,("api_spoolss_addprinterdriverex: unknown SPOOL_Q_ADDPRINTERDRIVEREX level %u.\n",
+ (unsigned int)q_u.level ));
+ setup_fault_pdu(p, NT_STATUS(DCERPC_FAULT_INVALID_TAG));
+ return True;
+ }
+ DEBUG(0,("spoolss_io_q_addprinterdriverex: unable to unmarshall SPOOL_Q_ADDPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_addprinterdriverex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_addprinterdriverex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_addprinterdriverex: unable to marshall SPOOL_R_ADDPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_deleteprinterdriverex(pipes_struct *p)
+{
+ SPOOL_Q_DELETEPRINTERDRIVEREX q_u;
+ SPOOL_R_DELETEPRINTERDRIVEREX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_deleteprinterdriverex("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_deleteprinterdriverex: unable to unmarshall SPOOL_Q_DELETEPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_deleteprinterdriverex(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_deleteprinterdriverex("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_deleteprinterdriverex: unable to marshall SPOOL_R_DELETEPRINTERDRIVEREX.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool api_spoolss_xcvdataport(pipes_struct *p)
+{
+ SPOOL_Q_XCVDATAPORT q_u;
+ SPOOL_R_XCVDATAPORT r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!spoolss_io_q_xcvdataport("", &q_u, data, 0)) {
+ DEBUG(0,("spoolss_io_q_replyopenprinter: unable to unmarshall SPOOL_Q_XCVDATAPORT.\n"));
+ return False;
+ }
+
+ r_u.status = _spoolss_xcvdataport(p, &q_u, &r_u);
+
+ if(!spoolss_io_r_xcvdataport("", &r_u, rdata, 0)) {
+ DEBUG(0,("spoolss_io_r_replyopenprinter: unable to marshall SPOOL_R_XCVDATAPORT.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+\pipe\spoolss commands
+********************************************************************/
+
+ struct api_struct api_spoolss_cmds[] =
+ {
+ {"SPOOLSS_OPENPRINTER", SPOOLSS_OPENPRINTER, api_spoolss_open_printer },
+ {"SPOOLSS_OPENPRINTEREX", SPOOLSS_OPENPRINTEREX, api_spoolss_open_printer_ex },
+ {"SPOOLSS_GETPRINTERDATA", SPOOLSS_GETPRINTERDATA, api_spoolss_getprinterdata },
+ {"SPOOLSS_CLOSEPRINTER", SPOOLSS_CLOSEPRINTER, api_spoolss_closeprinter },
+ {"SPOOLSS_DELETEPRINTER", SPOOLSS_DELETEPRINTER, api_spoolss_deleteprinter },
+ {"SPOOLSS_ABORTPRINTER", SPOOLSS_ABORTPRINTER, api_spoolss_abortprinter },
+ {"SPOOLSS_RFFPCNEX", SPOOLSS_RFFPCNEX, api_spoolss_rffpcnex },
+ {"SPOOLSS_RFNPCNEX", SPOOLSS_RFNPCNEX, api_spoolss_rfnpcnex },
+ {"SPOOLSS_ENUMPRINTERS", SPOOLSS_ENUMPRINTERS, api_spoolss_enumprinters },
+ {"SPOOLSS_GETPRINTER", SPOOLSS_GETPRINTER, api_spoolss_getprinter },
+ {"SPOOLSS_GETPRINTERDRIVER2", SPOOLSS_GETPRINTERDRIVER2, api_spoolss_getprinterdriver2 },
+ {"SPOOLSS_STARTPAGEPRINTER", SPOOLSS_STARTPAGEPRINTER, api_spoolss_startpageprinter },
+ {"SPOOLSS_ENDPAGEPRINTER", SPOOLSS_ENDPAGEPRINTER, api_spoolss_endpageprinter },
+ {"SPOOLSS_STARTDOCPRINTER", SPOOLSS_STARTDOCPRINTER, api_spoolss_startdocprinter },
+ {"SPOOLSS_ENDDOCPRINTER", SPOOLSS_ENDDOCPRINTER, api_spoolss_enddocprinter },
+ {"SPOOLSS_WRITEPRINTER", SPOOLSS_WRITEPRINTER, api_spoolss_writeprinter },
+ {"SPOOLSS_SETPRINTER", SPOOLSS_SETPRINTER, api_spoolss_setprinter },
+ {"SPOOLSS_FCPN", SPOOLSS_FCPN, api_spoolss_fcpn },
+ {"SPOOLSS_ADDJOB", SPOOLSS_ADDJOB, api_spoolss_addjob },
+ {"SPOOLSS_ENUMJOBS", SPOOLSS_ENUMJOBS, api_spoolss_enumjobs },
+ {"SPOOLSS_SCHEDULEJOB", SPOOLSS_SCHEDULEJOB, api_spoolss_schedulejob },
+ {"SPOOLSS_SETJOB", SPOOLSS_SETJOB, api_spoolss_setjob },
+ {"SPOOLSS_ENUMFORMS", SPOOLSS_ENUMFORMS, api_spoolss_enumforms },
+ {"SPOOLSS_ENUMPORTS", SPOOLSS_ENUMPORTS, api_spoolss_enumports },
+ {"SPOOLSS_ENUMPRINTERDRIVERS", SPOOLSS_ENUMPRINTERDRIVERS, api_spoolss_enumprinterdrivers },
+ {"SPOOLSS_ADDPRINTEREX", SPOOLSS_ADDPRINTEREX, api_spoolss_addprinterex },
+ {"SPOOLSS_ADDPRINTERDRIVER", SPOOLSS_ADDPRINTERDRIVER, api_spoolss_addprinterdriver },
+ {"SPOOLSS_DELETEPRINTERDRIVER", SPOOLSS_DELETEPRINTERDRIVER, api_spoolss_deleteprinterdriver },
+ {"SPOOLSS_GETPRINTERDRIVERDIRECTORY", SPOOLSS_GETPRINTERDRIVERDIRECTORY, api_spoolss_getprinterdriverdirectory },
+ {"SPOOLSS_ENUMPRINTERDATA", SPOOLSS_ENUMPRINTERDATA, api_spoolss_enumprinterdata },
+ {"SPOOLSS_SETPRINTERDATA", SPOOLSS_SETPRINTERDATA, api_spoolss_setprinterdata },
+ {"SPOOLSS_RESETPRINTER", SPOOLSS_RESETPRINTER, api_spoolss_reset_printer },
+ {"SPOOLSS_DELETEPRINTERDATA", SPOOLSS_DELETEPRINTERDATA, api_spoolss_deleteprinterdata },
+ {"SPOOLSS_ADDFORM", SPOOLSS_ADDFORM, api_spoolss_addform },
+ {"SPOOLSS_DELETEFORM", SPOOLSS_DELETEFORM, api_spoolss_deleteform },
+ {"SPOOLSS_GETFORM", SPOOLSS_GETFORM, api_spoolss_getform },
+ {"SPOOLSS_SETFORM", SPOOLSS_SETFORM, api_spoolss_setform },
+ {"SPOOLSS_ADDPRINTPROCESSOR", SPOOLSS_ADDPRINTPROCESSOR, api_spoolss_addprintprocessor },
+ {"SPOOLSS_ENUMPRINTPROCESSORS", SPOOLSS_ENUMPRINTPROCESSORS, api_spoolss_enumprintprocessors },
+ {"SPOOLSS_ENUMMONITORS", SPOOLSS_ENUMMONITORS, api_spoolss_enumprintmonitors },
+ {"SPOOLSS_GETJOB", SPOOLSS_GETJOB, api_spoolss_getjob },
+ {"SPOOLSS_ENUMPRINTPROCDATATYPES", SPOOLSS_ENUMPRINTPROCDATATYPES, api_spoolss_enumprintprocdatatypes },
+ {"SPOOLSS_GETPRINTERDATAEX", SPOOLSS_GETPRINTERDATAEX, api_spoolss_getprinterdataex },
+ {"SPOOLSS_SETPRINTERDATAEX", SPOOLSS_SETPRINTERDATAEX, api_spoolss_setprinterdataex },
+ {"SPOOLSS_DELETEPRINTERDATAEX", SPOOLSS_DELETEPRINTERDATAEX, api_spoolss_deleteprinterdataex },
+ {"SPOOLSS_ENUMPRINTERDATAEX", SPOOLSS_ENUMPRINTERDATAEX, api_spoolss_enumprinterdataex },
+ {"SPOOLSS_ENUMPRINTERKEY", SPOOLSS_ENUMPRINTERKEY, api_spoolss_enumprinterkey },
+ {"SPOOLSS_DELETEPRINTERKEY", SPOOLSS_DELETEPRINTERKEY, api_spoolss_deleteprinterkey },
+ {"SPOOLSS_GETPRINTPROCESSORDIRECTORY",SPOOLSS_GETPRINTPROCESSORDIRECTORY,api_spoolss_getprintprocessordirectory},
+ {"SPOOLSS_ADDPRINTERDRIVEREX", SPOOLSS_ADDPRINTERDRIVEREX, api_spoolss_addprinterdriverex },
+ {"SPOOLSS_DELETEPRINTERDRIVEREX", SPOOLSS_DELETEPRINTERDRIVEREX, api_spoolss_deleteprinterdriverex },
+ {"SPOOLSS_XCVDATAPORT", SPOOLSS_XCVDATAPORT, api_spoolss_xcvdataport },
+};
+
+void spoolss_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_spoolss_cmds;
+ *n_fns = sizeof(api_spoolss_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_spoolss_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ "spoolss", "spoolss", &syntax_spoolss,
+ api_spoolss_cmds,
+ sizeof(api_spoolss_cmds) / sizeof(struct api_struct));
+}
diff --git a/source3/rpc_server/srv_spoolss_nt.c b/source3/rpc_server/srv_spoolss_nt.c
new file mode 100644
index 0000000000..635898a9d5
--- /dev/null
+++ b/source3/rpc_server/srv_spoolss_nt.c
@@ -0,0 +1,9946 @@
+/*
+ * 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.
+ *
+ * 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"
+
+extern userdom_struct current_user_info;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#ifndef MAX_OPEN_PRINTER_EXS
+#define MAX_OPEN_PRINTER_EXS 50
+#endif
+
+#define MAGIC_DISPLAY_FREQUENCY 0xfade2bad
+#define PHANTOM_DEVMODE_KEY "_p_f_a_n_t_0_m_"
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+static Printer_entry *printers_list;
+
+typedef struct _counter_printer_0 {
+ struct _counter_printer_0 *next;
+ struct _counter_printer_0 *prev;
+
+ int snum;
+ uint32 counter;
+} counter_printer_0;
+
+static counter_printer_0 *counter_list;
+
+static struct rpc_pipe_client *notify_cli_pipe; /* print notify back-channel pipe handle*/
+static uint32 smb_connections=0;
+
+
+/* in printing/nt_printing.c */
+
+extern struct standard_mapping printer_std_mapping, printserver_std_mapping;
+
+/* API table for Xcv Monitor functions */
+
+struct xcv_api_table {
+ const char *name;
+ WERROR(*fn) (NT_USER_TOKEN *token, RPC_BUFFER *in, RPC_BUFFER *out, uint32 *needed);
+};
+
+/********************************************************************
+ * Canonicalize servername.
+ ********************************************************************/
+
+static const char *canon_servername(const char *servername)
+{
+ const char *pservername = servername;
+ 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;
+ 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;
+}
+
+/****************************************************************************
+ Functions to handle SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static void free_spool_notify_option(SPOOL_NOTIFY_OPTION **pp)
+{
+ if (*pp == NULL)
+ return;
+
+ SAFE_FREE((*pp)->ctr.type);
+ SAFE_FREE(*pp);
+}
+
+/***************************************************************************
+ Disconnect from the client
+****************************************************************************/
+
+static void srv_spoolss_replycloseprinter(int snum, POLICY_HND *handle)
+{
+ WERROR result;
+
+ /*
+ * 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,("print_notify_register_pid: Failed to register our pid for printer %s\n", lp_const_servicename(snum) ));
+
+ /* weird if the test succeds !!! */
+ if (smb_connections==0) {
+ DEBUG(0,("srv_spoolss_replycloseprinter:Trying to close non-existant notify backchannel !\n"));
+ return;
+ }
+
+ result = rpccli_spoolss_reply_close_printer(notify_cli_pipe,
+ talloc_tos(),
+ handle);
+
+ if (!W_ERROR_IS_OK(result))
+ DEBUG(0,("srv_spoolss_replycloseprinter: reply_close_printer failed [%s].\n",
+ dos_errstr(result)));
+
+ /* if it's the last connection, deconnect the IPC$ share */
+ if (smb_connections==1) {
+
+ cli_shutdown( rpc_pipe_np_smb_conn(notify_cli_pipe) );
+ notify_cli_pipe = NULL; /* The above call shuts downn the pipe also. */
+
+ messaging_deregister(smbd_messaging_context(),
+ MSG_PRINTER_NOTIFY2, NULL);
+
+ /* Tell the connections db we're no longer interested in
+ * printer notify messages. */
+
+ register_message_flags( False, FLAG_MSG_PRINT_NOTIFY );
+ }
+
+ smb_connections--;
+}
+
+/****************************************************************************
+ Functions to free a printer entry datastruct.
+****************************************************************************/
+
+static void free_printer_entry(void *ptr)
+{
+ Printer_entry *Printer = (Printer_entry *)ptr;
+
+ if (Printer->notify.client_connected==True) {
+ int snum = -1;
+
+ if ( Printer->printer_type == SPLHND_SERVER) {
+ snum = -1;
+ srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+ } else if (Printer->printer_type == SPLHND_PRINTER) {
+ snum = print_queue_snum(Printer->sharename);
+ if (snum != -1)
+ srv_spoolss_replycloseprinter(snum,
+ &Printer->notify.client_hnd);
+ }
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ free_spool_notify_option(&Printer->notify.option);
+ Printer->notify.option=NULL;
+ Printer->notify.client_connected=False;
+
+ free_nt_devicemode( &Printer->nt_devmode );
+ free_a_printer( &Printer->printer_info, 2 );
+
+ talloc_destroy( Printer->ctx );
+
+ /* Remove from the internal list. */
+ DLIST_REMOVE(printers_list, Printer);
+
+ SAFE_FREE(Printer);
+}
+
+/****************************************************************************
+ Functions to duplicate a SPOOL_NOTIFY_OPTION struct stored in Printer_entry.
+****************************************************************************/
+
+static SPOOL_NOTIFY_OPTION *dup_spool_notify_option(SPOOL_NOTIFY_OPTION *sp)
+{
+ SPOOL_NOTIFY_OPTION *new_sp = NULL;
+
+ if (!sp)
+ return NULL;
+
+ new_sp = SMB_MALLOC_P(SPOOL_NOTIFY_OPTION);
+ if (!new_sp)
+ return NULL;
+
+ *new_sp = *sp;
+
+ if (sp->ctr.count) {
+ new_sp->ctr.type = (SPOOL_NOTIFY_OPTION_TYPE *)memdup(sp->ctr.type, sizeof(SPOOL_NOTIFY_OPTION_TYPE) * sp->ctr.count);
+
+ if (!new_sp->ctr.type) {
+ SAFE_FREE(new_sp);
+ return NULL;
+ }
+ }
+
+ return new_sp;
+}
+
+/****************************************************************************
+ find printer index by handle
+****************************************************************************/
+
+static Printer_entry *find_printer_index_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *find_printer = NULL;
+
+ if(!find_policy_by_hnd(p,hnd,(void **)(void *)&find_printer)) {
+ DEBUG(2,("find_printer_index_by_hnd: Printer handle not found: "));
+ return NULL;
+ }
+
+ return find_printer;
+}
+
+/****************************************************************************
+ Close printer index by handle.
+****************************************************************************/
+
+static bool close_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *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.
+****************************************************************************/
+
+WERROR delete_printer_hook(TALLOC_CTX *ctx, NT_USER_TOKEN *token, const char *sharename )
+{
+ char *cmd = lp_deleteprinter_cmd();
+ char *command = NULL;
+ int ret;
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+ 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_NOMEM;
+ }
+ if ( token )
+ is_print_op = user_has_privileges( token, &se_printop );
+
+ DEBUG(10,("Running [%s]\n", command));
+
+ /********** BEGIN SePrintOperatorPrivlege BLOCK **********/
+
+ if ( is_print_op )
+ become_root();
+
+ if ( (ret = smbrun(command, NULL)) == 0 ) {
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(smbd_messaging_context(),
+ MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+ }
+
+ if ( is_print_op )
+ unbecome_root();
+
+ /********** END SePrintOperatorPrivlege BLOCK **********/
+
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ TALLOC_FREE(command);
+
+ if (ret != 0)
+ return WERR_BADFID; /* What to return here? */
+
+ /* go ahead and re-read the services immediately */
+ reload_services( False );
+
+ if ( lp_servicenumber( sharename ) < 0 )
+ return WERR_ACCESS_DENIED;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Delete a printer given a handle.
+****************************************************************************/
+
+static WERROR delete_printer_handle(pipes_struct *p, POLICY_HND *hnd)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, hnd);
+
+ if (!Printer) {
+ DEBUG(2,("delete_printer_handle: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(hnd)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * 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 */
+
+ if (del_a_printer( Printer->sharename ) != 0) {
+ DEBUG(3,("Error deleting printer %s\n", Printer->sharename));
+ return WERR_BADFID;
+ }
+
+ return delete_printer_hook(p->mem_ctx, p->pipe_user.nt_user_token, Printer->sharename );
+}
+
+/****************************************************************************
+ Return the snum of a printer corresponding to an handle.
+****************************************************************************/
+
+static bool get_printer_snum(pipes_struct *p, POLICY_HND *hnd, int *number,
+ struct share_params **params)
+{
+ Printer_entry *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(Printer_entry *Printer, char *handlename)
+{
+ DEBUG(3,("Setting printer type=%s\n", handlename));
+
+ if ( strlen(handlename) < 3 ) {
+ DEBUGADD(4,("A print server must have at least 1 char ! %s\n", handlename));
+ return False;
+ }
+
+ /* it's a print server */
+ if (*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;
+}
+
+/****************************************************************************
+ 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 bool set_printer_hnd_name(Printer_entry *Printer, char *handlename)
+{
+ int snum;
+ int n_services=lp_numservices();
+ char *aprinter, *printername;
+ const char *servername;
+ fstring sname;
+ bool found=False;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ WERROR result;
+
+ DEBUG(4,("Setting printer name=%s (len=%lu)\n", handlename, (unsigned long)strlen(handlename)));
+
+ aprinter = handlename;
+ if ( *handlename == '\\' ) {
+ servername = canon_servername(handlename);
+ if ( (aprinter = strchr_m( servername, '\\' )) != NULL ) {
+ *aprinter = '\0';
+ aprinter++;
+ }
+ } else {
+ servername = "";
+ }
+
+ /* save the servername to fill in replies on this handle */
+
+ if ( !is_myname_or_ipaddr( servername ) )
+ return False;
+
+ fstrcpy( Printer->servername, servername );
+
+ if ( Printer->printer_type == SPLHND_SERVER )
+ return True;
+
+ if ( Printer->printer_type != SPLHND_PRINTER )
+ return False;
+
+ DEBUGADD(5, ("searching for [%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;
+ }
+
+ /* 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++) {
+
+ /* no point going on if this is not a printer */
+
+ if ( !(lp_snum_ok(snum) && lp_print_ok(snum)) )
+ continue;
+
+ fstrcpy(sname, lp_servicename(snum));
+ if ( strequal( aprinter, sname ) ) {
+ found = True;
+ break;
+ }
+
+ /* no point looking up the printer object if
+ we aren't allowing printername != sharename */
+
+ if ( lp_force_printername(snum) )
+ continue;
+
+ fstrcpy(sname, lp_servicename(snum));
+
+ printer = NULL;
+
+ /* This call doesn't fill in the location or comment from
+ * a CUPS server for efficiency with large numbers of printers.
+ * JRA.
+ */
+
+ result = get_a_printer_search( NULL, &printer, 2, sname );
+ if ( !W_ERROR_IS_OK(result) ) {
+ DEBUG(0,("set_printer_hnd_name: failed to lookup printer [%s] -- result [%s]\n",
+ sname, dos_errstr(result)));
+ continue;
+ }
+
+ /* printername is always returned as \\server\printername */
+ if ( !(printername = strchr_m(&printer->info_2->printername[2], '\\')) ) {
+ DEBUG(0,("set_printer_hnd_name: info2->printername in wrong format! [%s]\n",
+ printer->info_2->printername));
+ free_a_printer( &printer, 2);
+ continue;
+ }
+
+ printername++;
+
+ if ( strequal(printername, aprinter) ) {
+ free_a_printer( &printer, 2);
+ found = True;
+ break;
+ }
+
+ DEBUGADD(10, ("printername: %s\n", printername));
+
+ free_a_printer( &printer, 2);
+ }
+
+ free_a_printer( &printer, 2);
+
+ if ( !found ) {
+ DEBUGADD(4,("Printer not found\n"));
+ return False;
+ }
+
+ DEBUGADD(4,("set_printer_hnd_name: Printer found: %s -> %s\n", aprinter, sname));
+
+ fstrcpy(Printer->sharename, sname);
+
+ return True;
+}
+
+/****************************************************************************
+ Find first available printer slot. creates a printer handle for you.
+ ****************************************************************************/
+
+static bool open_printer_hnd(pipes_struct *p, POLICY_HND *hnd, char *name, uint32 access_granted)
+{
+ Printer_entry *new_printer;
+
+ DEBUG(10,("open_printer_hnd: name [%s]\n", name));
+
+ if((new_printer=SMB_MALLOC_P(Printer_entry)) == NULL)
+ return False;
+
+ ZERO_STRUCTP(new_printer);
+
+ if (!create_policy_hnd(p, hnd, free_printer_entry, new_printer)) {
+ SAFE_FREE(new_printer);
+ return False;
+ }
+
+ /* Add to the internal list. */
+ DLIST_ADD(printers_list, new_printer);
+
+ new_printer->notify.option=NULL;
+
+ if ( !(new_printer->ctx = talloc_init("Printer Entry [%p]", hnd)) ) {
+ DEBUG(0,("open_printer_hnd: talloc_init() failed!\n"));
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ if (!set_printer_hnd_printertype(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ if (!set_printer_hnd_name(new_printer, name)) {
+ close_printer_handle(p, hnd);
+ return False;
+ }
+
+ new_printer->access_granted = access_granted;
+
+ DEBUG(5, ("%d printer handles active\n", (int)p->pipe_handles->count ));
+
+ return True;
+}
+
+/***************************************************************************
+ check to see if the client motify handle is monitoring the notification
+ given by (notify_type, notify_field).
+ **************************************************************************/
+
+static bool is_monitoring_event_flags(uint32 flags, uint16 notify_type,
+ uint16 notify_field)
+{
+ return True;
+}
+
+static bool is_monitoring_event(Printer_entry *p, uint16 notify_type,
+ uint16 notify_field)
+{
+ SPOOL_NOTIFY_OPTION *option = p->notify.option;
+ uint32 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->ctr.type[i].type != notify_type)
+ continue;
+
+ /* Check match for field */
+
+ for (j = 0; j < option->ctr.type[i].count; j++) {
+ if (option->ctr.type[i].fields[j] == 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;
+}
+
+/* Convert a notification message to a SPOOL_NOTIFY_INFO_DATA struct */
+
+static void notify_one_value(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = msg->notify.value[0];
+ data->notify_data.value[1] = 0;
+}
+
+static void notify_string(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ UNISTR2 unistr;
+
+ /* The length of the message includes the trailing \0 */
+
+ init_unistr2(&unistr, msg->notify.data, UNI_STR_TERMINATE);
+
+ data->notify_data.data.length = msg->len * 2;
+ data->notify_data.data.string = TALLOC_ARRAY(mem_ctx, uint16, msg->len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ memcpy(data->notify_data.data.string, unistr.buffer, msg->len * 2);
+}
+
+static void notify_system_time(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ TALLOC_CTX *mem_ctx)
+{
+ SYSTEMTIME systime;
+ prs_struct ps;
+
+ if (msg->len != sizeof(time_t)) {
+ DEBUG(5, ("notify_system_time: received wrong sized message (%d)\n",
+ msg->len));
+ return;
+ }
+
+ if (!prs_init(&ps, RPC_MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL)) {
+ DEBUG(5, ("notify_system_time: prs_init() failed\n"));
+ return;
+ }
+
+ if (!make_systemtime(&systime, gmtime((time_t *)msg->notify.data))) {
+ DEBUG(5, ("notify_system_time: unable to make systemtime\n"));
+ prs_mem_free(&ps);
+ return;
+ }
+
+ if (!spoolss_io_system_time("", &ps, 0, &systime)) {
+ prs_mem_free(&ps);
+ return;
+ }
+
+ data->notify_data.data.length = prs_offset(&ps);
+ if (prs_offset(&ps)) {
+ data->notify_data.data.string = (uint16 *)
+ TALLOC(mem_ctx, prs_offset(&ps));
+ if (!data->notify_data.data.string) {
+ prs_mem_free(&ps);
+ return;
+ }
+ prs_copy_all_data_out((char *)data->notify_data.data.string, &ps);
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+
+ prs_mem_free(&ps);
+}
+
+struct notify2_message_table {
+ const char *name;
+ void (*fn)(struct spoolss_notify_msg *msg,
+ SPOOL_NOTIFY_INFO_DATA *data, TALLOC_CTX *mem_ctx);
+};
+
+static struct notify2_message_table printer_notify_table[] = {
+ /* 0x00 */ { "PRINTER_NOTIFY_SERVER_NAME", notify_string },
+ /* 0x01 */ { "PRINTER_NOTIFY_PRINTER_NAME", notify_string },
+ /* 0x02 */ { "PRINTER_NOTIFY_SHARE_NAME", notify_string },
+ /* 0x03 */ { "PRINTER_NOTIFY_PORT_NAME", notify_string },
+ /* 0x04 */ { "PRINTER_NOTIFY_DRIVER_NAME", notify_string },
+ /* 0x05 */ { "PRINTER_NOTIFY_COMMENT", notify_string },
+ /* 0x06 */ { "PRINTER_NOTIFY_LOCATION", notify_string },
+ /* 0x07 */ { "PRINTER_NOTIFY_DEVMODE", NULL },
+ /* 0x08 */ { "PRINTER_NOTIFY_SEPFILE", notify_string },
+ /* 0x09 */ { "PRINTER_NOTIFY_PRINT_PROCESSOR", notify_string },
+ /* 0x0a */ { "PRINTER_NOTIFY_PARAMETERS", NULL },
+ /* 0x0b */ { "PRINTER_NOTIFY_DATATYPE", notify_string },
+ /* 0x0c */ { "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "PRINTER_NOTIFY_ATTRIBUTES", notify_one_value },
+ /* 0x0e */ { "PRINTER_NOTIFY_PRIORITY", notify_one_value },
+ /* 0x0f */ { "PRINTER_NOTIFY_DEFAULT_PRIORITY", NULL },
+ /* 0x10 */ { "PRINTER_NOTIFY_START_TIME", NULL },
+ /* 0x11 */ { "PRINTER_NOTIFY_UNTIL_TIME", NULL },
+ /* 0x12 */ { "PRINTER_NOTIFY_STATUS", notify_one_value },
+};
+
+static struct notify2_message_table job_notify_table[] = {
+ /* 0x00 */ { "JOB_NOTIFY_PRINTER_NAME", NULL },
+ /* 0x01 */ { "JOB_NOTIFY_MACHINE_NAME", NULL },
+ /* 0x02 */ { "JOB_NOTIFY_PORT_NAME", NULL },
+ /* 0x03 */ { "JOB_NOTIFY_USER_NAME", notify_string },
+ /* 0x04 */ { "JOB_NOTIFY_NOTIFY_NAME", NULL },
+ /* 0x05 */ { "JOB_NOTIFY_DATATYPE", NULL },
+ /* 0x06 */ { "JOB_NOTIFY_PRINT_PROCESSOR", NULL },
+ /* 0x07 */ { "JOB_NOTIFY_PARAMETERS", NULL },
+ /* 0x08 */ { "JOB_NOTIFY_DRIVER_NAME", NULL },
+ /* 0x09 */ { "JOB_NOTIFY_DEVMODE", NULL },
+ /* 0x0a */ { "JOB_NOTIFY_STATUS", notify_one_value },
+ /* 0x0b */ { "JOB_NOTIFY_STATUS_STRING", NULL },
+ /* 0x0c */ { "JOB_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+ /* 0x0d */ { "JOB_NOTIFY_DOCUMENT", notify_string },
+ /* 0x0e */ { "JOB_NOTIFY_PRIORITY", NULL },
+ /* 0x0f */ { "JOB_NOTIFY_POSITION", NULL },
+ /* 0x10 */ { "JOB_NOTIFY_SUBMITTED", notify_system_time },
+ /* 0x11 */ { "JOB_NOTIFY_START_TIME", NULL },
+ /* 0x12 */ { "JOB_NOTIFY_UNTIL_TIME", NULL },
+ /* 0x13 */ { "JOB_NOTIFY_TIME", NULL },
+ /* 0x14 */ { "JOB_NOTIFY_TOTAL_PAGES", notify_one_value },
+ /* 0x15 */ { "JOB_NOTIFY_PAGES_PRINTED", NULL },
+ /* 0x16 */ { "JOB_NOTIFY_TOTAL_BYTES", notify_one_value },
+ /* 0x17 */ { "JOB_NOTIFY_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 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 int 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;
+ int 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_ARRAY( 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_ARRAY( 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;
+}
+
+/***********************************************************************
+ Send a change notication message on all handles which have a call
+ back registered
+ **********************************************************************/
+
+static void send_notify2_changes( SPOOLSS_NOTIFY_MSG_CTR *ctr, uint32 idx )
+{
+ Printer_entry *p;
+ TALLOC_CTX *mem_ctx = notify_ctr_getctx( ctr );
+ SPOOLSS_NOTIFY_MSG_GROUP *msg_group = notify_ctr_getgroup( ctr, idx );
+ SPOOLSS_NOTIFY_MSG *messages;
+ int sending_msg_count;
+
+ if ( !msg_group ) {
+ DEBUG(5,("send_notify2_changes() called with no msg group!\n"));
+ return;
+ }
+
+ messages = msg_group->msgs;
+
+ if ( !messages ) {
+ 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) {
+ SPOOL_NOTIFY_INFO_DATA *data;
+ uint32 data_len = 0;
+ uint32 id;
+ int i;
+
+ /* Is there notification on this handle? */
+
+ if ( !p->notify.client_connected )
+ continue;
+
+ DEBUG(10,("Client connected! [\\\\%s\\%s]\n", p->servername, p->sharename));
+
+ /* For this printer? Print servers always receive
+ notifications. */
+
+ if ( ( p->printer_type == SPLHND_PRINTER ) &&
+ ( !strequal(msg_group->printername, p->sharename) ) )
+ continue;
+
+ DEBUG(10,("Our printer\n"));
+
+ /* allocate the max entries possible */
+
+ data = TALLOC_ARRAY( mem_ctx, SPOOL_NOTIFY_INFO_DATA, msg_group->num_msgs);
+ if (!data) {
+ return;
+ }
+
+ ZERO_STRUCTP(data);
+
+ /* build the array of change notifications */
+
+ sending_msg_count = 0;
+
+ for ( i=0; i<msg_group->num_msgs; i++ ) {
+ SPOOLSS_NOTIFY_MSG *msg = &messages[i];
+
+ /* Are we monitoring this event? */
+
+ if (!is_monitoring_event(p, msg->type, msg->field))
+ continue;
+
+ sending_msg_count++;
+
+
+ DEBUG(10,("process_notify2_message: Sending message type [0x%x] field [0x%2x] for printer [%s]\n",
+ msg->type, msg->field, p->sharename));
+
+ /*
+ * if the is a printer notification handle and not a job notification
+ * type, then set the id to 0. Other wise 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 ( ( p->printer_type == SPLHND_PRINTER ) && ( msg->type == PRINTER_NOTIFY_TYPE ) )
+ 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));
+ goto done;
+ }
+ }
+
+ construct_info_data( &data[data_len], 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, &data[data_len], mem_ctx);
+ break;
+
+ case JOB_NOTIFY_TYPE:
+ if ( job_notify_table[msg->field].fn )
+ job_notify_table[msg->field].fn(msg, &data[data_len], mem_ctx);
+ break;
+
+ default:
+ DEBUG(5, ("Unknown notification type %d\n", msg->type));
+ goto done;
+ }
+
+ data_len++;
+ }
+
+ if ( sending_msg_count ) {
+ rpccli_spoolss_rrpcn( notify_cli_pipe, mem_ctx, &p->notify.client_hnd,
+ data_len, data, p->notify.change, 0 );
+ }
+ }
+
+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 tv_sec, tv_usec;
+ size_t offset = 0;
+
+ /* Unpack message */
+
+ offset += tdb_unpack((uint8 *)buf + offset, len - offset, "f",
+ msg->printer);
+
+ offset += tdb_unpack((uint8 *)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 *)buf + offset, len - offset, "dd",
+ &msg->notify.value[0], &msg->notify.value[1]);
+ else
+ tdb_unpack((uint8 *)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 *)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;
+ char *buf = (char *)data->data;
+ char *msg_ptr;
+ size_t msg_len;
+ SPOOLSS_NOTIFY_MSG notify;
+ SPOOLSS_NOTIFY_MSG_CTR messages;
+ int num_groups;
+
+ 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)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(char* drivername)
+{
+ 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(smbd_messaging_context(), procid_self(),
+ MSG_PRINTER_DRVUPGRADE,
+ (uint8 *)drivername, len+1);
+
+ return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTER_DRVUPGRADE message and interate
+ over all printers, upgrading ones as necessary
+ **********************************************************************/
+
+void do_drv_upgrade_printer(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ fstring drivername;
+ int snum;
+ int n_services = lp_numservices();
+ size_t len;
+
+ len = MIN(data->length,sizeof(drivername)-1);
+ strncpy(drivername, (const char *)data->data, len);
+
+ 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_print_ok(snum) )
+ {
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ result = get_a_printer(NULL, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ continue;
+
+ if (printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername))
+ {
+ DEBUG(6,("Updating printer [%s]\n", printer->info_2->printername));
+
+ /* all we care about currently is the change_id */
+
+ result = mod_a_printer(printer, 2);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(3,("do_drv_upgrade_printer: mod_a_printer() failed with status [%s]\n",
+ dos_errstr(result)));
+ }
+ }
+
+ free_a_printer(&printer, 2);
+ }
+ }
+
+ /* all done */
+}
+
+/********************************************************************
+ Update the cache for all printq's with a registered client
+ connection
+ ********************************************************************/
+
+void update_monitored_printq_cache( void )
+{
+ Printer_entry *printer = printers_list;
+ int snum;
+
+ /* loop through all printers and update the cache where
+ client_connected == True */
+ while ( printer )
+ {
+ if ( (printer->printer_type == SPLHND_PRINTER)
+ && printer->notify.client_connected )
+ {
+ snum = print_queue_snum(printer->sharename);
+ print_queue_status( snum, NULL, NULL );
+ }
+
+ printer = printer->next;
+ }
+
+ 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_reset_printerdata(char* drivername)
+{
+ int len = strlen(drivername);
+
+ if (!len)
+ return False;
+
+ DEBUG(10,("srv_spoolss_reset_printerdata: Sending message about resetting printerdata [%s]\n",
+ drivername));
+
+ messaging_send_buf(smbd_messaging_context(), procid_self(),
+ MSG_PRINTERDATA_INIT_RESET,
+ (uint8 *)drivername, len+1);
+
+ return True;
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTERDATA_INIT_RESET message and interate
+ over all printers, resetting printer data as neessary
+ **********************************************************************/
+
+void reset_all_printerdata(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ fstring drivername;
+ int snum;
+ int n_services = lp_numservices();
+ size_t len;
+
+ len = MIN( data->length, sizeof(drivername)-1 );
+ strncpy( drivername, (const char *)data->data, len );
+
+ DEBUG(10,("reset_all_printerdata: 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_print_ok(snum) )
+ {
+ WERROR result;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ result = get_a_printer( NULL, &printer, 2, lp_const_servicename(snum) );
+ if ( !W_ERROR_IS_OK(result) )
+ continue;
+
+ /*
+ * if the printer is bound to the driver,
+ * then reset to the new driver initdata
+ */
+
+ if ( printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername) )
+ {
+ DEBUG(6,("reset_all_printerdata: Updating printer [%s]\n", printer->info_2->printername));
+
+ if ( !set_driver_init(printer, 2) ) {
+ DEBUG(5,("reset_all_printerdata: Error resetting printer data for printer [%s], driver [%s]!\n",
+ printer->info_2->printername, printer->info_2->drivername));
+ }
+
+ result = mod_a_printer( printer, 2 );
+ if ( !W_ERROR_IS_OK(result) ) {
+ DEBUG(3,("reset_all_printerdata: mod_a_printer() failed! (%s)\n",
+ get_dos_error_msg(result)));
+ }
+ }
+
+ free_a_printer( &printer, 2 );
+ }
+ }
+
+ /* all done */
+
+ return;
+}
+
+/********************************************************************
+ Copy routines used by convert_to_openprinterex()
+ *******************************************************************/
+
+static DEVICEMODE* dup_devicemode(TALLOC_CTX *ctx, DEVICEMODE *devmode)
+{
+ DEVICEMODE *d;
+ int len;
+
+ if (!devmode)
+ return NULL;
+
+ DEBUG (8,("dup_devmode\n"));
+
+ /* bulk copy first */
+
+ d = (DEVICEMODE *)TALLOC_MEMDUP(ctx, devmode, sizeof(DEVICEMODE));
+ if (!d)
+ return NULL;
+
+ /* dup the pointer members separately */
+
+ len = unistrlen(devmode->devicename.buffer);
+ if (len != -1) {
+ d->devicename.buffer = TALLOC_ARRAY(ctx, uint16, len);
+ if (!d->devicename.buffer) {
+ return NULL;
+ }
+ if (unistrcpy(d->devicename.buffer, devmode->devicename.buffer) != len)
+ return NULL;
+ }
+
+
+ len = unistrlen(devmode->formname.buffer);
+ if (len != -1) {
+ d->formname.buffer = TALLOC_ARRAY(ctx, uint16, len);
+ if (!d->formname.buffer) {
+ return NULL;
+ }
+ if (unistrcpy(d->formname.buffer, devmode->formname.buffer) != len)
+ return NULL;
+ }
+
+ if (devmode->driverextra) {
+ d->dev_private = (uint8 *)TALLOC_MEMDUP(ctx, devmode->dev_private,
+ devmode->driverextra);
+ if (!d->dev_private) {
+ return NULL;
+ }
+ } else {
+ d->dev_private = NULL;
+ }
+ return d;
+}
+
+static void copy_devmode_ctr(TALLOC_CTX *ctx, DEVMODE_CTR *new_ctr, DEVMODE_CTR *ctr)
+{
+ if (!new_ctr || !ctr)
+ return;
+
+ DEBUG(8,("copy_devmode_ctr\n"));
+
+ new_ctr->size = ctr->size;
+ new_ctr->devmode_ptr = ctr->devmode_ptr;
+
+ if(ctr->devmode_ptr)
+ new_ctr->devmode = dup_devicemode(ctx, ctr->devmode);
+}
+
+static void copy_printer_default(TALLOC_CTX *ctx, PRINTER_DEFAULT *new_def, PRINTER_DEFAULT *def)
+{
+ if (!new_def || !def)
+ return;
+
+ DEBUG(8,("copy_printer_defaults\n"));
+
+ new_def->datatype_ptr = def->datatype_ptr;
+
+ if (def->datatype_ptr)
+ copy_unistr2(&new_def->datatype, &def->datatype);
+
+ copy_devmode_ctr(ctx, &new_def->devmode_cont, &def->devmode_cont);
+
+ new_def->access_required = def->access_required;
+}
+
+/********************************************************************
+ * Convert a SPOOL_Q_OPEN_PRINTER structure to a
+ * SPOOL_Q_OPEN_PRINTER_EX structure
+ ********************************************************************/
+
+static WERROR convert_to_openprinterex(TALLOC_CTX *ctx, SPOOL_Q_OPEN_PRINTER_EX *q_u_ex, SPOOL_Q_OPEN_PRINTER *q_u)
+{
+ if (!q_u_ex || !q_u)
+ return WERR_OK;
+
+ DEBUG(8,("convert_to_openprinterex\n"));
+
+ if ( q_u->printername ) {
+ q_u_ex->printername = TALLOC_ZERO_P( ctx, UNISTR2 );
+ if (q_u_ex->printername == NULL)
+ return WERR_NOMEM;
+ copy_unistr2(q_u_ex->printername, q_u->printername);
+ }
+
+ copy_printer_default(ctx, &q_u_ex->printer_default, &q_u->printer_default);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * spoolss_open_printer
+ *
+ * called from the spoolss dispatcher
+ ********************************************************************/
+
+WERROR _spoolss_open_printer(pipes_struct *p, SPOOL_Q_OPEN_PRINTER *q_u, SPOOL_R_OPEN_PRINTER *r_u)
+{
+ SPOOL_Q_OPEN_PRINTER_EX q_u_ex;
+ SPOOL_R_OPEN_PRINTER_EX r_u_ex;
+
+ if (!q_u || !r_u)
+ return WERR_NOMEM;
+
+ ZERO_STRUCT(q_u_ex);
+ ZERO_STRUCT(r_u_ex);
+
+ /* convert the OpenPrinter() call to OpenPrinterEx() */
+
+ r_u_ex.status = convert_to_openprinterex(p->mem_ctx, &q_u_ex, q_u);
+ if (!W_ERROR_IS_OK(r_u_ex.status))
+ return r_u_ex.status;
+
+ r_u_ex.status = _spoolss_open_printer_ex(p, &q_u_ex, &r_u_ex);
+
+ /* convert back to OpenPrinter() */
+
+ memcpy(r_u, &r_u_ex, sizeof(*r_u));
+
+ if (W_ERROR_EQUAL(r_u->status, WERR_INVALID_PARAM)) {
+ /* OpenPrinterEx returns this for a bad
+ * printer name. We must return WERR_INVALID_PRINTER_NAME
+ * instead.
+ */
+ r_u->status = WERR_INVALID_PRINTER_NAME;
+ }
+ return r_u->status;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _spoolss_open_printer_ex( pipes_struct *p, SPOOL_Q_OPEN_PRINTER_EX *q_u, SPOOL_R_OPEN_PRINTER_EX *r_u)
+{
+ PRINTER_DEFAULT *printer_default = &q_u->printer_default;
+ POLICY_HND *handle = &r_u->handle;
+
+ fstring name;
+ int snum;
+ Printer_entry *Printer=NULL;
+
+ if (!q_u->printername) {
+ return WERR_INVALID_PARAM;
+ }
+
+ /* some sanity check because you can open a printer or a print server */
+ /* aka: \\server\printer or \\server */
+
+ unistr2_to_ascii(name, q_u->printername, sizeof(name));
+
+ DEBUGADD(3,("checking name: %s\n",name));
+
+ if (!open_printer_hnd(p, handle, name, 0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ Printer=find_printer_index_by_hnd(p, handle);
+ if ( !Printer ) {
+ DEBUG(0,(" _spoolss_open_printer_ex: logic error. Can't find printer "
+ "handle we created for printer %s\n", name ));
+ close_printer_handle(p,handle);
+ return WERR_INVALID_PARAM;
+ }
+
+ /*
+ * 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;
+
+ /* Map standard access rights to object specific access rights */
+
+ se_map_standard(&printer_default->access_required,
+ &printserver_std_mapping);
+
+ /* Deny any object specific bits that don't apply to print
+ servers (i.e printer and job specific bits) */
+
+ printer_default->access_required &= SPECIFIC_RIGHTS_MASK;
+
+ if (printer_default->access_required &
+ ~(SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE)) {
+ DEBUG(3, ("access DENIED for non-printserver bits\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Allow admin access */
+
+ if ( printer_default->access_required & SERVER_ACCESS_ADMINISTER )
+ {
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+
+ if (!lp_ms_add_printer_wizard()) {
+ close_printer_handle(p, 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 ((p->pipe_user.ut.uid != 0) &&
+ !user_has_privileges(p->pipe_user.nt_user_token,
+ &se_printop ) &&
+ !token_contains_name_in_list(
+ uidtoname(p->pipe_user.ut.uid),
+ NULL, NULL,
+ p->pipe_user.nt_user_token,
+ lp_printer_admin(snum))) {
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ printer_default->access_required = SERVER_ACCESS_ADMINISTER;
+ }
+ else
+ {
+ printer_default->access_required = SERVER_ACCESS_ENUMERATE;
+ }
+
+ DEBUG(4,("Setting print server access = %s\n", (printer_default->access_required == SERVER_ACCESS_ADMINISTER)
+ ? "SERVER_ACCESS_ADMINISTER" : "SERVER_ACCESS_ENUMERATE" ));
+
+ /* We fall through to return WERR_OK */
+ 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, handle, &snum, NULL)) {
+ close_printer_handle(p, handle);
+ return WERR_BADFID;
+ }
+
+ se_map_standard(&printer_default->access_required, &printer_std_mapping);
+
+ /* map an empty access mask to the minimum access mask */
+ if (printer_default->access_required == 0x0)
+ printer_default->access_required = 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)
+ && (printer_default->access_required & PRINTER_ACCESS_ADMINISTER))
+ {
+ printer_default->access_required = PRINTER_ACCESS_USE;
+ }
+
+ /* check smb.conf parameters and the the sec_desc */
+
+ if ( !check_access(smbd_server_fd(), lp_hostsallow(snum), lp_hostsdeny(snum)) ) {
+ DEBUG(3, ("access DENIED (hosts allow/deny) for printer open\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!user_ok_token(uidtoname(p->pipe_user.ut.uid), NULL,
+ p->pipe_user.nt_user_token, snum) ||
+ !print_access_check(p->server_info, snum,
+ printer_default->access_required)) {
+ DEBUG(3, ("access DENIED for printer open\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if ((printer_default->access_required & SPECIFIC_RIGHTS_MASK)& ~(PRINTER_ACCESS_ADMINISTER|PRINTER_ACCESS_USE)) {
+ DEBUG(3, ("access DENIED for printer open - unknown bits\n"));
+ close_printer_handle(p, handle);
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (printer_default->access_required & PRINTER_ACCESS_ADMINISTER)
+ printer_default->access_required = PRINTER_ACCESS_ADMINISTER;
+ else
+ printer_default->access_required = PRINTER_ACCESS_USE;
+
+ DEBUG(4,("Setting printer access = %s\n", (printer_default->access_required == PRINTER_ACCESS_ADMINISTER)
+ ? "PRINTER_ACCESS_ADMINISTER" : "PRINTER_ACCESS_USE" ));
+
+ break;
+
+ default:
+ /* sanity check to prevent programmer error */
+ return WERR_BADFID;
+ }
+
+ Printer->access_granted = printer_default->access_required;
+
+ /*
+ * 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)
+ && q_u->printer_default.devmode_cont.devmode_ptr )
+ {
+ convert_devicemode( Printer->sharename, q_u->printer_default.devmode_cont.devmode,
+ &Printer->nt_devmode );
+ }
+
+#if 0 /* JERRY -- I'm doubtful this is really effective */
+ /* HACK ALERT!!! Sleep for 1/3 of a second to try trigger a LAN/WAN
+ optimization in Windows 2000 clients --jerry */
+
+ if ( (printer_default->access_required == PRINTER_ACCESS_ADMINISTER)
+ && (RA_WIN2K == get_remote_arch()) )
+ {
+ DEBUG(10,("_spoolss_open_printer_ex: Enabling LAN/WAN hack for Win2k clients.\n"));
+ sys_usleep( 500000 );
+ }
+#endif
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool convert_printer_info(const SPOOL_PRINTER_INFO_LEVEL *uni,
+ NT_PRINTER_INFO_LEVEL *printer, uint32 level)
+{
+ bool ret;
+
+ switch (level) {
+ case 2:
+ /* allocate memory if needed. Messy because
+ convert_printer_info is used to update an existing
+ printer or build a new one */
+
+ if ( !printer->info_2 ) {
+ printer->info_2 = TALLOC_ZERO_P( printer, NT_PRINTER_INFO_LEVEL_2 );
+ if ( !printer->info_2 ) {
+ DEBUG(0,("convert_printer_info: talloc() failed!\n"));
+ return False;
+ }
+ }
+
+ ret = uni_2_asc_printer_info_2(uni->info_2, printer->info_2);
+ printer->info_2->setuptime = time(NULL);
+
+ return ret;
+ }
+
+ return False;
+}
+
+static bool convert_printer_driver_info(const SPOOL_PRINTER_DRIVER_INFO_LEVEL *uni,
+ NT_PRINTER_DRIVER_INFO_LEVEL *printer, uint32 level)
+{
+ bool result = True;
+
+ switch (level) {
+ case 3:
+ printer->info_3=NULL;
+ if (!uni_2_asc_printer_driver_3(uni->info_3, &printer->info_3))
+ result = False;
+ break;
+ case 6:
+ printer->info_6=NULL;
+ if (!uni_2_asc_printer_driver_6(uni->info_6, &printer->info_6))
+ result = False;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+bool convert_devicemode(const char *printername, const DEVICEMODE *devmode,
+ NT_DEVICEMODE **pp_nt_devmode)
+{
+ NT_DEVICEMODE *nt_devmode = *pp_nt_devmode;
+
+ /*
+ * Ensure nt_devmode is a valid pointer
+ * as we will be overwriting it.
+ */
+
+ if (nt_devmode == NULL) {
+ DEBUG(5, ("convert_devicemode: allocating a generic devmode\n"));
+ if ((nt_devmode = construct_nt_devicemode(printername)) == NULL)
+ return False;
+ }
+
+ rpcstr_pull(nt_devmode->devicename,devmode->devicename.buffer, 31, -1, 0);
+ rpcstr_pull(nt_devmode->formname,devmode->formname.buffer, 31, -1, 0);
+
+ nt_devmode->specversion=devmode->specversion;
+ nt_devmode->driverversion=devmode->driverversion;
+ nt_devmode->size=devmode->size;
+ nt_devmode->fields=devmode->fields;
+ nt_devmode->orientation=devmode->orientation;
+ nt_devmode->papersize=devmode->papersize;
+ nt_devmode->paperlength=devmode->paperlength;
+ nt_devmode->paperwidth=devmode->paperwidth;
+ nt_devmode->scale=devmode->scale;
+ nt_devmode->copies=devmode->copies;
+ nt_devmode->defaultsource=devmode->defaultsource;
+ nt_devmode->printquality=devmode->printquality;
+ nt_devmode->color=devmode->color;
+ nt_devmode->duplex=devmode->duplex;
+ nt_devmode->yresolution=devmode->yresolution;
+ nt_devmode->ttoption=devmode->ttoption;
+ nt_devmode->collate=devmode->collate;
+
+ nt_devmode->logpixels=devmode->logpixels;
+ nt_devmode->bitsperpel=devmode->bitsperpel;
+ nt_devmode->pelswidth=devmode->pelswidth;
+ nt_devmode->pelsheight=devmode->pelsheight;
+ nt_devmode->displayflags=devmode->displayflags;
+ nt_devmode->displayfrequency=devmode->displayfrequency;
+ nt_devmode->icmmethod=devmode->icmmethod;
+ nt_devmode->icmintent=devmode->icmintent;
+ nt_devmode->mediatype=devmode->mediatype;
+ nt_devmode->dithertype=devmode->dithertype;
+ nt_devmode->reserved1=devmode->reserved1;
+ nt_devmode->reserved2=devmode->reserved2;
+ nt_devmode->panningwidth=devmode->panningwidth;
+ nt_devmode->panningheight=devmode->panningheight;
+
+ /*
+ * Only change private and driverextra if the incoming devmode
+ * has a new one. JRA.
+ */
+
+ if ((devmode->driverextra != 0) && (devmode->dev_private != NULL)) {
+ SAFE_FREE(nt_devmode->nt_dev_private);
+ nt_devmode->driverextra=devmode->driverextra;
+ if((nt_devmode->nt_dev_private=SMB_MALLOC_ARRAY(uint8, nt_devmode->driverextra)) == NULL)
+ return False;
+ memcpy(nt_devmode->nt_dev_private, devmode->dev_private, nt_devmode->driverextra);
+ }
+
+ *pp_nt_devmode = nt_devmode;
+
+ return True;
+}
+
+/********************************************************************
+ * _spoolss_enddocprinter_internal.
+ ********************************************************************/
+
+static WERROR _spoolss_enddocprinter_internal(pipes_struct *p, POLICY_HND *handle)
+{
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ int snum;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enddocprinter_internal: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ Printer->document_started=False;
+ print_job_end(snum, Printer->jobid,NORMAL_CLOSE);
+ /* error codes unhandled so far ... */
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_closeprinter
+ ********************************************************************/
+
+WERROR _spoolss_closeprinter(pipes_struct *p, SPOOL_Q_CLOSEPRINTER *q_u, SPOOL_R_CLOSEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ if (Printer && Printer->document_started)
+ _spoolss_enddocprinter_internal(p, handle); /* print job was not closed */
+
+ if (!close_printer_handle(p, handle))
+ return WERR_BADFID;
+
+ /* 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 */
+
+ memset(&r_u->handle, '\0', sizeof(r_u->handle));
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_deleteprinter
+
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinter(pipes_struct *p, SPOOL_Q_DELETEPRINTER *q_u, SPOOL_R_DELETEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ WERROR result;
+
+ if (Printer && Printer->document_started)
+ _spoolss_enddocprinter_internal(p, handle); /* print job was not closed */
+
+ memcpy(&r_u->handle, &q_u->handle, sizeof(r_u->handle));
+
+ result = delete_printer_handle(p, handle);
+
+ update_c_setprinter(False);
+
+ return result;
+}
+
+/*******************************************************************
+ * static function to lookup the version id corresponding to an
+ * long architecture string
+ ******************************************************************/
+
+static int get_version_id (char * arch)
+{
+ int i;
+ struct table_node archi_table[]= {
+
+ {"Windows 4.0", "WIN40", 0 },
+ {"Windows NT x86", "W32X86", 2 },
+ {"Windows NT R4000", "W32MIPS", 2 },
+ {"Windows NT Alpha_AXP", "W32ALPHA", 2 },
+ {"Windows NT PowerPC", "W32PPC", 2 },
+ {"Windows IA64", "IA64", 3 },
+ {"Windows x64", "x64", 3 },
+ {NULL, "", -1 }
+ };
+
+ 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(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER *q_u, SPOOL_R_DELETEPRINTERDRIVER *r_u)
+{
+ fstring driver;
+ fstring arch;
+ NT_PRINTER_DRIVER_INFO_LEVEL info;
+ NT_PRINTER_DRIVER_INFO_LEVEL info_win2k;
+ int version;
+ WERROR status;
+ WERROR status_win2k = WERR_ACCESS_DENIED;
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ( (p->pipe_user.ut.uid != 0)
+ && !user_has_privileges(p->pipe_user.nt_user_token, &se_printop )
+ && !token_contains_name_in_list(
+ uidtoname(p->pipe_user.ut.uid), NULL,
+ NULL, p->pipe_user.nt_user_token,
+ lp_printer_admin(-1)) )
+ {
+ return WERR_ACCESS_DENIED;
+ }
+
+ unistr2_to_ascii(driver, &q_u->driver, sizeof(driver));
+ unistr2_to_ascii(arch, &q_u->arch, sizeof(arch));
+
+ /* check that we have a valid driver name first */
+
+ if ((version=get_version_id(arch)) == -1)
+ return WERR_INVALID_ENVIRONMENT;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(info_win2k);
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version)))
+ {
+ /* try for Win2k driver if "Windows NT x86" */
+
+ if ( version == 2 ) {
+ version = 3;
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+ /* otherwise it was a failure */
+ else {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+
+ }
+
+ if (printer_driver_in_use(info.info_3)) {
+ status = WERR_PRINTER_DRIVER_IN_USE;
+ goto done;
+ }
+
+ if ( version == 2 )
+ {
+ if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+ {
+ /* if we get to here, we now have 2 driver info structures to remove */
+ /* remove the Win2k driver first*/
+
+ status_win2k = delete_printer_driver(info_win2k.info_3, &p->pipe_user, 3, False );
+ free_a_printer_driver( info_win2k, 3 );
+
+ /* this should not have failed---if it did, report to client */
+ if ( !W_ERROR_IS_OK(status_win2k) )
+ {
+ status = status_win2k;
+ goto done;
+ }
+ }
+ }
+
+ status = delete_printer_driver(info.info_3, &p->pipe_user, version, False);
+
+ /* if at least one of the deletes succeeded return OK */
+
+ if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+ status = WERR_OK;
+
+done:
+ free_a_printer_driver( info, 3 );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriverex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, SPOOL_R_DELETEPRINTERDRIVEREX *r_u)
+{
+ fstring driver;
+ fstring arch;
+ NT_PRINTER_DRIVER_INFO_LEVEL info;
+ NT_PRINTER_DRIVER_INFO_LEVEL info_win2k;
+ int version;
+ uint32 flags = q_u->delete_flags;
+ bool delete_files;
+ WERROR status;
+ WERROR status_win2k = WERR_ACCESS_DENIED;
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+
+ /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
+ and not a printer admin, then fail */
+
+ if ( (p->pipe_user.ut.uid != 0)
+ && !user_has_privileges(p->pipe_user.nt_user_token, &se_printop )
+ && !token_contains_name_in_list(
+ uidtoname(p->pipe_user.ut.uid), NULL, NULL,
+ p->pipe_user.nt_user_token, lp_printer_admin(-1)) )
+ {
+ return WERR_ACCESS_DENIED;
+ }
+
+ unistr2_to_ascii(driver, &q_u->driver, sizeof(driver));
+ unistr2_to_ascii(arch, &q_u->arch, sizeof(arch));
+
+ /* check that we have a valid driver name first */
+ if ((version=get_version_id(arch)) == -1) {
+ /* this is what NT returns */
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ if ( flags & DPD_DELETE_SPECIFIC_VERSION )
+ version = q_u->version;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(info_win2k);
+
+ status = get_a_printer_driver(&info, 3, driver, arch, version);
+
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ /*
+ * if the client asked for a specific version,
+ * or this is something other than Windows NT x86,
+ * then we've failed
+ */
+
+ if ( (flags&DPD_DELETE_SPECIFIC_VERSION) || (version !=2) )
+ goto done;
+
+ /* try for Win2k driver if "Windows NT x86" */
+
+ version = 3;
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+ status = WERR_UNKNOWN_PRINTER_DRIVER;
+ goto done;
+ }
+ }
+
+ if ( printer_driver_in_use(info.info_3) ) {
+ 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_DELTE_ALL_FILE is set,
+ * then the delete should fail if **any** files overlap with
+ * other drivers
+ * (2) If DPD_DELTE_UNUSED_FILES is sert, then delete all
+ * non-overlapping files
+ * (3) If neither DPD_DELTE_ALL_FILE nor DPD_DELTE_ALL_FILES
+ * is set, the do not delete any files
+ * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+ */
+
+ delete_files = flags & (DPD_DELETE_ALL_FILES|DPD_DELETE_UNUSED_FILES);
+
+ /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
+
+ if ( delete_files && printer_driver_files_in_use(info.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+ /* no idea of the correct error here */
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+
+ /* also check for W32X86/3 if necessary; maybe we already have? */
+
+ if ( (version == 2) && ((flags&DPD_DELETE_SPECIFIC_VERSION) != DPD_DELETE_SPECIFIC_VERSION) ) {
+ if (W_ERROR_IS_OK(get_a_printer_driver(&info_win2k, 3, driver, arch, 3)))
+ {
+
+ if ( delete_files && printer_driver_files_in_use(info_win2k.info_3) & (flags&DPD_DELETE_ALL_FILES) ) {
+ /* no idea of the correct error here */
+ free_a_printer_driver( info_win2k, 3 );
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* if we get to here, we now have 2 driver info structures to remove */
+ /* remove the Win2k driver first*/
+
+ status_win2k = delete_printer_driver(info_win2k.info_3, &p->pipe_user, 3, delete_files);
+ free_a_printer_driver( info_win2k, 3 );
+
+ /* this should not have failed---if it did, report to client */
+
+ if ( !W_ERROR_IS_OK(status_win2k) )
+ goto done;
+ }
+ }
+
+ status = delete_printer_driver(info.info_3, &p->pipe_user, version, delete_files);
+
+ if ( W_ERROR_IS_OK(status) || W_ERROR_IS_OK(status_win2k) )
+ status = WERR_OK;
+done:
+ free_a_printer_driver( info, 3 );
+
+ return status;
+}
+
+
+/****************************************************************************
+ Internal routine for retreiving printerdata
+ ***************************************************************************/
+
+static WERROR get_printer_dataex( TALLOC_CTX *ctx, NT_PRINTER_INFO_LEVEL *printer,
+ const char *key, const char *value, uint32 *type, uint8 **data,
+ uint32 *needed, uint32 in_size )
+{
+ REGISTRY_VALUE *val;
+ uint32 size;
+ int data_len;
+
+ if ( !(val = get_printer_data( printer->info_2, key, value)) )
+ return WERR_BADFILE;
+
+ *type = regval_type( val );
+
+ DEBUG(5,("get_printer_dataex: allocating %d\n", in_size));
+
+ size = regval_size( val );
+
+ /* copy the min(in_size, len) */
+
+ if ( in_size ) {
+ data_len = (size > in_size) ? in_size : size*sizeof(uint8);
+
+ /* special case for 0 length values */
+ if ( data_len ) {
+ if ( (*data = (uint8 *)TALLOC_MEMDUP(ctx, regval_data_p(val), data_len)) == NULL )
+ return WERR_NOMEM;
+ }
+ else {
+ if ( (*data = (uint8 *)TALLOC_ZERO(ctx, in_size)) == NULL )
+ return WERR_NOMEM;
+ }
+ }
+ else
+ *data = NULL;
+
+ *needed = size;
+
+ DEBUG(5,("get_printer_dataex: copy done\n"));
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ Internal routine for removing printerdata
+ ***************************************************************************/
+
+static WERROR delete_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value )
+{
+ return delete_printer_data( printer->info_2, key, value );
+}
+
+/****************************************************************************
+ Internal routine for storing printerdata
+ ***************************************************************************/
+
+WERROR set_printer_dataex( NT_PRINTER_INFO_LEVEL *printer, const char *key, const char *value,
+ uint32 type, uint8 *data, int real_len )
+{
+ /* the registry objects enforce uniqueness based on value name */
+
+ return add_printer_data( printer->info_2, key, value, type, data, real_len );
+}
+
+/********************************************************************
+ GetPrinterData on a printer server Handle.
+********************************************************************/
+
+static WERROR getprinterdata_printer_server(TALLOC_CTX *ctx, fstring value, uint32 *type, uint8 **data, uint32 *needed, uint32 in_size)
+{
+ int i;
+
+ DEBUG(8,("getprinterdata_printer_server:%s\n", value));
+
+ if (!StrCaseCmp(value, "W3SvcInstalled")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "BeepEnabled")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "EventLog")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+ /* formally was 0x1b */
+ SIVAL(*data, 0, 0x0);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "NetPopup")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0x00);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "MajorVersion")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+
+ /* 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, 0, 2);
+ else
+ SIVAL(*data, 0, 3);
+
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "MinorVersion")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+ SIVAL(*data, 0, 0);
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ /* REG_BINARY
+ * uint32 size = 0x114
+ * uint32 major = 5
+ * uint32 minor = [0|1]
+ * uint32 build = [2195|2600]
+ * extra unicode string = e.g. "Service Pack 3"
+ */
+ if (!StrCaseCmp(value, "OSVersion")) {
+ *type = REG_BINARY;
+ *needed = 0x114;
+
+ if ( !(*data = TALLOC_ZERO_ARRAY(ctx, uint8, (*needed > in_size) ? *needed:in_size )) )
+ return WERR_NOMEM;
+
+ SIVAL(*data, 0, *needed); /* size */
+ SIVAL(*data, 4, 5); /* Windows 2000 == 5.0 */
+ SIVAL(*data, 8, 0);
+ SIVAL(*data, 12, 2195); /* build */
+
+ /* leave extra string empty */
+
+ return WERR_OK;
+ }
+
+
+ if (!StrCaseCmp(value, "DefaultSpoolDirectory")) {
+ const char *string="C:\\PRINTERS";
+ *type = REG_SZ;
+ *needed = 2*(strlen(string)+1);
+ if((*data = (uint8 *)TALLOC(ctx, (*needed > in_size) ? *needed:in_size )) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+
+ /* it's done by hand ready to go on the wire */
+ for (i=0; i<strlen(string); i++) {
+ (*data)[2*i]=string[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "Architecture")) {
+ const char *string="Windows NT x86";
+ *type = REG_SZ;
+ *needed = 2*(strlen(string)+1);
+ if((*data = (uint8 *)TALLOC(ctx, (*needed > in_size) ? *needed:in_size )) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+ for (i=0; i<strlen(string); i++) {
+ (*data)[2*i]=string[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "DsPresent")) {
+ *type = REG_DWORD;
+ if ( !(*data = TALLOC_ARRAY(ctx, uint8, sizeof(uint32) )) )
+ return WERR_NOMEM;
+
+ /* only show the publish check box if we are a
+ memeber of a AD domain */
+
+ if ( lp_security() == SEC_ADS )
+ SIVAL(*data, 0, 0x01);
+ else
+ SIVAL(*data, 0, 0x00);
+
+ *needed = 0x4;
+ return WERR_OK;
+ }
+
+ if (!StrCaseCmp(value, "DNSMachineName")) {
+ const char *hostname = get_mydnsfullname();
+
+ if (!hostname)
+ return WERR_BADFILE;
+ *type = REG_SZ;
+ *needed = 2*(strlen(hostname)+1);
+ if((*data = (uint8 *)TALLOC(ctx, (*needed > in_size) ? *needed:in_size )) == NULL)
+ return WERR_NOMEM;
+ memset(*data, 0, (*needed > in_size) ? *needed:in_size);
+ for (i=0; i<strlen(hostname); i++) {
+ (*data)[2*i]=hostname[i];
+ (*data)[2*i+1]='\0';
+ }
+ return WERR_OK;
+ }
+
+
+ return WERR_BADFILE;
+}
+
+/********************************************************************
+ * spoolss_getprinterdata
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdata(pipes_struct *p, SPOOL_Q_GETPRINTERDATA *q_u, SPOOL_R_GETPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *valuename = &q_u->valuename;
+ uint32 in_size = q_u->size;
+ uint32 *type = &r_u->type;
+ uint32 *out_size = &r_u->size;
+ uint8 **data = &r_u->data;
+ uint32 *needed = &r_u->needed;
+ WERROR status;
+ fstring value;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+
+ /*
+ * Reminder: when it's a string, the length is in BYTES
+ * even if UNICODE is negociated.
+ *
+ * JFM, 4/19/1999
+ */
+
+ *out_size = in_size;
+
+ /* in case of problem, return some default values */
+
+ *needed = 0;
+ *type = 0;
+
+ DEBUG(4,("_spoolss_getprinterdata\n"));
+
+ if ( !Printer ) {
+ DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ unistr2_to_ascii(value, valuename, sizeof(value));
+
+ if ( Printer->printer_type == SPLHND_SERVER )
+ status = getprinterdata_printer_server( p->mem_ctx, value, type, data, needed, *out_size );
+ else
+ {
+ if ( !get_printer_snum(p,handle, &snum, NULL) ) {
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(status) )
+ goto done;
+
+ /* XP sends this and wants to change id value from the PRINTER_INFO_0 */
+
+ if ( strequal(value, "ChangeId") ) {
+ *type = REG_DWORD;
+ *needed = sizeof(uint32);
+ if ( (*data = (uint8*)TALLOC(p->mem_ctx, sizeof(uint32))) == NULL) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+ SIVAL( *data, 0, printer->info_2->changeid );
+ status = WERR_OK;
+ }
+ else
+ status = get_printer_dataex( p->mem_ctx, printer, SPOOL_PRINTERDATA_KEY, value, type, data, needed, *out_size );
+ }
+
+ if (*needed > *out_size)
+ status = WERR_MORE_DATA;
+
+done:
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ DEBUG(5, ("error %d: allocating %d\n", W_ERROR_V(status),*out_size));
+
+ /* reply this param doesn't exist */
+
+ if ( *out_size ) {
+ if((*data=(uint8 *)TALLOC_ZERO_ARRAY(p->mem_ctx, uint8, *out_size)) == NULL) {
+ if ( printer )
+ free_a_printer( &printer, 2 );
+ return WERR_NOMEM;
+ }
+ } else {
+ *data = NULL;
+ }
+ }
+
+ /* cleanup & exit */
+
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+/*********************************************************
+ Connect to the client machine.
+**********************************************************/
+
+static bool spoolss_connect_to_client(struct rpc_pipe_client **pp_pipe,
+ struct sockaddr_storage *client_ss, const char *remote_machine)
+{
+ NTSTATUS ret;
+ struct cli_state *the_cli;
+ struct sockaddr_storage rm_addr;
+
+ if ( is_zero_addr(client_ss) ) {
+ if ( !resolve_name( remote_machine, &rm_addr, 0x20) ) {
+ DEBUG(2,("spoolss_connect_to_client: Can't resolve address for %s\n", remote_machine));
+ return False;
+ }
+
+ if (ismyaddr(&rm_addr)) {
+ DEBUG(0,("spoolss_connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n", remote_machine));
+ return False;
+ }
+ } else {
+ char addr[INET6_ADDRSTRLEN];
+ 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));
+ }
+
+ /* setup the connection */
+
+ ret = cli_full_connection( &the_cli, global_myname(), remote_machine,
+ &rm_addr, 0, "IPC$", "IPC",
+ "", /* username */
+ "", /* domain */
+ "", /* password */
+ 0, lp_client_signing(), NULL );
+
+ if ( !NT_STATUS_IS_OK( ret ) ) {
+ DEBUG(2,("spoolss_connect_to_client: connection to [%s] failed!\n",
+ remote_machine ));
+ return False;
+ }
+
+ if ( the_cli->protocol != PROTOCOL_NT1 ) {
+ DEBUG(0,("spoolss_connect_to_client: machine %s didn't negotiate NT protocol.\n", remote_machine));
+ cli_shutdown(the_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(the_cli, &syntax_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(the_cli);
+ return False;
+ }
+
+ return True;
+}
+
+/***************************************************************************
+ Connect to the client.
+****************************************************************************/
+
+static bool srv_spoolss_replyopenprinter(int snum, const char *printer,
+ uint32 localprinter, uint32 type,
+ POLICY_HND *handle, struct sockaddr_storage *client_ss)
+{
+ WERROR result;
+
+ /*
+ * If it's the first connection, contact the client
+ * and connect to the IPC$ share anonymously
+ */
+ if (smb_connections==0) {
+ fstring unix_printer;
+
+ fstrcpy(unix_printer, printer+2); /* the +2 is to strip the leading 2 backslashs */
+
+ if ( !spoolss_connect_to_client( &notify_cli_pipe, client_ss, unix_printer ))
+ return False;
+
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_PRINTER_NOTIFY2,
+ receive_notify2_message_list);
+ /* Tell the connections db we're now interested in printer
+ * notify messages. */
+ register_message_flags( True, FLAG_MSG_PRINT_NOTIFY );
+ }
+
+ /*
+ * Tell the specific printing tdb we want messages for this printer
+ * by registering our PID.
+ */
+
+ if (!print_notify_register_pid(snum))
+ DEBUG(0,("print_notify_register_pid: Failed to register our pid for printer %s\n", printer ));
+
+ smb_connections++;
+
+ result = rpccli_spoolss_reply_open_printer(notify_cli_pipe,
+ talloc_tos(),
+ printer,
+ localprinter,
+ type,
+ handle);
+
+ if (!W_ERROR_IS_OK(result))
+ DEBUG(5,("srv_spoolss_reply_open_printer: Client RPC returned [%s]\n",
+ dos_errstr(result)));
+
+ return (W_ERROR_IS_OK(result));
+}
+
+/********************************************************************
+ * _spoolss_rffpcnex
+ * ReplyFindFirstPrinterChangeNotifyEx
+ *
+ * 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_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 flags = q_u->flags;
+ uint32 options = q_u->options;
+ UNISTR2 *localmachine = &q_u->localmachine;
+ uint32 printerlocal = q_u->printerlocal;
+ int snum = -1;
+ SPOOL_NOTIFY_OPTION *option = q_u->option;
+ struct sockaddr_storage client_ss;
+
+ /* store the notify value in the printer struct */
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_rffpcnex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ Printer->notify.flags=flags;
+ Printer->notify.options=options;
+ Printer->notify.printerlocal=printerlocal;
+
+ if (Printer->notify.option)
+ free_spool_notify_option(&Printer->notify.option);
+
+ Printer->notify.option=dup_spool_notify_option(option);
+
+ unistr2_to_ascii(Printer->notify.localmachine, localmachine,
+ sizeof(Printer->notify.localmachine));
+
+ /* 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, handle, &snum, NULL) )
+ return WERR_BADFID;
+
+ if (!interpret_string_addr(&client_ss, p->client_address,
+ AI_NUMERICHOST)) {
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine,
+ Printer->notify.printerlocal, 1,
+ &Printer->notify.client_hnd, &client_ss))
+ return WERR_SERVER_UNAVAILABLE;
+
+ Printer->notify.client_connected=True;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servername
+ ********************************************************************/
+
+void spoolss_notify_server_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->servername);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername (not including the servername).
+ ********************************************************************/
+
+void spoolss_notify_printer_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ /* the notify name should not contain the \\server\ part */
+ char *p = strrchr(printer->info_2->printername, '\\');
+
+ if (!p) {
+ p = printer->info_2->printername;
+ } else {
+ p++;
+ }
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, p);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the servicename
+ ********************************************************************/
+
+void spoolss_notify_share_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, lp_servicename(snum));
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the port name
+ ********************************************************************/
+
+void spoolss_notify_port_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ /* even if it's strange, that's consistant in all the code */
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->portname);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the printername
+ * but it doesn't exist, have to see what to do
+ ********************************************************************/
+
+void spoolss_notify_driver_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->drivername);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ ********************************************************************/
+
+void spoolss_notify_comment(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ if (*printer->info_2->comment == '\0')
+ len = rpcstr_push_talloc(mem_ctx, &temp, lp_comment(snum));
+ else
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->comment);
+
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the comment
+ * location = "Room 1, floor 2, building 3"
+ ********************************************************************/
+
+void spoolss_notify_location(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->location);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * 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(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /* for a dummy implementation we have to zero the fields */
+ data->notify_data.data.length = 0;
+ data->notify_data.data.string = NULL;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the separator file name
+ ********************************************************************/
+
+void spoolss_notify_sepfile(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->sepfile);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor
+ * jfm:xxxx return always winprint to indicate we don't do anything to it
+ ********************************************************************/
+
+void spoolss_notify_print_processor(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->printprocessor);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the print processor options
+ * jfm:xxxx send an empty string
+ ********************************************************************/
+
+void spoolss_notify_parameters(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->parameters);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the data type
+ * jfm:xxxx always send RAW as data type
+ ********************************************************************/
+
+void spoolss_notify_datatype(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, printer->info_2->datatype);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * 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(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.sd.size = printer->info_2->secdesc_buf->sd_size;
+ data->notify_data.sd.desc = dup_sec_desc( mem_ctx, printer->info_2->secdesc_buf->sd ) ;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the attributes
+ * jfm:xxxx a samba printer is always shared
+ ********************************************************************/
+
+void spoolss_notify_attributes(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->attributes;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the priority
+ ********************************************************************/
+
+static void spoolss_notify_priority(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->priority;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the default priority
+ ********************************************************************/
+
+static void spoolss_notify_default_priority(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->default_priority;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the start time
+ ********************************************************************/
+
+static void spoolss_notify_start_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->starttime;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the until time
+ ********************************************************************/
+
+static void spoolss_notify_until_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = printer->info_2->untiltime;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the status
+ ********************************************************************/
+
+static void spoolss_notify_status(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ print_status_struct status;
+
+ print_queue_length(snum, &status);
+ data->notify_data.value[0]=(uint32) status.status;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the number of jobs queued
+ ********************************************************************/
+
+void spoolss_notify_cjobs(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0] = print_queue_length(snum, NULL);
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with the average ppm
+ ********************************************************************/
+
+static void spoolss_notify_average_ppm(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /* always respond 8 pages per minutes */
+ /* a little hard ! */
+ data->notify_data.value[0] = printer->info_2->averageppm;
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with username
+ ********************************************************************/
+
+static void spoolss_notify_username(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, queue->fs_user);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=nt_printj_status(queue->status);
+ data->notify_data.value[1] = 0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job name
+ ********************************************************************/
+
+static void spoolss_notify_job_name(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, queue->fs_file);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job status
+ ********************************************************************/
+
+static void spoolss_notify_job_status_string(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ /*
+ * Now we're returning job status codes we just return a "" here. JRA.
+ */
+
+ const char *p = "";
+ smb_ucs2_t *temp = NULL;
+ uint32 len;
+
+#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. */
+
+ len = rpcstr_push_talloc(mem_ctx, &temp, p);
+ if (len == (uint32)-1) {
+ len = 0;
+ }
+
+ data->notify_data.data.length = len;
+ if (len) {
+ data->notify_data.data.string = (uint16 *)temp;
+ } else {
+ data->notify_data.data.string = NULL;
+ }
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job time
+ ********************************************************************/
+
+static void spoolss_notify_job_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=0x0;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with job size
+ ********************************************************************/
+
+static void spoolss_notify_job_size(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->size;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with page info
+ ********************************************************************/
+static void spoolss_notify_total_pages(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->page_count;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ * fill a notify_info_data with pages printed info.
+ ********************************************************************/
+static void spoolss_notify_pages_printed(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=0; /* Add code when back-end tracks this */
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with job position.
+ ********************************************************************/
+
+static void spoolss_notify_job_position(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ data->notify_data.value[0]=queue->job;
+ data->notify_data.value[1]=0;
+}
+
+/*******************************************************************
+ Fill a notify_info_data with submitted time.
+ ********************************************************************/
+
+static void spoolss_notify_submitted_time(int snum,
+ SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer,
+ TALLOC_CTX *mem_ctx)
+{
+ struct tm *t;
+ uint32 len;
+ SYSTEMTIME st;
+ char *p;
+
+ t=gmtime(&queue->time);
+
+ len = sizeof(SYSTEMTIME);
+
+ data->notify_data.data.length = len;
+ data->notify_data.data.string = (uint16 *)TALLOC(mem_ctx, len);
+
+ if (!data->notify_data.data.string) {
+ data->notify_data.data.length = 0;
+ return;
+ }
+
+ make_systemtime(&st, t);
+
+ /*
+ * Systemtime must be linearized as a set of UINT16's.
+ * Fix from Benjamin (Bj) Kuit bj@it.uts.edu.au
+ */
+
+ p = (char *)data->notify_data.data.string;
+ SSVAL(p, 0, st.year);
+ SSVAL(p, 2, st.month);
+ SSVAL(p, 4, st.dayofweek);
+ SSVAL(p, 6, st.day);
+ SSVAL(p, 8, st.hour);
+ SSVAL(p, 10, st.minute);
+ SSVAL(p, 12, st.second);
+ SSVAL(p, 14, st.milliseconds);
+}
+
+struct s_notify_info_data_table
+{
+ uint16 type;
+ uint16 field;
+ const char *name;
+ uint32 size;
+ void (*fn) (int snum, SPOOL_NOTIFY_INFO_DATA *data,
+ print_queue_struct *queue,
+ NT_PRINTER_INFO_LEVEL *printer, 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 or a two value uint32. */
+
+static const struct s_notify_info_data_table notify_info_data_table[] =
+{
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME, "PRINTER_NOTIFY_SERVER_NAME", NOTIFY_STRING, spoolss_notify_server_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME, "PRINTER_NOTIFY_PRINTER_NAME", NOTIFY_STRING, spoolss_notify_printer_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME, "PRINTER_NOTIFY_SHARE_NAME", NOTIFY_STRING, spoolss_notify_share_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME, "PRINTER_NOTIFY_PORT_NAME", NOTIFY_STRING, spoolss_notify_port_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME, "PRINTER_NOTIFY_DRIVER_NAME", NOTIFY_STRING, spoolss_notify_driver_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT, "PRINTER_NOTIFY_COMMENT", NOTIFY_STRING, spoolss_notify_comment },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION, "PRINTER_NOTIFY_LOCATION", NOTIFY_STRING, spoolss_notify_location },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE, "PRINTER_NOTIFY_DEVMODE", NOTIFY_POINTER, spoolss_notify_devmode },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE, "PRINTER_NOTIFY_SEPFILE", NOTIFY_STRING, spoolss_notify_sepfile },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR, "PRINTER_NOTIFY_PRINT_PROCESSOR", NOTIFY_STRING, spoolss_notify_print_processor },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS, "PRINTER_NOTIFY_PARAMETERS", NOTIFY_STRING, spoolss_notify_parameters },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE, "PRINTER_NOTIFY_DATATYPE", NOTIFY_STRING, spoolss_notify_datatype },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_SECDESC, spoolss_notify_security_desc },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES, "PRINTER_NOTIFY_ATTRIBUTES", NOTIFY_ONE_VALUE, spoolss_notify_attributes },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY, "PRINTER_NOTIFY_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY, "PRINTER_NOTIFY_DEFAULT_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_default_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME, "PRINTER_NOTIFY_START_TIME", NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME, "PRINTER_NOTIFY_UNTIL_TIME", NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS, "PRINTER_NOTIFY_STATUS", NOTIFY_ONE_VALUE, spoolss_notify_status },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING, "PRINTER_NOTIFY_STATUS_STRING", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS, "PRINTER_NOTIFY_CJOBS", NOTIFY_ONE_VALUE, spoolss_notify_cjobs },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM, "PRINTER_NOTIFY_AVERAGE_PPM", NOTIFY_ONE_VALUE, spoolss_notify_average_ppm },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES, "PRINTER_NOTIFY_TOTAL_PAGES", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED, "PRINTER_NOTIFY_PAGES_PRINTED", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES, "PRINTER_NOTIFY_TOTAL_BYTES", NOTIFY_POINTER, NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED, "PRINTER_NOTIFY_BYTES_PRINTED", NOTIFY_POINTER, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINTER_NAME, "JOB_NOTIFY_PRINTER_NAME", NOTIFY_STRING, spoolss_notify_printer_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_MACHINE_NAME, "JOB_NOTIFY_MACHINE_NAME", NOTIFY_STRING, spoolss_notify_server_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PORT_NAME, "JOB_NOTIFY_PORT_NAME", NOTIFY_STRING, spoolss_notify_port_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME, "JOB_NOTIFY_USER_NAME", NOTIFY_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_NOTIFY_NAME, "JOB_NOTIFY_NOTIFY_NAME", NOTIFY_STRING, spoolss_notify_username },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DATATYPE, "JOB_NOTIFY_DATATYPE", NOTIFY_STRING, spoolss_notify_datatype },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINT_PROCESSOR, "JOB_NOTIFY_PRINT_PROCESSOR", NOTIFY_STRING, spoolss_notify_print_processor },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PARAMETERS, "JOB_NOTIFY_PARAMETERS", NOTIFY_STRING, spoolss_notify_parameters },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DRIVER_NAME, "JOB_NOTIFY_DRIVER_NAME", NOTIFY_STRING, spoolss_notify_driver_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DEVMODE, "JOB_NOTIFY_DEVMODE", NOTIFY_POINTER, spoolss_notify_devmode },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS, "JOB_NOTIFY_STATUS", NOTIFY_ONE_VALUE, spoolss_notify_job_status },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS_STRING, "JOB_NOTIFY_STATUS_STRING", NOTIFY_STRING, spoolss_notify_job_status_string },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SECURITY_DESCRIPTOR, "JOB_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_POINTER, NULL },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT, "JOB_NOTIFY_DOCUMENT", NOTIFY_STRING, spoolss_notify_job_name },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRIORITY, "JOB_NOTIFY_PRIORITY", NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_POSITION, "JOB_NOTIFY_POSITION", NOTIFY_ONE_VALUE, spoolss_notify_job_position },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED, "JOB_NOTIFY_SUBMITTED", NOTIFY_POINTER, spoolss_notify_submitted_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_START_TIME, "JOB_NOTIFY_START_TIME", NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_UNTIL_TIME, "JOB_NOTIFY_UNTIL_TIME", NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TIME, "JOB_NOTIFY_TIME", NOTIFY_ONE_VALUE, spoolss_notify_job_time },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_PAGES, "JOB_NOTIFY_TOTAL_PAGES", NOTIFY_ONE_VALUE, spoolss_notify_total_pages },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PAGES_PRINTED, "JOB_NOTIFY_PAGES_PRINTED", NOTIFY_ONE_VALUE, spoolss_notify_pages_printed },
+{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_BYTES, "JOB_NOTIFY_TOTAL_BYTES", NOTIFY_ONE_VALUE, spoolss_notify_job_size },
+{ PRINT_TABLE_END, 0x0, NULL, 0x0, NULL },
+};
+
+/*******************************************************************
+ Return the size of info_data structure.
+********************************************************************/
+
+static uint32 size_of_notify_info_data(uint16 type, uint16 field)
+{
+ int i=0;
+
+ for (i = 0; i < (sizeof(notify_info_data_table)/sizeof(struct s_notify_info_data_table)); i++) {
+ if ( (notify_info_data_table[i].type == type)
+ && (notify_info_data_table[i].field == field) ) {
+ switch(notify_info_data_table[i].size) {
+ case NOTIFY_ONE_VALUE:
+ case NOTIFY_TWO_VALUE:
+ return 1;
+ case NOTIFY_STRING:
+ return 2;
+
+ /* The only pointer notify data I have seen on
+ the wire is the submitted time and this has
+ the notify size set to 4. -tpot */
+
+ case NOTIFY_POINTER:
+ return 4;
+
+ case NOTIFY_SECDESC:
+ return 5;
+ }
+ }
+ }
+
+ DEBUG(5, ("invalid notify data type %d/%d\n", type, field));
+
+ return 0;
+}
+
+/*******************************************************************
+ Return the type of notify_info_data.
+********************************************************************/
+
+static uint32 type_of_notify_info_data(uint16 type, uint16 field)
+{
+ uint32 i=0;
+
+ for (i = 0; i < (sizeof(notify_info_data_table)/sizeof(struct s_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].size;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool search_notify(uint16 type, uint16 field, int *value)
+{
+ int i;
+
+ for (i = 0; notify_info_data_table[i].type != PRINT_TABLE_END; 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;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void construct_info_data(SPOOL_NOTIFY_INFO_DATA *info_data, uint16 type, uint16 field, int id)
+{
+ info_data->type = type;
+ info_data->field = field;
+ info_data->reserved = 0;
+
+ info_data->size = size_of_notify_info_data(type, field);
+ info_data->enc_type = type_of_notify_info_data(type, field);
+
+ info_data->id = id;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static bool construct_notify_printer_info(Printer_entry *print_hnd, SPOOL_NOTIFY_INFO *info, int
+ snum, SPOOL_NOTIFY_OPTION_TYPE
+ *option_type, uint32 id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ uint16 type;
+ uint16 field;
+
+ SPOOL_NOTIFY_INFO_DATA *current_data;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ print_queue_struct *queue=NULL;
+
+ type=option_type->type;
+
+ DEBUG(4,("construct_notify_printer_info: Notify type: [%s], number of notify info: [%d] on printer: [%s]\n",
+ (option_type->type==PRINTER_NOTIFY_TYPE?"PRINTER_NOTIFY_TYPE":"JOB_NOTIFY_TYPE"),
+ option_type->count, lp_servicename(snum)));
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &printer, 2, lp_const_servicename(snum))))
+ return False;
+
+ for(field_num=0; field_num<option_type->count; field_num++) {
+ field = option_type->fields[field_num];
+
+ DEBUG(4,("construct_notify_printer_info: notify [%d]: type [%x], field [%x]\n", field_num, type, field));
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ if((info->data=SMB_REALLOC_ARRAY(info->data, SPOOL_NOTIFY_INFO_DATA, info->count+1)) == NULL) {
+ DEBUG(2,("construct_notify_printer_info: failed to enlarge buffer info->data!\n"));
+ free_a_printer(&printer, 2);
+ return False;
+ }
+
+ current_data = &info->data[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, printer->info_2->printername ));
+
+ notify_info_data_table[j].fn(snum, current_data, queue,
+ printer, mem_ctx);
+
+ info->count++;
+ }
+
+ free_a_printer(&printer, 2);
+ return True;
+}
+
+/*******************************************************************
+ *
+ * fill a notify_info struct with info asked
+ *
+ ********************************************************************/
+
+static bool construct_notify_jobs_info(print_queue_struct *queue,
+ SPOOL_NOTIFY_INFO *info,
+ NT_PRINTER_INFO_LEVEL *printer,
+ int snum, SPOOL_NOTIFY_OPTION_TYPE
+ *option_type, uint32 id,
+ TALLOC_CTX *mem_ctx)
+{
+ int field_num,j;
+ uint16 type;
+ uint16 field;
+
+ SPOOL_NOTIFY_INFO_DATA *current_data;
+
+ DEBUG(4,("construct_notify_jobs_info\n"));
+
+ type = option_type->type;
+
+ DEBUGADD(4,("Notify type: [%s], number of notify info: [%d]\n",
+ (option_type->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];
+
+ if (!search_notify(type, field, &j) )
+ continue;
+
+ if((info->data=SMB_REALLOC_ARRAY(info->data, SPOOL_NOTIFY_INFO_DATA, info->count+1)) == NULL) {
+ DEBUG(2,("construct_notify_jobs_info: failed to enlarg buffer info->data!\n"));
+ return False;
+ }
+
+ current_data=&(info->data[info->count]);
+
+ construct_info_data(current_data, type, field, id);
+ notify_info_data_table[j].fn(snum, current_data, queue,
+ printer, 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(pipes_struct *p, POLICY_HND *hnd,
+ SPOOL_NOTIFY_INFO *info,
+ TALLOC_CTX *mem_ctx)
+{
+ int snum;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+ int n_services=lp_numservices();
+ int i;
+ SPOOL_NOTIFY_OPTION *option;
+ SPOOL_NOTIFY_OPTION_TYPE *option_type;
+
+ DEBUG(4,("printserver_notify_info\n"));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ option=Printer->notify.option;
+ info->version=2;
+ info->data=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_BADFID;
+
+ for (i=0; i<option->count; i++) {
+ option_type=&(option->ctr.type[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_print_ok(snum) )
+ construct_notify_printer_info ( Printer, info, snum, option_type, snum, mem_ctx );
+ }
+ }
+
+#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(pipes_struct *p, POLICY_HND *hnd, SPOOL_NOTIFY_INFO *info,
+ TALLOC_CTX *mem_ctx)
+{
+ int snum;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, hnd);
+ int i;
+ uint32 id;
+ SPOOL_NOTIFY_OPTION *option;
+ SPOOL_NOTIFY_OPTION_TYPE *option_type;
+ int count,j;
+ print_queue_struct *queue=NULL;
+ print_status_struct status;
+
+ DEBUG(4,("printer_notify_info\n"));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ option=Printer->notify.option;
+ id = 0x0;
+ info->version=2;
+ info->data=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_BADFID;
+
+ get_printer_snum(p, hnd, &snum, NULL);
+
+ for (i=0; i<option->count; i++) {
+ option_type=&option->ctr.type[i];
+
+ switch ( option_type->type ) {
+ case PRINTER_NOTIFY_TYPE:
+ if(construct_notify_printer_info(Printer, info, snum,
+ option_type, id,
+ mem_ctx))
+ id--;
+ break;
+
+ case JOB_NOTIFY_TYPE: {
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ count = print_queue_status(snum, &queue, &status);
+
+ if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))))
+ goto done;
+
+ for (j=0; j<count; j++) {
+ construct_notify_jobs_info(&queue[j], info,
+ printer, snum,
+ option_type,
+ queue[j].job,
+ mem_ctx);
+ }
+
+ free_a_printer(&printer, 2);
+
+ done:
+ 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));
+ }
+ */
+ return WERR_OK;
+}
+
+/********************************************************************
+ * spoolss_rfnpcnex
+ ********************************************************************/
+
+WERROR _spoolss_rfnpcnex( pipes_struct *p, SPOOL_Q_RFNPCNEX *q_u, SPOOL_R_RFNPCNEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ SPOOL_NOTIFY_INFO *info = &r_u->info;
+
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ WERROR result = WERR_BADFID;
+
+ /* we always have a NOTIFY_INFO struct */
+ r_u->info_ptr=0x1;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_rfnpcnex: Invalid handle (%s:%u:%u).\n",
+ OUR_HANDLE(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
+ * informations 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.client_connected) {
+ DEBUG(10,("_spoolss_rfnpcnex: Saving change value in request [%x]\n", q_u->change));
+ Printer->notify.change = q_u->change;
+ }
+
+ /* just ignore the SPOOL_NOTIFY_OPTION */
+
+ switch (Printer->printer_type) {
+ case SPLHND_SERVER:
+ result = printserver_notify_info(p, handle, info, p->mem_ctx);
+ break;
+
+ case SPLHND_PRINTER:
+ result = printer_notify_info(p, handle, info, p->mem_ctx);
+ break;
+ }
+
+ Printer->notify.fnpcn = False;
+
+done:
+ return result;
+}
+
+/********************************************************************
+ * construct_printer_info_0
+ * fill a printer_info_0 struct
+ ********************************************************************/
+
+static bool construct_printer_info_0(Printer_entry *print_hnd, PRINTER_INFO_0 *printer, int snum)
+{
+ char *chaine = NULL;
+ int count;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ counter_printer_0 *session_counter;
+ uint32 global_counter;
+ struct tm *t;
+ time_t setuptime;
+ print_status_struct status;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername);
+
+ chaine = talloc_asprintf(ctx, "\\\\%s", get_server_name(print_hnd));
+ if (!chaine) {
+ free_a_printer(&ntprinter,2);
+ return false;
+ }
+
+ count = print_queue_length(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;
+ }
+
+ init_unistr(&printer->servername, chaine);
+
+ /* it's the first time, add it to the list */
+ if (session_counter==NULL) {
+ if((session_counter=SMB_MALLOC_P(counter_printer_0)) == NULL) {
+ free_a_printer(&ntprinter, 2);
+ return False;
+ }
+ ZERO_STRUCTP(session_counter);
+ session_counter->snum=snum;
+ session_counter->counter=0;
+ DLIST_ADD(counter_list, session_counter);
+ }
+
+ /* increment it */
+ session_counter->counter++;
+
+ /* 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
+ */
+ global_counter=session_counter->counter;
+ printer->cjobs = count;
+ printer->total_jobs = 0;
+ printer->total_bytes = 0;
+
+ setuptime = (time_t)ntprinter->info_2->setuptime;
+ t=gmtime(&setuptime);
+
+ printer->year = t->tm_year+1900;
+ printer->month = t->tm_mon+1;
+ printer->dayofweek = t->tm_wday;
+ printer->day = t->tm_mday;
+ printer->hour = t->tm_hour;
+ printer->minute = t->tm_min;
+ printer->second = t->tm_sec;
+ printer->milliseconds = 0;
+
+ printer->global_counter = global_counter;
+ printer->total_pages = 0;
+
+ /* in 2.2 we reported ourselves as 0x0004 and 0x0565 */
+ printer->major_version = 0x0005; /* NT 5 */
+ printer->build_version = 0x0893; /* build 2195 */
+
+ printer->unknown7 = 0x1;
+ printer->unknown8 = 0x0;
+ printer->unknown9 = 0x0;
+ printer->session_counter = session_counter->counter;
+ printer->unknown11 = 0x0;
+ printer->printer_errors = 0x0; /* number of print failure */
+ printer->unknown13 = 0x0;
+ printer->unknown14 = 0x1;
+ printer->unknown15 = 0x024a; /* 586 Pentium ? */
+ printer->unknown16 = 0x0;
+ printer->change_id = ntprinter->info_2->changeid; /* ChangeID in milliseconds*/
+ printer->unknown18 = 0x0;
+ printer->status = nt_printq_status(status.status);
+ printer->unknown20 = 0x0;
+ printer->c_setprinter = get_c_setprinter(); /* monotonically increasing sum of delta printer counts */
+ printer->unknown22 = 0x0;
+ printer->unknown23 = 0x6; /* 6 ???*/
+ printer->unknown24 = 0; /* unknown 24 to 26 are always 0 */
+ printer->unknown25 = 0;
+ printer->unknown26 = 0;
+ printer->unknown27 = 0;
+ printer->unknown28 = 0;
+ printer->unknown29 = 0;
+
+ free_a_printer(&ntprinter,2);
+ return (True);
+}
+
+/********************************************************************
+ * construct_printer_info_1
+ * fill a printer_info_1 struct
+ ********************************************************************/
+static bool construct_printer_info_1(Printer_entry *print_hnd, uint32 flags, PRINTER_INFO_1 *printer, int snum)
+{
+ char *chaine = NULL;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return false;
+
+ printer->flags=flags;
+
+ if (*ntprinter->info_2->comment == '\0') {
+ init_unistr(&printer->comment, lp_comment(snum));
+ chaine = talloc_asprintf(ctx,
+ "%s,%s,%s", ntprinter->info_2->printername,
+ ntprinter->info_2->drivername, lp_comment(snum));
+ }
+ else {
+ init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+ chaine = talloc_asprintf(ctx,
+ "%s,%s,%s", ntprinter->info_2->printername,
+ ntprinter->info_2->drivername, ntprinter->info_2->comment);
+ }
+
+ if (!chaine) {
+ free_a_printer(&ntprinter,2);
+ return false;
+ }
+
+ init_unistr(&printer->description, chaine);
+ init_unistr(&printer->name, ntprinter->info_2->printername);
+
+ free_a_printer(&ntprinter,2);
+
+ return True;
+}
+
+/****************************************************************************
+ Free a DEVMODE struct.
+****************************************************************************/
+
+static void free_dev_mode(DEVICEMODE *dev)
+{
+ if (dev == NULL)
+ return;
+
+ SAFE_FREE(dev->dev_private);
+ SAFE_FREE(dev);
+}
+
+
+/****************************************************************************
+ Convert an NT_DEVICEMODE to a DEVICEMODE structure. Both pointers
+ should be valid upon entry
+****************************************************************************/
+
+static bool convert_nt_devicemode( DEVICEMODE *devmode, NT_DEVICEMODE *ntdevmode )
+{
+ if ( !devmode || !ntdevmode )
+ return False;
+
+ init_unistr(&devmode->devicename, ntdevmode->devicename);
+
+ init_unistr(&devmode->formname, ntdevmode->formname);
+
+ devmode->specversion = ntdevmode->specversion;
+ devmode->driverversion = ntdevmode->driverversion;
+ devmode->size = ntdevmode->size;
+ devmode->driverextra = ntdevmode->driverextra;
+ devmode->fields = ntdevmode->fields;
+
+ devmode->orientation = ntdevmode->orientation;
+ devmode->papersize = ntdevmode->papersize;
+ devmode->paperlength = ntdevmode->paperlength;
+ devmode->paperwidth = ntdevmode->paperwidth;
+ devmode->scale = ntdevmode->scale;
+ devmode->copies = ntdevmode->copies;
+ devmode->defaultsource = ntdevmode->defaultsource;
+ devmode->printquality = ntdevmode->printquality;
+ devmode->color = ntdevmode->color;
+ devmode->duplex = ntdevmode->duplex;
+ devmode->yresolution = ntdevmode->yresolution;
+ devmode->ttoption = ntdevmode->ttoption;
+ devmode->collate = ntdevmode->collate;
+ devmode->icmmethod = ntdevmode->icmmethod;
+ devmode->icmintent = ntdevmode->icmintent;
+ devmode->mediatype = ntdevmode->mediatype;
+ devmode->dithertype = ntdevmode->dithertype;
+
+ if (ntdevmode->nt_dev_private != NULL) {
+ if ((devmode->dev_private=(uint8 *)memdup(ntdevmode->nt_dev_private, ntdevmode->driverextra)) == NULL)
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Create a DEVMODE struct. Returns malloced memory.
+****************************************************************************/
+
+DEVICEMODE *construct_dev_mode(const char *servicename)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ DEVICEMODE *devmode = NULL;
+
+ DEBUG(7,("construct_dev_mode\n"));
+
+ DEBUGADD(8,("getting printer characteristics\n"));
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, servicename)))
+ return NULL;
+
+ if ( !printer->info_2->devmode ) {
+ DEBUG(5, ("BONG! There was no device mode!\n"));
+ goto done;
+ }
+
+ if ((devmode = SMB_MALLOC_P(DEVICEMODE)) == NULL) {
+ DEBUG(2,("construct_dev_mode: malloc fail.\n"));
+ goto done;
+ }
+
+ ZERO_STRUCTP(devmode);
+
+ DEBUGADD(8,("loading DEVICEMODE\n"));
+
+ if ( !convert_nt_devicemode( devmode, printer->info_2->devmode ) ) {
+ free_dev_mode( devmode );
+ devmode = NULL;
+ }
+
+done:
+ free_a_printer(&printer,2);
+
+ return devmode;
+}
+
+/********************************************************************
+ * construct_printer_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static bool construct_printer_info_2(Printer_entry *print_hnd, PRINTER_INFO_2 *printer, int snum)
+{
+ int count;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ print_status_struct status;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ count = print_queue_length(snum, &status);
+
+ init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+ init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/
+ init_unistr(&printer->sharename, lp_servicename(snum)); /* sharename */
+ init_unistr(&printer->portname, ntprinter->info_2->portname); /* port */
+ init_unistr(&printer->drivername, ntprinter->info_2->drivername); /* drivername */
+
+ if (*ntprinter->info_2->comment == '\0')
+ init_unistr(&printer->comment, lp_comment(snum)); /* comment */
+ else
+ init_unistr(&printer->comment, ntprinter->info_2->comment); /* saved comment. */
+
+ init_unistr(&printer->location, ntprinter->info_2->location); /* location */
+ init_unistr(&printer->sepfile, ntprinter->info_2->sepfile); /* separator file */
+ init_unistr(&printer->printprocessor, ntprinter->info_2->printprocessor);/* print processor */
+ init_unistr(&printer->datatype, ntprinter->info_2->datatype); /* datatype */
+ init_unistr(&printer->parameters, ntprinter->info_2->parameters); /* parameters (of print processor) */
+
+ printer->attributes = ntprinter->info_2->attributes;
+
+ printer->priority = ntprinter->info_2->priority; /* priority */
+ printer->defaultpriority = ntprinter->info_2->default_priority; /* default priority */
+ printer->starttime = ntprinter->info_2->starttime; /* starttime */
+ printer->untiltime = ntprinter->info_2->untiltime; /* untiltime */
+ printer->status = nt_printq_status(status.status); /* status */
+ printer->cjobs = count; /* jobs */
+ printer->averageppm = ntprinter->info_2->averageppm; /* average pages per minute */
+
+ if ( !(printer->devmode = construct_dev_mode(
+ lp_const_servicename(snum))) )
+ DEBUG(8, ("Returning NULL Devicemode!\n"));
+
+ printer->secdesc = NULL;
+
+ if ( ntprinter->info_2->secdesc_buf
+ && ntprinter->info_2->secdesc_buf->sd_size != 0 )
+ {
+ /* don't use talloc_steal() here unless you do a deep steal of all
+ the SEC_DESC members */
+
+ printer->secdesc = dup_sec_desc( talloc_tos(),
+ ntprinter->info_2->secdesc_buf->sd );
+ }
+
+ free_a_printer(&ntprinter, 2);
+
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static bool construct_printer_info_3(Printer_entry *print_hnd, PRINTER_INFO_3 **pp_printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ PRINTER_INFO_3 *printer = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ *pp_printer = NULL;
+ if ((printer = SMB_MALLOC_P(PRINTER_INFO_3)) == NULL) {
+ DEBUG(2,("construct_printer_info_3: malloc fail.\n"));
+ free_a_printer(&ntprinter, 2);
+ return False;
+ }
+
+ ZERO_STRUCTP(printer);
+
+ /* These are the components of the SD we are returning. */
+
+ if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->sd_size != 0) {
+ /* don't use talloc_steal() here unless you do a deep steal of all
+ the SEC_DESC members */
+
+ printer->secdesc = dup_sec_desc( talloc_tos(),
+ ntprinter->info_2->secdesc_buf->sd );
+ }
+
+ free_a_printer(&ntprinter, 2);
+
+ *pp_printer = printer;
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_4
+ * fill a printer_info_4 struct
+ ********************************************************************/
+
+static bool construct_printer_info_4(Printer_entry *print_hnd, PRINTER_INFO_4 *printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/
+ init_unistr(&printer->servername, ntprinter->info_2->servername); /* servername*/
+ printer->attributes = ntprinter->info_2->attributes;
+
+ free_a_printer(&ntprinter, 2);
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_5
+ * fill a printer_info_5 struct
+ ********************************************************************/
+
+static bool construct_printer_info_5(Printer_entry *print_hnd, PRINTER_INFO_5 *printer, int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2, lp_const_servicename(snum))))
+ return False;
+
+ init_unistr(&printer->printername, ntprinter->info_2->printername);
+ init_unistr(&printer->portname, ntprinter->info_2->portname);
+ printer->attributes = ntprinter->info_2->attributes;
+
+ /* these two are not used by NT+ according to MSDN */
+
+ printer->device_not_selected_timeout = 0x0; /* have seen 0x3a98 */
+ printer->transmission_retry_timeout = 0x0; /* have seen 0xafc8 */
+
+ free_a_printer(&ntprinter, 2);
+
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct
+ ********************************************************************/
+
+static bool construct_printer_info_6(Printer_entry *print_hnd,
+ PRINTER_INFO_6 *printer,
+ int snum)
+{
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ int count;
+ print_status_struct status;
+
+ if (!W_ERROR_IS_OK(get_a_printer(print_hnd, &ntprinter, 2,
+ lp_const_servicename(snum))))
+ return False;
+
+ count = print_queue_length(snum, &status);
+
+ printer->status = nt_printq_status(status.status);
+
+ free_a_printer(&ntprinter, 2);
+
+ return True;
+}
+
+/********************************************************************
+ * construct_printer_info_7
+ * fill a printer_info_7 struct
+ ********************************************************************/
+
+static bool construct_printer_info_7(Printer_entry *print_hnd, PRINTER_INFO_7 *printer, int snum)
+{
+ char *guid_str = NULL;
+ struct GUID guid;
+
+ if (is_printer_published(print_hnd, snum, &guid)) {
+ if (asprintf(&guid_str, "{%s}",
+ smb_uuid_string(talloc_tos(), guid)) == -1) {
+ return false;
+ }
+ strupper_m(guid_str);
+ init_unistr(&printer->guid, guid_str);
+ SAFE_FREE(guid_str);
+ printer->action = SPOOL_DS_PUBLISH;
+ } else {
+ init_unistr(&printer->guid, "");
+ printer->action = SPOOL_DS_UNPUBLISH;
+ }
+
+ return True;
+}
+
+/********************************************************************
+ Spoolss_enumprinters.
+********************************************************************/
+
+static WERROR enum_all_printers_info_1(uint32 flags, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int snum;
+ int i;
+ int n_services=lp_numservices();
+ PRINTER_INFO_1 *printers=NULL;
+ PRINTER_INFO_1 current_prt;
+ WERROR result = WERR_OK;
+
+ DEBUG(4,("enum_all_printers_info_1\n"));
+
+ for (snum=0; snum<n_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+ if (construct_printer_info_1(NULL, flags, &current_prt, snum)) {
+ if((printers=SMB_REALLOC_ARRAY(printers, PRINTER_INFO_1, *returned +1)) == NULL) {
+ DEBUG(2,("enum_all_printers_info_1: failed to enlarge printers buffer!\n"));
+ *returned=0;
+ return WERR_NOMEM;
+ }
+ DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_1\n", *returned));
+
+ memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_1));
+ (*returned)++;
+ }
+ }
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_printer_info_1(&printers[i]);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_printer_info_1("", buffer, &printers[i], 0);
+
+out:
+ /* clear memory */
+
+ SAFE_FREE(printers);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/********************************************************************
+ enum_all_printers_info_1_local.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_local(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ DEBUG(4,("enum_all_printers_info_1_local\n"));
+
+ return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ enum_all_printers_info_1_name.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_name(fstring name, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ char *s = name;
+
+ DEBUG(4,("enum_all_printers_info_1_name\n"));
+
+ if ((name[0] == '\\') && (name[1] == '\\'))
+ s = name + 2;
+
+ if (is_myname_or_ipaddr(s)) {
+ return enum_all_printers_info_1(PRINTER_ENUM_ICON8, buffer, offered, needed, returned);
+ }
+ else
+ return WERR_INVALID_NAME;
+}
+
+#if 0 /* JERRY -- disabled for now. Don't think this is used, tested, or correct */
+/********************************************************************
+ enum_all_printers_info_1_remote.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_remote(fstring name, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTER_INFO_1 *printer;
+ fstring printername;
+ fstring desc;
+ fstring comment;
+ DEBUG(4,("enum_all_printers_info_1_remote\n"));
+ WERROR result = WERR_OK;
+
+ /* JFM: currently it's more a place holder than anything else.
+ * In the spooler world there is a notion of server registration.
+ * the print servers are registered on the PDC (in the same domain)
+ *
+ * We should have a TDB here. The registration is done thru an
+ * undocumented RPC call.
+ */
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_1)) == NULL)
+ return WERR_NOMEM;
+
+ *returned=1;
+
+ slprintf(printername, sizeof(printername)-1,"Windows NT Remote Printers!!\\\\%s", name);
+ slprintf(desc, sizeof(desc)-1,"%s", name);
+ slprintf(comment, sizeof(comment)-1, "Logged on Domain");
+
+ init_unistr(&printer->description, desc);
+ init_unistr(&printer->name, printername);
+ init_unistr(&printer->comment, comment);
+ printer->flags=PRINTER_ENUM_ICON3|PRINTER_ENUM_CONTAINER;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_1(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_1("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+#endif
+
+/********************************************************************
+ enum_all_printers_info_1_network.
+*********************************************************************/
+
+static WERROR enum_all_printers_info_1_network(fstring name, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ char *s = name;
+
+ 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 (name[0] == '\\' && name[1] == '\\')
+ s = name + 2;
+
+ if (is_myname_or_ipaddr(s))
+ return WERR_CAN_NOT_COMPLETE;
+
+ return enum_all_printers_info_1(PRINTER_ENUM_UNKNOWN_8, buffer, offered, needed, returned);
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+static WERROR enum_all_printers_info_2(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int snum;
+ int i;
+ int n_services=lp_numservices();
+ PRINTER_INFO_2 *printers=NULL;
+ PRINTER_INFO_2 current_prt;
+ WERROR result = WERR_OK;
+
+ *returned = 0;
+
+ for (snum=0; snum<n_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && lp_print_ok(snum) ) {
+ DEBUG(4,("Found a printer in smb.conf: %s[%x]\n", lp_servicename(snum), snum));
+
+ if (construct_printer_info_2(NULL, &current_prt, snum)) {
+ if ( !(printers=SMB_REALLOC_ARRAY(printers, PRINTER_INFO_2, *returned +1)) ) {
+ DEBUG(2,("enum_all_printers_info_2: failed to enlarge printers buffer!\n"));
+ *returned = 0;
+ return WERR_NOMEM;
+ }
+
+ DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_2\n", *returned + 1));
+
+ memcpy(&printers[*returned], &current_prt, sizeof(PRINTER_INFO_2));
+
+ (*returned)++;
+ }
+ }
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_printer_info_2(&printers[i]);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_printer_info_2("", buffer, &(printers[i]), 0);
+
+out:
+ /* clear memory */
+
+ for (i=0; i<*returned; i++)
+ free_devmode(printers[i].devmode);
+
+ SAFE_FREE(printers);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 1
+ ********************************************************************/
+
+static WERROR enumprinters_level1( uint32 flags, fstring name,
+ RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ /* Not all the flags are equals */
+
+ if (flags & PRINTER_ENUM_LOCAL)
+ return enum_all_printers_info_1_local(buffer, offered, needed, returned);
+
+ if (flags & PRINTER_ENUM_NAME)
+ return enum_all_printers_info_1_name(name, buffer, offered, needed, returned);
+
+#if 0 /* JERRY - disabled for now */
+ if (flags & PRINTER_ENUM_REMOTE)
+ return enum_all_printers_info_1_remote(name, buffer, offered, needed, returned);
+#endif
+
+ if (flags & PRINTER_ENUM_NETWORK)
+ return enum_all_printers_info_1_network(name, buffer, offered, needed, returned);
+
+ return WERR_OK; /* NT4sp5 does that */
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 2
+ ********************************************************************/
+
+static WERROR enumprinters_level2( uint32 flags, const char *servername,
+ RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ if (flags & PRINTER_ENUM_LOCAL) {
+ return enum_all_printers_info_2(buffer, offered, needed, returned);
+ }
+
+ if (flags & PRINTER_ENUM_NAME) {
+ if (is_myname_or_ipaddr(canon_servername(servername)))
+ return enum_all_printers_info_2(buffer, offered, needed, returned);
+ else
+ return WERR_INVALID_NAME;
+ }
+
+ if (flags & PRINTER_ENUM_REMOTE)
+ return WERR_UNKNOWN_LEVEL;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * handle enumeration of printers at level 5
+ ********************************************************************/
+
+static WERROR enumprinters_level5( uint32 flags, const char *servername,
+ RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+/* return enum_all_printers_info_5(buffer, offered, needed, returned);*/
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_enumprinters
+ *
+ * called from api_spoolss_enumprinters (see this to understand)
+ ********************************************************************/
+
+WERROR _spoolss_enumprinters( pipes_struct *p, SPOOL_Q_ENUMPRINTERS *q_u, SPOOL_R_ENUMPRINTERS *r_u)
+{
+ uint32 flags = q_u->flags;
+ UNISTR2 *servername = &q_u->servername;
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ fstring name;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumprinters\n"));
+
+ *needed=0;
+ *returned=0;
+
+ /*
+ * 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
+ */
+
+ unistr2_to_ascii(name, servername, sizeof(name));
+ strupper_m(name);
+
+ switch (level) {
+ case 1:
+ return enumprinters_level1(flags, name, buffer, offered, needed, returned);
+ case 2:
+ return enumprinters_level2(flags, name, buffer, offered, needed, returned);
+ case 5:
+ return enumprinters_level5(flags, name, buffer, offered, needed, returned);
+ case 3:
+ case 4:
+ break;
+ }
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_0(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_0 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_0)) == NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_0(print_hnd, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_0(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_0("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+
+ SAFE_FREE(printer);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_1(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_1 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_1)) == NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_1(print_hnd, PRINTER_ENUM_ICON8, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_1(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_1("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ SAFE_FREE(printer);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_2(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_2 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_2))==NULL)
+ return WERR_NOMEM;
+
+ construct_printer_info_2(print_hnd, printer, snum);
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_2(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ if (!smb_io_printer_info_2("", buffer, printer, 0))
+ result = WERR_NOMEM;
+
+out:
+ /* clear memory */
+ free_printer_info_2(printer);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_3(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_3 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if (!construct_printer_info_3(print_hnd, &printer, snum))
+ return WERR_NOMEM;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_3(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_3("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ free_printer_info_3(printer);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_4(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_4 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_4))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_4(print_hnd, printer, snum)) {
+ SAFE_FREE(printer);
+ return WERR_NOMEM;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_4(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_4("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ free_printer_info_4(printer);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinter_level_5(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_5 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_5))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_5(print_hnd, printer, snum)) {
+ free_printer_info_5(printer);
+ return WERR_NOMEM;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_5(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_5("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ free_printer_info_5(printer);
+
+ return result;
+}
+
+static WERROR getprinter_level_6(Printer_entry *print_hnd,
+ int snum,
+ RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed)
+{
+ PRINTER_INFO_6 *printer;
+ WERROR result = WERR_OK;
+
+ if ((printer = SMB_MALLOC_P(PRINTER_INFO_6)) == NULL) {
+ return WERR_NOMEM;
+ }
+
+ if (!construct_printer_info_6(print_hnd, printer, snum)) {
+ free_printer_info_6(printer);
+ return WERR_NOMEM;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_6(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_6("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ free_printer_info_6(printer);
+
+ return result;
+}
+
+static WERROR getprinter_level_7(Printer_entry *print_hnd, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ PRINTER_INFO_7 *printer=NULL;
+ WERROR result = WERR_OK;
+
+ if((printer=SMB_MALLOC_P(PRINTER_INFO_7))==NULL)
+ return WERR_NOMEM;
+
+ if (!construct_printer_info_7(print_hnd, printer, snum)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_info_7(printer);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_info_7("", buffer, printer, 0);
+
+out:
+ /* clear memory */
+ free_printer_info_7(printer);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinter(pipes_struct *p, SPOOL_Q_GETPRINTER *q_u, SPOOL_R_GETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+
+ int snum;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ *needed=0;
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ switch (level) {
+ case 0:
+ return getprinter_level_0(Printer, snum, buffer, offered, needed);
+ case 1:
+ return getprinter_level_1(Printer, snum, buffer, offered, needed);
+ case 2:
+ return getprinter_level_2(Printer, snum, buffer, offered, needed);
+ case 3:
+ return getprinter_level_3(Printer, snum, buffer, offered, needed);
+ case 4:
+ return getprinter_level_4(Printer, snum, buffer, offered, needed);
+ case 5:
+ return getprinter_level_5(Printer, snum, buffer, offered, needed);
+ case 6:
+ return getprinter_level_6(Printer, snum, buffer, offered, needed);
+ case 7:
+ return getprinter_level_7(Printer, snum, buffer, offered, needed);
+ }
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/********************************************************************
+ * fill a DRIVER_INFO_1 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_1(DRIVER_INFO_1 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, const char *servername, fstring architecture)
+{
+ init_unistr( &info->name, driver.info_3->name);
+}
+
+/********************************************************************
+ * construct_printer_driver_info_1
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_1(DRIVER_INFO_1 *info, int snum, const char *servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ ZERO_STRUCT(driver);
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return WERR_INVALID_PRINTER_NAME;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version))) {
+ free_a_printer(&printer, 2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ fill_printer_driver_info_1(info, driver, servername, architecture);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_2(DRIVER_INFO_2 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, const char *servername)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *temp = NULL;
+ const char *cservername = canon_servername(servername);
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else {
+ init_unistr( &info->driverpath, "" );
+ }
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->datafile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->configfile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+}
+
+/********************************************************************
+ * construct_printer_driver_info_2
+ * fill a printer_info_2 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_2(DRIVER_INFO_2 *info, int snum, const char *servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+
+ ZERO_STRUCT(printer);
+ ZERO_STRUCT(driver);
+
+ if (!W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, lp_const_servicename(snum))))
+ return WERR_INVALID_PRINTER_NAME;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version))) {
+ free_a_printer(&printer, 2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ fill_printer_driver_info_2(info, driver, servername);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * copy a strings array and convert to UNICODE
+ *
+ * convert an array of ascii string to a UNICODE string
+ ********************************************************************/
+
+static uint32 init_unistr_array(uint16 **uni_array, fstring *char_array, const char *servername)
+{
+ int i=0;
+ int j=0;
+ const char *v;
+ char *line = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ DEBUG(6,("init_unistr_array\n"));
+ *uni_array=NULL;
+
+ while (true) {
+ if ( !char_array ) {
+ v = "";
+ } else {
+ v = char_array[i];
+ if (!v)
+ v = ""; /* hack to handle null lists */
+ }
+
+ /* hack to allow this to be used in places other than when generating
+ the list of dependent files */
+
+ TALLOC_FREE(line);
+ if ( servername ) {
+ line = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ canon_servername(servername),
+ v);
+ } else {
+ line = talloc_strdup(ctx, v);
+ }
+
+ if (!line) {
+ SAFE_FREE(*uni_array);
+ return 0;
+ }
+ DEBUGADD(6,("%d:%s:%lu\n", i, line, (unsigned long)strlen(line)));
+
+ /* add one extra unit16 for the second terminating NULL */
+
+ if ( (*uni_array=SMB_REALLOC_ARRAY(*uni_array, uint16, j+1+strlen(line)+2)) == NULL ) {
+ DEBUG(2,("init_unistr_array: Realloc error\n" ));
+ return 0;
+ }
+
+ if ( !strlen(v) )
+ break;
+
+ j += (rpcstr_push((*uni_array+j), line, sizeof(uint16)*strlen(line)+2, STR_TERMINATE) / sizeof(uint16));
+ i++;
+ }
+
+ if (*uni_array) {
+ /* special case for ""; we need to add both NULL's here */
+ if (!j)
+ (*uni_array)[j++]=0x0000;
+ (*uni_array)[j]=0x0000;
+ }
+
+ DEBUGADD(6,("last one:done\n"));
+
+ /* return size of array in uint16's */
+
+ return j+1;
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static void fill_printer_driver_info_3(DRIVER_INFO_3 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, const char *servername)
+{
+ char *temp = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+ const char *cservername = canon_servername(servername);
+
+ ZERO_STRUCTP(info);
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->datafile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->configfile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->helpfile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->helpfile);
+ init_unistr( &info->helpfile, temp );
+ } else
+ init_unistr( &info->helpfile, "" );
+
+ TALLOC_FREE(temp);
+ init_unistr( &info->monitorname, driver.info_3->monitorname );
+ init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+ info->dependentfiles=NULL;
+ init_unistr_array(&info->dependentfiles, driver.info_3->dependentfiles, cservername);
+}
+
+/********************************************************************
+ * construct_printer_info_3
+ * fill a printer_info_3 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_3(DRIVER_INFO_3 *info, int snum, const char *servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR status;
+ ZERO_STRUCT(driver);
+
+ status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+ if (!W_ERROR_IS_OK(status))
+ return WERR_INVALID_PRINTER_NAME;
+
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+
+#if 0 /* JERRY */
+
+ /*
+ * I put this code in during testing. Helpful when commenting out the
+ * support for DRIVER_INFO_6 in regards to win2k. Not needed in general
+ * as win2k always queries the driver using an infor level of 6.
+ * I've left it in (but ifdef'd out) because I'll probably
+ * use it in experimentation again in the future. --jerry 22/01/2002
+ */
+
+ if (!W_ERROR_IS_OK(status)) {
+ /*
+ * Is this a W2k client ?
+ */
+ if (version == 3) {
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_3: status: %s\n", dos_errstr(status)));
+ }
+#endif
+
+ if (!W_ERROR_IS_OK(status)) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+#if 0 /* JERRY */
+ }
+#endif
+
+
+ fill_printer_driver_info_3(info, driver, servername);
+
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct - we know that driver is really level 3. This sucks. JRA.
+ ********************************************************************/
+
+static void fill_printer_driver_info_6(DRIVER_INFO_6 *info, NT_PRINTER_DRIVER_INFO_LEVEL driver, const char *servername)
+{
+ char *temp = NULL;
+ fstring nullstr;
+ TALLOC_CTX *ctx = talloc_tos();
+ const char *cservername = canon_servername(servername);
+
+ ZERO_STRUCTP(info);
+ memset(&nullstr, '\0', sizeof(fstring));
+
+ info->version=driver.info_3->cversion;
+
+ init_unistr( &info->name, driver.info_3->name );
+ init_unistr( &info->architecture, driver.info_3->environment );
+
+ if (strlen(driver.info_3->driverpath)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->driverpath);
+ init_unistr( &info->driverpath, temp );
+ } else
+ init_unistr( &info->driverpath, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->datafile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->datafile);
+ init_unistr( &info->datafile, temp );
+ } else
+ init_unistr( &info->datafile, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->configfile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->configfile);
+ init_unistr( &info->configfile, temp );
+ } else
+ init_unistr( &info->configfile, "" );
+
+ TALLOC_FREE(temp);
+ if (strlen(driver.info_3->helpfile)) {
+ temp = talloc_asprintf(ctx,
+ "\\\\%s%s",
+ cservername,
+ driver.info_3->helpfile);
+ init_unistr( &info->helpfile, temp );
+ } else
+ init_unistr( &info->helpfile, "" );
+
+ TALLOC_FREE(temp);
+ init_unistr( &info->monitorname, driver.info_3->monitorname );
+ init_unistr( &info->defaultdatatype, driver.info_3->defaultdatatype );
+
+ info->dependentfiles = NULL;
+ init_unistr_array( &info->dependentfiles, driver.info_3->dependentfiles, servername );
+
+ info->previousdrivernames=NULL;
+ init_unistr_array(&info->previousdrivernames, &nullstr, servername);
+
+ info->driver_date=0;
+
+ info->padding=0;
+ info->driver_version_low=0;
+ info->driver_version_high=0;
+
+ init_unistr( &info->mfgname, "");
+ init_unistr( &info->oem_url, "");
+ init_unistr( &info->hardware_id, "");
+ init_unistr( &info->provider, "");
+}
+
+/********************************************************************
+ * construct_printer_info_6
+ * fill a printer_info_6 struct
+ ********************************************************************/
+
+static WERROR construct_printer_driver_info_6(DRIVER_INFO_6 *info, int snum,
+ const char *servername, fstring architecture, uint32 version)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR status;
+
+ ZERO_STRUCT(driver);
+
+ status=get_a_printer(NULL, &printer, 2, lp_const_servicename(snum) );
+
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+
+ if (!W_ERROR_IS_OK(status))
+ return WERR_INVALID_PRINTER_NAME;
+
+ status = get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+
+ if (!W_ERROR_IS_OK(status))
+ {
+ /*
+ * Is this a W2k client ?
+ */
+
+ if (version < 3) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* Yes - try again with a WinNT driver. */
+ version = 2;
+ status=get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version);
+ DEBUG(8,("construct_printer_driver_info_6: status: %s\n", dos_errstr(status)));
+ if (!W_ERROR_IS_OK(status)) {
+ free_a_printer(&printer,2);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ }
+
+ fill_printer_driver_info_6(info, driver, servername);
+
+ free_a_printer(&printer,2);
+ free_a_printer_driver(driver, 3);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_3(DRIVER_INFO_3 *info)
+{
+ SAFE_FREE(info->dependentfiles);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void free_printer_driver_info_6(DRIVER_INFO_6 *info)
+{
+ SAFE_FREE(info->dependentfiles);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level1(const char *servername, fstring architecture, uint32 version, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_1 *info=NULL;
+ WERROR result;
+
+ if((info=SMB_MALLOC_P(DRIVER_INFO_1)) == NULL)
+ return WERR_NOMEM;
+
+ result = construct_printer_driver_info_1(info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(result))
+ goto out;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_1(info);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_1("", buffer, info, 0);
+
+out:
+ /* clear memory */
+ SAFE_FREE(info);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level2(const char *servername, fstring architecture, uint32 version, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_2 *info=NULL;
+ WERROR result;
+
+ if((info=SMB_MALLOC_P(DRIVER_INFO_2)) == NULL)
+ return WERR_NOMEM;
+
+ result = construct_printer_driver_info_2(info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(result))
+ goto out;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_2(info);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_2("", buffer, info, 0);
+
+out:
+ /* clear memory */
+ SAFE_FREE(info);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level3(const char *servername, fstring architecture, uint32 version, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_3 info;
+ WERROR result;
+
+ ZERO_STRUCT(info);
+
+ result = construct_printer_driver_info_3(&info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(result))
+ goto out;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_3(&info);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_3("", buffer, &info, 0);
+
+out:
+ free_printer_driver_info_3(&info);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriver2_level6(const char *servername, fstring architecture, uint32 version, int snum, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ DRIVER_INFO_6 info;
+ WERROR result;
+
+ ZERO_STRUCT(info);
+
+ result = construct_printer_driver_info_6(&info, snum, servername, architecture, version);
+ if (!W_ERROR_IS_OK(result))
+ goto out;
+
+ /* check the required size. */
+ *needed += spoolss_size_printer_driver_info_6(&info);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ smb_io_printer_driver_info_6("", buffer, &info, 0);
+
+out:
+ free_printer_driver_info_6(&info);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriver2(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVER2 *q_u, SPOOL_R_GETPRINTERDRIVER2 *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *uni_arch = &q_u->architecture;
+ uint32 level = q_u->level;
+ uint32 clientmajorversion = q_u->clientmajorversion;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *servermajorversion = &r_u->servermajorversion;
+ uint32 *serverminorversion = &r_u->serverminorversion;
+ Printer_entry *printer;
+
+ fstring servername;
+ fstring architecture;
+ int snum;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_getprinterdriver2\n"));
+
+ if ( !(printer = find_printer_index_by_hnd( p, handle )) ) {
+ DEBUG(0,("_spoolss_getprinterdriver2: invalid printer handle!\n"));
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ *needed = 0;
+ *servermajorversion = 0;
+ *serverminorversion = 0;
+
+ fstrcpy(servername, get_server_name( printer ));
+ unistr2_to_ascii(architecture, uni_arch, sizeof(architecture));
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ switch (level) {
+ case 1:
+ return getprinterdriver2_level1(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 2:
+ return getprinterdriver2_level2(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 3:
+ return getprinterdriver2_level3(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+ case 6:
+ return getprinterdriver2_level6(servername, architecture, clientmajorversion, snum, buffer, offered, needed);
+#if 0 /* JERRY */
+ case 101:
+ /* apparently this call is the equivalent of
+ EnumPrinterDataEx() for the DsDriver key */
+ break;
+#endif
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_startpageprinter(pipes_struct *p, SPOOL_Q_STARTPAGEPRINTER *q_u, SPOOL_R_STARTPAGEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(3,("Error in startpageprinter printer handle\n"));
+ return WERR_BADFID;
+ }
+
+ Printer->page_started=True;
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_endpageprinter(pipes_struct *p, SPOOL_Q_ENDPAGEPRINTER *q_u, SPOOL_R_ENDPAGEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ int snum;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_endpageprinter: Invalid handle (%s:%u:%u).\n",OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ Printer->page_started=False;
+ print_job_endpage(snum, Printer->jobid);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_startdocprinter(pipes_struct *p, SPOOL_Q_STARTDOCPRINTER *q_u, SPOOL_R_STARTDOCPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ DOC_INFO *docinfo = &q_u->doc_info_container.docinfo;
+ uint32 *jobid = &r_u->jobid;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ DOC_INFO_1 *info_1 = &docinfo->doc_info_1;
+ int snum;
+ char *jobname = NULL;
+ fstring datatype;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_startdocprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * a nice thing with NT is it doesn't listen to what you tell it.
+ * when asked to send _only_ RAW datas, it tries to send datas
+ * in EMF format.
+ *
+ * So I add checks like in NT Server ...
+ */
+
+ if (info_1->p_datatype != 0) {
+ unistr2_to_ascii(datatype, &info_1->datatype, sizeof(datatype));
+ if (strcmp(datatype, "RAW") != 0) {
+ (*jobid)=0;
+ return WERR_INVALID_DATATYPE;
+ }
+ }
+
+ /* get the share number of the printer */
+ if (!get_printer_snum(p, handle, &snum, NULL)) {
+ return WERR_BADFID;
+ }
+
+ jobname = unistr2_to_ascii_talloc(ctx, &info_1->docname);
+
+ Printer->jobid = print_job_start(p->server_info, snum, jobname,
+ Printer->nt_devmode);
+
+ /* An error occured in print_job_start() so return an appropriate
+ NT error code. */
+
+ if (Printer->jobid == -1) {
+ return map_werror_from_unix(errno);
+ }
+
+ Printer->document_started=True;
+ (*jobid) = Printer->jobid;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+WERROR _spoolss_enddocprinter(pipes_struct *p, SPOOL_Q_ENDDOCPRINTER *q_u, SPOOL_R_ENDDOCPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+
+ return _spoolss_enddocprinter_internal(p, handle);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_writeprinter(pipes_struct *p, SPOOL_Q_WRITEPRINTER *q_u, SPOOL_R_WRITEPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 buffer_size = q_u->buffer_size;
+ uint8 *buffer = q_u->buffer;
+ uint32 *buffer_written = &q_u->buffer_size2;
+ int snum;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_writeprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+ r_u->buffer_written = q_u->buffer_size2;
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ (*buffer_written) = (uint32)print_job_write(snum, Printer->jobid, (const char *)buffer,
+ (SMB_OFF_T)-1, (size_t)buffer_size);
+ if (*buffer_written == (uint32)-1) {
+ r_u->buffer_written = 0;
+ if (errno == ENOSPC)
+ return WERR_NO_SPOOL_SPACE;
+ else
+ return WERR_ACCESS_DENIED;
+ }
+
+ r_u->buffer_written = q_u->buffer_size2;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ * api_spoolss_getprinter
+ * called from the spoolss dispatcher
+ *
+ ********************************************************************/
+
+static WERROR control_printer(POLICY_HND *handle, uint32 command,
+ pipes_struct *p)
+{
+ int snum;
+ WERROR errcode = WERR_BADFUNC;
+ Printer_entry *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_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ switch (command) {
+ case PRINTER_CONTROL_PAUSE:
+ if (print_queue_pause(p->server_info, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case PRINTER_CONTROL_RESUME:
+ case PRINTER_CONTROL_UNPAUSE:
+ if (print_queue_resume(p->server_info, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case PRINTER_CONTROL_PURGE:
+ if (print_queue_purge(p->server_info, snum, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return errcode;
+}
+
+/********************************************************************
+ * api_spoolss_abortprinter
+ * From MSDN: "Deletes printer's spool file if printer is configured
+ * for spooling"
+ ********************************************************************/
+
+WERROR _spoolss_abortprinter(pipes_struct *p, SPOOL_Q_ABORTPRINTER *q_u, SPOOL_R_ABORTPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR errcode = WERR_OK;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_abortprinter: Invalid handle (%s:%u:%u)\n",OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ print_job_delete(p->server_info, snum, Printer->jobid, &errcode );
+
+ return errcode;
+}
+
+/********************************************************************
+ * called by spoolss_api_setprinter
+ * when updating a printer description
+ ********************************************************************/
+
+static WERROR update_printer_sec(POLICY_HND *handle, uint32 level,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ pipes_struct *p, SEC_DESC_BUF *secdesc_ctr)
+{
+ SEC_DESC_BUF *new_secdesc_ctr = NULL, *old_secdesc_ctr = NULL;
+ WERROR result;
+ int snum;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer || !get_printer_snum(p, handle, &snum, NULL)) {
+ DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n",
+ OUR_HANDLE(handle)));
+
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!secdesc_ctr) {
+ DEBUG(10,("update_printer_sec: secdesc_ctr is NULL !\n"));
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /* 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. */
+
+ if ( Printer->access_granted != PRINTER_ACCESS_ADMINISTER ) {
+ DEBUG(4,("update_printer_sec: updated denied by printer permissions\n"));
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* NT seems to like setting the security descriptor even though
+ nothing may have actually changed. */
+
+ if ( !nt_printing_getsec(p->mem_ctx, Printer->sharename, &old_secdesc_ctr)) {
+ DEBUG(2,("update_printer_sec: nt_printing_getsec() failed\n"));
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ SEC_ACL *the_acl;
+ int i;
+
+ the_acl = old_secdesc_ctr->sd->dacl;
+ DEBUG(10, ("old_secdesc_ctr for %s has %d aces:\n",
+ PRINTERNAME(snum), the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ DEBUG(10, ("%s 0x%08x\n", sid_string_dbg(
+ &the_acl->aces[i].trustee),
+ 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",
+ PRINTERNAME(snum), the_acl->num_aces));
+
+ for (i = 0; i < the_acl->num_aces; i++) {
+ DEBUG(10, ("%s 0x%08x\n", sid_string_dbg(
+ &the_acl->aces[i].trustee),
+ the_acl->aces[i].access_mask));
+ }
+ } else {
+ DEBUG(10, ("dacl for secdesc_ctr is NULL\n"));
+ }
+ }
+
+ new_secdesc_ctr = sec_desc_merge(p->mem_ctx, secdesc_ctr, old_secdesc_ctr);
+ if (!new_secdesc_ctr) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ if (sec_desc_equal(new_secdesc_ctr->sd, old_secdesc_ctr->sd)) {
+ result = WERR_OK;
+ goto done;
+ }
+
+ result = nt_printing_setsec(Printer->sharename, new_secdesc_ctr);
+
+ done:
+
+ return result;
+}
+
+/********************************************************************
+ Canonicalize printer info from a client
+
+ ATTN: It does not matter what we set the servername to hear
+ since we do the necessary work in get_a_printer() to set it to
+ the correct value based on what the client sent in the
+ _spoolss_open_printer_ex().
+ ********************************************************************/
+
+static bool check_printer_ok(NT_PRINTER_INFO_LEVEL_2 *info, 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",
+ info->servername, info->printername, info->sharename,
+ info->portname, info->drivername, info->comment, info->location));
+
+ /* we force some elements to "correct" values */
+ slprintf(info->servername, sizeof(info->servername)-1, "\\\\%s", global_myname());
+ fstrcpy(info->sharename, lp_servicename(snum));
+
+ /* check to see if we allow printername != sharename */
+
+ if ( lp_force_printername(snum) ) {
+ slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s",
+ global_myname(), info->sharename );
+ } else {
+
+ /* make sure printername is in \\server\printername format */
+
+ fstrcpy( printername, info->printername );
+ p = printername;
+ if ( printername[0] == '\\' && printername[1] == '\\' ) {
+ if ( (p = strchr_m( &printername[2], '\\' )) != NULL )
+ p++;
+ }
+
+ slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s",
+ global_myname(), p );
+ }
+
+ info->attributes |= PRINTER_ATTRIBUTE_SAMBA;
+ info->attributes &= ~PRINTER_ATTRIBUTE_NOT_SAMBA;
+
+
+
+ return True;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR add_port_hook(TALLOC_CTX *ctx, NT_USER_TOKEN *token, const char *portname, const char *uri )
+{
+ char *cmd = lp_addport_cmd();
+ char *command = NULL;
+ int ret;
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+ 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_NOMEM;
+ }
+
+ if ( token )
+ is_print_op = user_has_privileges( token, &se_printop );
+
+ DEBUG(10,("Running [%s]\n", command));
+
+ /********* BEGIN SePrintOperatorPrivilege **********/
+
+ if ( is_print_op )
+ become_root();
+
+ ret = smbrun(command, 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;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+bool add_printer_hook(TALLOC_CTX *ctx, NT_USER_TOKEN *token, NT_PRINTER_INFO_LEVEL *printer)
+{
+ char *cmd = lp_addprinter_cmd();
+ char **qlines;
+ char *command = NULL;
+ int numlines;
+ int ret;
+ int fd;
+ SE_PRIV se_printop = SE_PRINT_OPERATOR;
+ bool is_print_op = False;
+ char *remote_machine = talloc_strdup(ctx, "%m");
+
+ if (!remote_machine) {
+ return false;
+ }
+ remote_machine = talloc_sub_basic(ctx,
+ current_user_info.smb_name,
+ current_user_info.domain,
+ remote_machine);
+ if (!remote_machine) {
+ return false;
+ }
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
+ cmd, printer->info_2->printername, printer->info_2->sharename,
+ printer->info_2->portname, printer->info_2->drivername,
+ printer->info_2->location, printer->info_2->comment, remote_machine);
+ if (!command) {
+ return false;
+ }
+
+ if ( token )
+ is_print_op = user_has_privileges( token, &se_printop );
+
+ DEBUG(10,("Running [%s]\n", command));
+
+ /********* BEGIN SePrintOperatorPrivilege **********/
+
+ if ( is_print_op )
+ become_root();
+
+ if ( (ret = smbrun(command, &fd)) == 0 ) {
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(smbd_messaging_context(),
+ MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+ }
+
+ if ( is_print_op )
+ unbecome_root();
+
+ /********* END SePrintOperatorPrivilege **********/
+
+ DEBUGADD(10,("returned [%d]\n", ret));
+
+ TALLOC_FREE(command);
+ TALLOC_FREE(remote_machine);
+
+ if ( ret != 0 ) {
+ if (fd != -1)
+ close(fd);
+ return False;
+ }
+
+ /* reload our services immediately */
+ reload_services( False );
+
+ numlines = 0;
+ /* Get lines and convert them back to dos-codepage */
+ qlines = fd_lines_load(fd, &numlines, 0);
+ 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. */
+ strncpy(printer->info_2->portname, qlines[0], sizeof(printer->info_2->portname));
+ DEBUGADD(6,("Line[0] = [%s]\n", qlines[0]));
+ }
+
+ file_lines_free(qlines);
+ return True;
+}
+
+
+/********************************************************************
+ * Called by spoolss_api_setprinter
+ * when updating a printer description.
+ ********************************************************************/
+
+static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ DEVICEMODE *devmode)
+{
+ int snum;
+ NT_PRINTER_INFO_LEVEL *printer = NULL, *old_printer = NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ WERROR result;
+ UNISTR2 buffer;
+ fstring asc_buffer;
+
+ DEBUG(8,("update_printer\n"));
+
+ result = WERR_OK;
+
+ if (!Printer) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL)) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(get_a_printer(Printer, &printer, 2, lp_const_servicename(snum))) ||
+ (!W_ERROR_IS_OK(get_a_printer(Printer, &old_printer, 2, lp_const_servicename(snum))))) {
+ result = WERR_BADFID;
+ goto done;
+ }
+
+ DEBUGADD(8,("Converting info_2 struct\n"));
+
+ /*
+ * convert_printer_info converts the incoming
+ * info from the client and overwrites the info
+ * just read from the tdb in the pointer 'printer'.
+ */
+
+ if (!convert_printer_info(info, printer, level)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ if (devmode) {
+ /* we have a valid devmode
+ convert it and link it*/
+
+ DEBUGADD(8,("update_printer: Converting the devicemode struct\n"));
+ if (!convert_devicemode(printer->info_2->printername, devmode,
+ &printer->info_2->devmode)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ }
+
+ /* Do sanity check on the requested changes for Samba */
+
+ if (!check_printer_ok(printer->info_2, snum)) {
+ result = WERR_INVALID_PARAM;
+ 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_cmd()
+ && (!strequal(printer->info_2->drivername, old_printer->info_2->drivername)
+ || !strequal(printer->info_2->comment, old_printer->info_2->comment)
+ || !strequal(printer->info_2->portname, old_printer->info_2->portname)
+ || !strequal(printer->info_2->location, old_printer->info_2->location)) )
+ {
+ /* add_printer_hook() will call reload_services() */
+
+ if ( !add_printer_hook(p->mem_ctx, p->pipe_user.nt_user_token, printer) ) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+ }
+
+ /*
+ * When a *new* driver is bound to a printer, the drivername is used to
+ * lookup previously saved driver initialization info, which is then
+ * bound to the printer, simulating what happens in the Windows arch.
+ */
+ if (!strequal(printer->info_2->drivername, old_printer->info_2->drivername))
+ {
+ if (!set_driver_init(printer, 2))
+ {
+ DEBUG(5,("update_printer: Error restoring driver initialization data for driver [%s]!\n",
+ printer->info_2->drivername));
+ }
+
+ DEBUG(10,("update_printer: changing driver [%s]! Sending event!\n",
+ printer->info_2->drivername));
+
+ notify_printer_driver(snum, printer->info_2->drivername);
+ }
+
+ /*
+ * flag which changes actually occured. This is a small subset of
+ * all the possible changes. We also have to update things in the
+ * DsSpooler key.
+ */
+
+ if (!strequal(printer->info_2->comment, old_printer->info_2->comment)) {
+ init_unistr2( &buffer, printer->info_2->comment, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "description",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_comment(snum, printer->info_2->comment);
+ }
+
+ if (!strequal(printer->info_2->sharename, old_printer->info_2->sharename)) {
+ init_unistr2( &buffer, printer->info_2->sharename, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shareName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_sharename(snum, printer->info_2->sharename);
+ }
+
+ if (!strequal(printer->info_2->printername, old_printer->info_2->printername)) {
+ char *pname;
+
+ if ( (pname = strchr_m( printer->info_2->printername+2, '\\' )) != NULL )
+ pname++;
+ else
+ pname = printer->info_2->printername;
+
+
+ init_unistr2( &buffer, pname, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "printerName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_printername( snum, pname );
+ }
+
+ if (!strequal(printer->info_2->portname, old_printer->info_2->portname)) {
+ init_unistr2( &buffer, printer->info_2->portname, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "portName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_port(snum, printer->info_2->portname);
+ }
+
+ if (!strequal(printer->info_2->location, old_printer->info_2->location)) {
+ init_unistr2( &buffer, printer->info_2->location, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "location",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ notify_printer_location(snum, printer->info_2->location);
+ }
+
+ /* here we need to update some more DsSpooler keys */
+ /* uNCName, serverName, shortServerName */
+
+ init_unistr2( &buffer, global_myname(), UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "serverName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "shortServerName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ slprintf( asc_buffer, sizeof(asc_buffer)-1, "\\\\%s\\%s",
+ global_myname(), printer->info_2->sharename );
+ init_unistr2( &buffer, asc_buffer, UNI_STR_TERMINATE);
+ set_printer_dataex( printer, SPOOL_DSSPOOLER_KEY, "uNCName",
+ REG_SZ, (uint8*)buffer.buffer, buffer.uni_str_len*2 );
+
+ /* Update printer info */
+ result = mod_a_printer(printer, 2);
+
+done:
+ free_a_printer(&printer, 2);
+ free_a_printer(&old_printer, 2);
+
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+static WERROR publish_or_unpublish_printer(pipes_struct *p, POLICY_HND *handle,
+ const SPOOL_PRINTER_INFO_LEVEL *info)
+{
+#ifdef HAVE_ADS
+ SPOOL_PRINTER_INFO_LEVEL_7 *info7 = info->info_7;
+ int snum;
+ Printer_entry *Printer;
+
+ if ( lp_security() != SEC_ADS ) {
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("publish_or_unpublish_printer, action = %d\n",info7->action));
+
+ if (!Printer)
+ return WERR_BADFID;
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ nt_printer_publish(Printer, snum, info7->action);
+
+ return WERR_OK;
+#else
+ return WERR_UNKNOWN_LEVEL;
+#endif
+}
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinter(pipes_struct *p, SPOOL_Q_SETPRINTER *q_u, SPOOL_R_SETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+ DEVMODE_CTR devmode_ctr = q_u->devmode_ctr;
+ SEC_DESC_BUF *secdesc_ctr = q_u->secdesc_ctr;
+ uint32 command = q_u->command;
+ WERROR result;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* check the level */
+ switch (level) {
+ case 0:
+ return control_printer(handle, command, p);
+ case 2:
+ result = update_printer(p, handle, level, info, devmode_ctr.devmode);
+ if (!W_ERROR_IS_OK(result))
+ return result;
+ if (secdesc_ctr)
+ result = update_printer_sec(handle, level, info, p, secdesc_ctr);
+ return result;
+ case 3:
+ return update_printer_sec(handle, level, info, p,
+ secdesc_ctr);
+ case 7:
+ return publish_or_unpublish_printer(p, handle, info);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_fcpn(pipes_struct *p, SPOOL_Q_FCPN *q_u, SPOOL_R_FCPN *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer= find_printer_index_by_hnd(p, handle);
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_fcpn: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (Printer->notify.client_connected==True) {
+ int snum = -1;
+
+ if ( Printer->printer_type == SPLHND_SERVER)
+ snum = -1;
+ else if ( (Printer->printer_type == SPLHND_PRINTER) &&
+ !get_printer_snum(p, handle, &snum, NULL) )
+ return WERR_BADFID;
+
+ srv_spoolss_replycloseprinter(snum, &Printer->notify.client_hnd);
+ }
+
+ Printer->notify.flags=0;
+ Printer->notify.options=0;
+ Printer->notify.localmachine[0]='\0';
+ Printer->notify.printerlocal=0;
+ if (Printer->notify.option)
+ free_spool_notify_option(&Printer->notify.option);
+ Printer->notify.client_connected=False;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addjob(pipes_struct *p, SPOOL_Q_ADDJOB *q_u, SPOOL_R_ADDJOB *r_u)
+{
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (q_u->offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+
+ r_u->needed = 0;
+ return WERR_INVALID_PARAM; /* this is what a NT server
+ returns for AddJob. AddJob
+ must fail on non-local
+ printers */
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_job_info_1(JOB_INFO_1 *job_info, const print_queue_struct *queue,
+ int position, int snum,
+ const NT_PRINTER_INFO_LEVEL *ntprinter)
+{
+ struct tm *t;
+
+ t=gmtime(&queue->time);
+
+ job_info->jobid=queue->job;
+ init_unistr(&job_info->printername, lp_servicename(snum));
+ init_unistr(&job_info->machinename, ntprinter->info_2->servername);
+ init_unistr(&job_info->username, queue->fs_user);
+ init_unistr(&job_info->document, queue->fs_file);
+ init_unistr(&job_info->datatype, "RAW");
+ init_unistr(&job_info->text_status, "");
+ job_info->status=nt_printj_status(queue->status);
+ job_info->priority=queue->priority;
+ job_info->position=position;
+ job_info->totalpages=queue->page_count;
+ job_info->pagesprinted=0;
+
+ make_systemtime(&job_info->submitted, t);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static bool fill_job_info_2(JOB_INFO_2 *job_info, const print_queue_struct *queue,
+ int position, int snum,
+ const NT_PRINTER_INFO_LEVEL *ntprinter,
+ DEVICEMODE *devmode)
+{
+ struct tm *t;
+
+ t=gmtime(&queue->time);
+
+ job_info->jobid=queue->job;
+
+ init_unistr(&job_info->printername, ntprinter->info_2->printername);
+
+ init_unistr(&job_info->machinename, ntprinter->info_2->servername);
+ init_unistr(&job_info->username, queue->fs_user);
+ init_unistr(&job_info->document, queue->fs_file);
+ init_unistr(&job_info->notifyname, queue->fs_user);
+ init_unistr(&job_info->datatype, "RAW");
+ init_unistr(&job_info->printprocessor, "winprint");
+ init_unistr(&job_info->parameters, "");
+ init_unistr(&job_info->drivername, ntprinter->info_2->drivername);
+ init_unistr(&job_info->text_status, "");
+
+/* and here the security descriptor */
+
+ job_info->status=nt_printj_status(queue->status);
+ job_info->priority=queue->priority;
+ job_info->position=position;
+ job_info->starttime=0;
+ job_info->untiltime=0;
+ job_info->totalpages=queue->page_count;
+ job_info->size=queue->size;
+ make_systemtime(&(job_info->submitted), t);
+ job_info->timeelapsed=0;
+ job_info->pagesprinted=0;
+
+ job_info->devmode = devmode;
+
+ return (True);
+}
+
+/****************************************************************************
+ Enumjobs at level 1.
+****************************************************************************/
+
+static WERROR enumjobs_level1(const print_queue_struct *queue, int snum,
+ const NT_PRINTER_INFO_LEVEL *ntprinter,
+ RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ JOB_INFO_1 *info;
+ int i;
+ WERROR result = WERR_OK;
+
+ info=SMB_MALLOC_ARRAY(JOB_INFO_1,*returned);
+ if (info==NULL) {
+ *returned=0;
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<*returned; i++)
+ fill_job_info_1( &info[i], &queue[i], i, snum, ntprinter );
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_job_info_1(&info[i]);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_job_info_1("", buffer, &info[i], 0);
+
+out:
+ /* clear memory */
+ SAFE_FREE(info);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ Enumjobs at level 2.
+****************************************************************************/
+
+static WERROR enumjobs_level2(const print_queue_struct *queue, int snum,
+ const NT_PRINTER_INFO_LEVEL *ntprinter,
+ RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed, uint32 *returned)
+{
+ JOB_INFO_2 *info = NULL;
+ int i;
+ WERROR result = WERR_OK;
+ DEVICEMODE *devmode = NULL;
+
+ if ( !(info = SMB_MALLOC_ARRAY(JOB_INFO_2,*returned)) ) {
+ *returned=0;
+ return WERR_NOMEM;
+ }
+
+ /* this should not be a failure condition if the devmode is NULL */
+
+ devmode = construct_dev_mode(lp_const_servicename(snum));
+
+ for (i=0; i<*returned; i++)
+ fill_job_info_2(&(info[i]), &queue[i], i, snum, ntprinter, devmode);
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++)
+ (*needed) += spoolss_size_job_info_2(&info[i]);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the structures */
+ for (i=0; i<*returned; i++)
+ smb_io_job_info_2("", buffer, &info[i], 0);
+
+out:
+ free_devmode(devmode);
+ SAFE_FREE(info);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+
+}
+
+/****************************************************************************
+ Enumjobs.
+****************************************************************************/
+
+WERROR _spoolss_enumjobs( pipes_struct *p, SPOOL_Q_ENUMJOBS *q_u, SPOOL_R_ENUMJOBS *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+ WERROR wret;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ int snum;
+ print_status_struct prt_status;
+ print_queue_struct *queue=NULL;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumjobs\n"));
+
+ *needed=0;
+ *returned=0;
+
+ /* lookup the printer snum and tdb entry */
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ wret = get_a_printer(NULL, &ntprinter, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(wret) )
+ return wret;
+
+ *returned = print_queue_status(snum, &queue, &prt_status);
+ DEBUGADD(4,("count:[%d], status:[%d], [%s]\n", *returned, prt_status.status, prt_status.message));
+
+ if (*returned == 0) {
+ SAFE_FREE(queue);
+ free_a_printer(&ntprinter, 2);
+ return WERR_OK;
+ }
+
+ switch (level) {
+ case 1:
+ wret = enumjobs_level1(queue, snum, ntprinter, buffer, offered, needed, returned);
+ break;
+ case 2:
+ wret = enumjobs_level2(queue, snum, ntprinter, buffer, offered, needed, returned);
+ break;
+ default:
+ *returned=0;
+ wret = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ SAFE_FREE(queue);
+ free_a_printer( &ntprinter, 2 );
+ return wret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_schedulejob( pipes_struct *p, SPOOL_Q_SCHEDULEJOB *q_u, SPOOL_R_SCHEDULEJOB *r_u)
+{
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setjob(pipes_struct *p, SPOOL_Q_SETJOB *q_u, SPOOL_R_SETJOB *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 jobid = q_u->jobid;
+ uint32 command = q_u->command;
+
+ int snum;
+ WERROR errcode = WERR_BADFUNC;
+
+ if (!get_printer_snum(p, handle, &snum, NULL)) {
+ return WERR_BADFID;
+ }
+
+ if (!print_job_exists(lp_const_servicename(snum), jobid)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ switch (command) {
+ case JOB_CONTROL_CANCEL:
+ case JOB_CONTROL_DELETE:
+ if (print_job_delete(p->server_info, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case JOB_CONTROL_PAUSE:
+ if (print_job_pause(p->server_info, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ case JOB_CONTROL_RESTART:
+ case JOB_CONTROL_RESUME:
+ if (print_job_resume(p->server_info, snum, jobid, &errcode)) {
+ errcode = WERR_OK;
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return errcode;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 1.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level1(const char *servername, fstring architecture, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_1 *driver_info_1=NULL;
+ WERROR result = WERR_OK;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1) {
+ SAFE_FREE(driver_info_1);
+ return WERR_NOMEM;
+ }
+
+ if(ndrivers != 0) {
+ if((driver_info_1=SMB_REALLOC_ARRAY(driver_info_1, DRIVER_INFO_1, *returned+ndrivers )) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level1: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ SAFE_FREE(driver_info_1);
+ return status;
+ }
+ fill_printer_driver_info_1(&driver_info_1[*returned+i], driver, servername, architecture );
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_1(&driver_info_1[i]);
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the driver structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_1("", buffer, &driver_info_1[i], 0);
+ }
+
+out:
+ SAFE_FREE(driver_info_1);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 2.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level2(const char *servername, fstring architecture, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ DRIVER_INFO_2 *driver_info_2=NULL;
+ WERROR result = WERR_OK;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1) {
+ SAFE_FREE(driver_info_2);
+ return WERR_NOMEM;
+ }
+
+ if(ndrivers != 0) {
+ if((driver_info_2=SMB_REALLOC_ARRAY(driver_info_2, DRIVER_INFO_2, *returned+ndrivers )) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level2: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ SAFE_FREE(driver_info_2);
+ return status;
+ }
+ fill_printer_driver_info_2(&driver_info_2[*returned+i], driver, servername);
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_2(&(driver_info_2[i]));
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the form structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_2("", buffer, &(driver_info_2[i]), 0);
+ }
+
+out:
+ SAFE_FREE(driver_info_2);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers at level 3.
+****************************************************************************/
+
+static WERROR enumprinterdrivers_level3(const char *servername, fstring architecture, RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ int i;
+ int ndrivers;
+ uint32 version;
+ fstring *list = NULL;
+ DRIVER_INFO_3 *driver_info_3=NULL;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ WERROR result = WERR_OK;
+
+ *returned=0;
+
+ for (version=0; version<DRIVER_MAX_VERSION; version++) {
+ list=NULL;
+ ndrivers=get_ntdrivers(&list, architecture, version);
+ DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
+
+ if(ndrivers == -1) {
+ SAFE_FREE(driver_info_3);
+ return WERR_NOMEM;
+ }
+
+ if(ndrivers != 0) {
+ if((driver_info_3=SMB_REALLOC_ARRAY(driver_info_3, DRIVER_INFO_3, *returned+ndrivers )) == NULL) {
+ DEBUG(0,("enumprinterdrivers_level3: failed to enlarge driver info buffer!\n"));
+ SAFE_FREE(list);
+ return WERR_NOMEM;
+ }
+ }
+
+ for (i=0; i<ndrivers; i++) {
+ WERROR status;
+
+ DEBUGADD(5,("\tdriver: [%s]\n", list[i]));
+ ZERO_STRUCT(driver);
+ status = get_a_printer_driver(&driver, 3, list[i],
+ architecture, version);
+ if (!W_ERROR_IS_OK(status)) {
+ SAFE_FREE(list);
+ SAFE_FREE(driver_info_3);
+ return status;
+ }
+ fill_printer_driver_info_3(&driver_info_3[*returned+i], driver, servername);
+ free_a_printer_driver(driver, 3);
+ }
+
+ *returned+=ndrivers;
+ SAFE_FREE(list);
+ }
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d]'s size\n",i));
+ *needed += spoolss_size_printer_driver_info_3(&driver_info_3[i]);
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the driver structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding driver [%d] to buffer\n",i));
+ smb_io_printer_driver_info_3("", buffer, &driver_info_3[i], 0);
+ }
+
+out:
+ for (i=0; i<*returned; i++) {
+ SAFE_FREE(driver_info_3[i].dependentfiles);
+ }
+
+ SAFE_FREE(driver_info_3);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ Enumerates all printer drivers.
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdrivers( pipes_struct *p, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, SPOOL_R_ENUMPRINTERDRIVERS *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+ const char *cservername;
+ fstring servername;
+ fstring architecture;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumprinterdrivers\n"));
+
+ *needed = 0;
+ *returned = 0;
+
+ unistr2_to_ascii(architecture, &q_u->environment, sizeof(architecture));
+ unistr2_to_ascii(servername, &q_u->name, sizeof(servername));
+
+ cservername = canon_servername(servername);
+
+ if (!is_myname_or_ipaddr(cservername))
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+
+ switch (level) {
+ case 1:
+ return enumprinterdrivers_level1(cservername, architecture, buffer, offered, needed, returned);
+ case 2:
+ return enumprinterdrivers_level2(cservername, architecture, buffer, offered, needed, returned);
+ case 3:
+ return enumprinterdrivers_level3(cservername, architecture, buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_form_1(FORM_1 *form, nt_forms_struct *list)
+{
+ form->flag=list->flag;
+ init_unistr(&form->name, list->name);
+ form->width=list->width;
+ form->length=list->length;
+ form->left=list->left;
+ form->top=list->top;
+ form->right=list->right;
+ form->bottom=list->bottom;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumforms(pipes_struct *p, SPOOL_Q_ENUMFORMS *q_u, SPOOL_R_ENUMFORMS *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *numofforms = &r_u->numofforms;
+ uint32 numbuiltinforms;
+
+ nt_forms_struct *list=NULL;
+ nt_forms_struct *builtinlist=NULL;
+ FORM_1 *forms_1;
+ int buffer_size=0;
+ int i;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0) ) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumforms\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+ DEBUGADD(5,("Info level [%d]\n", level));
+
+ numbuiltinforms = get_builtin_ntforms(&builtinlist);
+ DEBUGADD(5,("Number of builtin forms [%d]\n", numbuiltinforms));
+ *numofforms = get_ntforms(&list);
+ DEBUGADD(5,("Number of user forms [%d]\n", *numofforms));
+ *numofforms += numbuiltinforms;
+
+ if (*numofforms == 0) {
+ SAFE_FREE(builtinlist);
+ SAFE_FREE(list);
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ switch (level) {
+ case 1:
+ if ((forms_1=SMB_MALLOC_ARRAY(FORM_1, *numofforms)) == NULL) {
+ SAFE_FREE(builtinlist);
+ SAFE_FREE(list);
+ *numofforms=0;
+ return WERR_NOMEM;
+ }
+
+ /* construct the list of form structures */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("Filling form number [%d]\n",i));
+ fill_form_1(&forms_1[i], &builtinlist[i]);
+ }
+
+ SAFE_FREE(builtinlist);
+
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("Filling form number [%d]\n",i));
+ fill_form_1(&forms_1[i], &list[i-numbuiltinforms]);
+ }
+
+ SAFE_FREE(list);
+
+ /* check the required size. */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("adding form [%d]'s size\n",i));
+ buffer_size += spoolss_size_form_1(&forms_1[i]);
+ }
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("adding form [%d]'s size\n",i));
+ buffer_size += spoolss_size_form_1(&forms_1[i]);
+ }
+
+ *needed=buffer_size;
+
+ if (*needed > offered) {
+ SAFE_FREE(forms_1);
+ *numofforms=0;
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, buffer_size)){
+ SAFE_FREE(forms_1);
+ *numofforms=0;
+ return WERR_NOMEM;
+ }
+
+ /* fill the buffer with the form structures */
+ for (i=0; i<numbuiltinforms; i++) {
+ DEBUGADD(6,("adding form [%d] to buffer\n",i));
+ smb_io_form_1("", buffer, &forms_1[i], 0);
+ }
+ for (; i<*numofforms; i++) {
+ DEBUGADD(6,("adding form [%d] to buffer\n",i));
+ smb_io_form_1("", buffer, &forms_1[i], 0);
+ }
+
+ SAFE_FREE(forms_1);
+
+ return WERR_OK;
+
+ default:
+ SAFE_FREE(list);
+ SAFE_FREE(builtinlist);
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getform(pipes_struct *p, SPOOL_Q_GETFORM *q_u, SPOOL_R_GETFORM *r_u)
+{
+ uint32 level = q_u->level;
+ UNISTR2 *uni_formname = &q_u->formname;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+
+ nt_forms_struct *list=NULL;
+ nt_forms_struct builtin_form;
+ bool foundBuiltin;
+ FORM_1 form_1;
+ fstring form_name;
+ int buffer_size=0;
+ int numofforms=0, i=0;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ unistr2_to_ascii(form_name, uni_formname, sizeof(form_name));
+
+ DEBUG(4,("_spoolss_getform\n"));
+ DEBUGADD(5,("Offered buffer size [%d]\n", offered));
+ DEBUGADD(5,("Info level [%d]\n", level));
+
+ foundBuiltin = get_a_builtin_ntform(uni_formname,&builtin_form);
+ if (!foundBuiltin) {
+ numofforms = get_ntforms(&list);
+ DEBUGADD(5,("Number of forms [%d]\n", numofforms));
+
+ if (numofforms == 0)
+ return WERR_BADFID;
+ }
+
+ switch (level) {
+ case 1:
+ if (foundBuiltin) {
+ fill_form_1(&form_1, &builtin_form);
+ } else {
+
+ /* Check if the requested name is in the list of form structures */
+ for (i=0; i<numofforms; i++) {
+
+ DEBUG(4,("_spoolss_getform: checking form %s (want %s)\n", list[i].name, form_name));
+
+ if (strequal(form_name, list[i].name)) {
+ DEBUGADD(6,("Found form %s number [%d]\n", form_name, i));
+ fill_form_1(&form_1, &list[i]);
+ break;
+ }
+ }
+
+ SAFE_FREE(list);
+ if (i == numofforms) {
+ return WERR_BADFID;
+ }
+ }
+ /* check the required size. */
+
+ *needed=spoolss_size_form_1(&form_1);
+
+ if (*needed > offered)
+ return WERR_INSUFFICIENT_BUFFER;
+
+ if (!rpcbuf_alloc_size(buffer, buffer_size))
+ return WERR_NOMEM;
+
+ /* fill the buffer with the form structures */
+ DEBUGADD(6,("adding form %s [%d] to buffer\n", form_name, i));
+ smb_io_form_1("", buffer, &form_1, 0);
+
+ return WERR_OK;
+
+ default:
+ SAFE_FREE(list);
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_port_1(PORT_INFO_1 *port, const char *name)
+{
+ init_unistr(&port->port_name, name);
+}
+
+/****************************************************************************
+ TODO: This probably needs distinguish between TCP/IP and Local ports
+ somehow.
+****************************************************************************/
+
+static void fill_port_2(PORT_INFO_2 *port, const char *name)
+{
+ init_unistr(&port->port_name, name);
+ init_unistr(&port->monitor_name, "Local Monitor");
+ init_unistr(&port->description, SPL_LOCAL_PORT );
+ port->port_type=PORT_TYPE_WRITE;
+ port->reserved=0x0;
+}
+
+
+/****************************************************************************
+ wrapper around the enumer ports command
+****************************************************************************/
+
+WERROR enumports_hook(TALLOC_CTX *ctx, int *count, char ***lines )
+{
+ char *cmd = lp_enumports_cmd();
+ 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 = SMB_MALLOC_ARRAY( char*, 2 ))) {
+ return WERR_NOMEM;
+ }
+ if (!(qlines[0] = SMB_STRDUP( SAMBA_PRINTER_PORT_NAME ))) {
+ SAFE_FREE(qlines);
+ return WERR_NOMEM;
+ }
+ qlines[1] = NULL;
+ numlines = 1;
+ }
+ else {
+ /* we have a valid enumport command */
+
+ command = talloc_asprintf(ctx, "%s \"%d\"", cmd, 1);
+ if (!command) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(10,("Running [%s]\n", command));
+ ret = smbrun(command, &fd);
+ 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);
+ DEBUGADD(10,("Lines returned = [%d]\n", numlines));
+ close(fd);
+ }
+
+ *count = numlines;
+ *lines = qlines;
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+ enumports level 1.
+****************************************************************************/
+
+static WERROR enumports_level_1(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PORT_INFO_1 *ports=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)) {
+ file_lines_free(qlines);
+ return result;
+ }
+
+ if(numlines) {
+ if((ports=SMB_MALLOC_ARRAY( PORT_INFO_1, numlines )) == NULL) {
+ DEBUG(10,("Returning WERR_NOMEM [%s]\n",
+ dos_errstr(WERR_NOMEM)));
+ file_lines_free(qlines);
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ fill_port_1(&ports[i], qlines[i]);
+ }
+ }
+ file_lines_free(qlines);
+
+ *returned = numlines;
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d]'s size\n", i));
+ *needed += spoolss_size_port_info_1(&ports[i]);
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the ports structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d] to buffer\n", i));
+ smb_io_port_1("", buffer, &ports[i], 0);
+ }
+
+out:
+ SAFE_FREE(ports);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ enumports level 2.
+****************************************************************************/
+
+static WERROR enumports_level_2(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PORT_INFO_2 *ports=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)) {
+ file_lines_free(qlines);
+ return result;
+ }
+
+ if(numlines) {
+ if((ports=SMB_MALLOC_ARRAY( PORT_INFO_2, numlines)) == NULL) {
+ file_lines_free(qlines);
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<numlines; i++) {
+ DEBUG(6,("Filling port number [%d] with port [%s]\n", i, qlines[i]));
+ fill_port_2(&(ports[i]), qlines[i]);
+ }
+ }
+
+ file_lines_free(qlines);
+
+ *returned = numlines;
+
+ /* check the required size. */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d]'s size\n", i));
+ *needed += spoolss_size_port_info_2(&ports[i]);
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ /* fill the buffer with the ports structures */
+ for (i=0; i<*returned; i++) {
+ DEBUGADD(6,("adding port [%d] to buffer\n", i));
+ smb_io_port_2("", buffer, &ports[i], 0);
+ }
+
+out:
+ SAFE_FREE(ports);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ enumports.
+****************************************************************************/
+
+WERROR _spoolss_enumports( pipes_struct *p, SPOOL_Q_ENUMPORTS *q_u, SPOOL_R_ENUMPORTS *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_enumports\n"));
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumports_level_1(buffer, offered, needed, returned);
+ case 2:
+ return enumports_level_2(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR spoolss_addprinterex_level_2( pipes_struct *p, const UNISTR2 *uni_srv_name,
+ const SPOOL_PRINTER_INFO_LEVEL *info,
+ DEVICEMODE *devmode, SEC_DESC_BUF *sec_desc_buf,
+ uint32 user_switch, const SPOOL_USER_CTR *user,
+ POLICY_HND *handle)
+{
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ fstring name;
+ int snum;
+ WERROR err = WERR_OK;
+
+ if ( !(printer = TALLOC_ZERO_P(NULL, NT_PRINTER_INFO_LEVEL)) ) {
+ DEBUG(0,("spoolss_addprinterex_level_2: malloc fail.\n"));
+ return WERR_NOMEM;
+ }
+
+ /* convert from UNICODE to ASCII - this allocates the info_2 struct inside *printer.*/
+ if (!convert_printer_info(info, printer, 2)) {
+ free_a_printer(&printer, 2);
+ return WERR_NOMEM;
+ }
+
+ /* check to see if the printer already exists */
+
+ if ((snum = print_queue_snum(printer->info_2->sharename)) != -1) {
+ DEBUG(5, ("spoolss_addprinterex_level_2: Attempted to add a printer named [%s] when one already existed!\n",
+ printer->info_2->sharename));
+ free_a_printer(&printer, 2);
+ return WERR_PRINTER_ALREADY_EXISTS;
+ }
+
+ /* FIXME!!! smbd should check to see if the driver is installed before
+ trying to add a printer like this --jerry */
+
+ if (*lp_addprinter_cmd() ) {
+ if ( !add_printer_hook(p->mem_ctx, p->pipe_user.nt_user_token, printer) ) {
+ free_a_printer(&printer,2);
+ 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",
+ printer->info_2->sharename ));
+ }
+
+ /* use our primary netbios name since get_a_printer() will convert
+ it to what the client expects on a case by case basis */
+
+ slprintf(name, sizeof(name)-1, "\\\\%s\\%s", global_myname(),
+ printer->info_2->sharename);
+
+
+ if ((snum = print_queue_snum(printer->info_2->sharename)) == -1) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* you must be a printer admin to add a new printer */
+ if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ /*
+ * Do sanity check on the requested changes for Samba.
+ */
+
+ if (!check_printer_ok(printer->info_2, snum)) {
+ free_a_printer(&printer,2);
+ return WERR_INVALID_PARAM;
+ }
+
+ /*
+ * When a printer is created, the drivername bound to the printer is used
+ * to lookup previously saved driver initialization info, which is then
+ * bound to the new printer, simulating what happens in the Windows arch.
+ */
+
+ if (!devmode)
+ {
+ set_driver_init(printer, 2);
+ }
+ else
+ {
+ /* A valid devmode was included, convert and link it
+ */
+ DEBUGADD(10, ("spoolss_addprinterex_level_2: devmode included, converting\n"));
+
+ if (!convert_devicemode(printer->info_2->printername, devmode,
+ &printer->info_2->devmode))
+ return WERR_NOMEM;
+ }
+
+ /* write the ASCII on disk */
+ err = mod_a_printer(printer, 2);
+ if (!W_ERROR_IS_OK(err)) {
+ free_a_printer(&printer,2);
+ return err;
+ }
+
+ if (!open_printer_hnd(p, handle, name, PRINTER_ACCESS_ADMINISTER)) {
+ /* Handle open failed - remove addition. */
+ del_a_printer(printer->info_2->sharename);
+ free_a_printer(&printer,2);
+ return WERR_ACCESS_DENIED;
+ }
+
+ update_c_setprinter(False);
+ free_a_printer(&printer,2);
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterex( pipes_struct *p, SPOOL_Q_ADDPRINTEREX *q_u, SPOOL_R_ADDPRINTEREX *r_u)
+{
+ UNISTR2 *uni_srv_name = q_u->server_name;
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_INFO_LEVEL *info = &q_u->info;
+ DEVICEMODE *devmode = q_u->devmode_ctr.devmode;
+ SEC_DESC_BUF *sdb = q_u->secdesc_ctr;
+ uint32 user_switch = q_u->user_switch;
+ SPOOL_USER_CTR *user = &q_u->user_ctr;
+ POLICY_HND *handle = &r_u->handle;
+
+ switch (level) {
+ case 1:
+ /* we don't handle yet */
+ /* but I know what to do ... */
+ return WERR_UNKNOWN_LEVEL;
+ case 2:
+ return spoolss_addprinterex_level_2(p, uni_srv_name, info,
+ devmode, sdb,
+ user_switch, user, handle);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addprinterdriver(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVER *q_u, SPOOL_R_ADDPRINTERDRIVER *r_u)
+{
+ uint32 level = q_u->level;
+ SPOOL_PRINTER_DRIVER_INFO_LEVEL *info = &q_u->info;
+ WERROR err = WERR_OK;
+ NT_PRINTER_DRIVER_INFO_LEVEL driver;
+ fstring driver_name;
+ uint32 version;
+
+ ZERO_STRUCT(driver);
+
+ if (!convert_printer_driver_info(info, &driver, level)) {
+ err = WERR_NOMEM;
+ goto done;
+ }
+
+ DEBUG(5,("Cleaning driver's information\n"));
+ err = clean_up_driver_struct(driver, level, &p->pipe_user);
+ if (!W_ERROR_IS_OK(err))
+ goto done;
+
+ DEBUG(5,("Moving driver to final destination\n"));
+ if( !W_ERROR_IS_OK(err = move_driver_to_download_area(driver, level, &p->pipe_user, &err)) ) {
+ goto done;
+ }
+
+ if (add_a_printer_driver(driver, level)!=0) {
+ err = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ switch(level) {
+ case 3:
+ fstrcpy(driver_name,
+ driver.info_3->name ? driver.info_3->name : "");
+ break;
+ case 6:
+ fstrcpy(driver_name,
+ driver.info_6->name ? driver.info_6->name : "");
+ break;
+ }
+
+ /*
+ * I think this is where he 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)) {
+ DEBUG(0,("_spoolss_addprinterdriver: Failed to send message about upgrading driver [%s]!\n",
+ driver_name));
+ }
+
+ /*
+ * Based on the version (e.g. driver destination dir: 0=9x,2=Nt/2k,3=2k/Xp),
+ * decide if the driver init data should be deleted. The rules are:
+ * 1) never delete init data if it is a 9x driver, they don't use it anyway
+ * 2) delete init data only if there is no 2k/Xp driver
+ * 3) always delete init data
+ * The generalized rule is always use init data from the highest order driver.
+ * It is necessary to follow the driver install by an initialization step to
+ * finish off this process.
+ */
+ if (level == 3)
+ version = driver.info_3->cversion;
+ else if (level == 6)
+ version = driver.info_6->version;
+ else
+ version = -1;
+ switch (version) {
+ /*
+ * 9x printer driver - never delete init data
+ */
+ case 0:
+ DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for 9x driver [%s]\n",
+ driver_name));
+ break;
+
+ /*
+ * Nt or 2k (compatiblity mode) printer driver - only delete init data if
+ * there is no 2k/Xp driver init data for this driver name.
+ */
+ case 2:
+ {
+ NT_PRINTER_DRIVER_INFO_LEVEL driver1;
+
+ if (!W_ERROR_IS_OK(get_a_printer_driver(&driver1, 3, driver_name, "Windows NT x86", 3))) {
+ /*
+ * No 2k/Xp driver found, delete init data (if any) for the new Nt driver.
+ */
+ if (!del_driver_init(driver_name))
+ DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) Nt failed!\n", driver_name));
+ } else {
+ /*
+ * a 2k/Xp driver was found, don't delete init data because Nt driver will use it.
+ */
+ free_a_printer_driver(driver1,3);
+ DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for Nt driver [%s]\n",
+ driver_name));
+ }
+ }
+ break;
+
+ /*
+ * 2k or Xp printer driver - always delete init data
+ */
+ case 3:
+ if (!del_driver_init(driver_name))
+ DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) 2k/Xp failed!\n", driver_name));
+ break;
+
+ default:
+ DEBUG(0,("_spoolss_addprinterdriver: invalid level=%d\n", level));
+ break;
+ }
+
+
+done:
+ free_a_printer_driver(driver, level);
+ return err;
+}
+
+/********************************************************************
+ * spoolss_addprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_addprinterdriverex(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, SPOOL_R_ADDPRINTERDRIVEREX *r_u)
+{
+ SPOOL_Q_ADDPRINTERDRIVER q_u_local;
+ SPOOL_R_ADDPRINTERDRIVER r_u_local;
+
+ /*
+ * we only support the semantics of AddPrinterDriver()
+ * i.e. only copy files that are newer than existing ones
+ */
+
+ if ( q_u->copy_flags != APD_COPY_NEW_FILES )
+ return WERR_ACCESS_DENIED;
+
+ ZERO_STRUCT(q_u_local);
+ ZERO_STRUCT(r_u_local);
+
+ /* just pass the information off to _spoolss_addprinterdriver() */
+ q_u_local.server_name_ptr = q_u->server_name_ptr;
+ copy_unistr2(&q_u_local.server_name, &q_u->server_name);
+ q_u_local.level = q_u->level;
+ memcpy( &q_u_local.info, &q_u->info, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL) );
+
+ return _spoolss_addprinterdriver( p, &q_u_local, &r_u_local );
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_driverdir_1(DRIVER_DIRECTORY_1 *info, char *name)
+{
+ init_unistr(&info->name, name);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getprinterdriverdir_level_1(UNISTR2 *name, UNISTR2 *uni_environment, RPC_BUFFER *buffer, uint32 offered, uint32 *needed)
+{
+ char *path = NULL;
+ char *long_archi = NULL;
+ char *servername = NULL;
+ const char *pservername = NULL;
+ const char *short_archi;
+ DRIVER_DIRECTORY_1 *info=NULL;
+ WERROR result = WERR_OK;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ servername = unistr2_to_ascii_talloc(ctx, name);
+ if (!servername) {
+ return WERR_NOMEM;
+ }
+ long_archi = unistr2_to_ascii_talloc(ctx, uni_environment);
+ if (!long_archi) {
+ return WERR_NOMEM;
+ }
+
+ pservername = canon_servername(servername);
+
+ if ( !is_myname_or_ipaddr(pservername))
+ return WERR_INVALID_PARAM;
+
+ if (!(short_archi = get_short_archi(long_archi)))
+ return WERR_INVALID_ENVIRONMENT;
+
+ if((info=SMB_MALLOC_P(DRIVER_DIRECTORY_1)) == NULL)
+ return WERR_NOMEM;
+
+ path = talloc_asprintf(ctx,
+ "\\\\%s\\print$\\%s", pservername, short_archi);
+ if (!path) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ DEBUG(4,("printer driver directory: [%s]\n", path));
+
+ fill_driverdir_1(info, path);
+
+ *needed += spoolss_size_driverdir_info_1(info);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ smb_io_driverdir_1("", buffer, info, 0);
+
+out:
+ SAFE_FREE(info);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getprinterdriverdirectory(pipes_struct *p, SPOOL_Q_GETPRINTERDRIVERDIR *q_u, SPOOL_R_GETPRINTERDRIVERDIR *r_u)
+{
+ UNISTR2 *name = &q_u->name;
+ UNISTR2 *uni_environment = &q_u->environment;
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(4,("_spoolss_getprinterdriverdirectory\n"));
+
+ *needed=0;
+
+ switch(level) {
+ case 1:
+ return getprinterdriverdir_level_1(name, uni_environment, buffer, offered, needed);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, SPOOL_R_ENUMPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 idx = q_u->index;
+ uint32 in_value_len = q_u->valuesize;
+ uint32 in_data_len = q_u->datasize;
+ uint32 *out_max_value_len = &r_u->valuesize;
+ uint16 **out_value = &r_u->value;
+ uint32 *out_value_len = &r_u->realvaluesize;
+ uint32 *out_type = &r_u->type;
+ uint32 *out_max_data_len = &r_u->datasize;
+ uint8 **data_out = &r_u->data;
+ uint32 *out_data_len = &r_u->realdatasize;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ uint32 biggest_valuesize;
+ uint32 biggest_datasize;
+ uint32 data_len;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR result;
+ REGISTRY_VALUE *val = NULL;
+ NT_PRINTER_DATA *p_data;
+ int i, key_index, num_values;
+ int name_length;
+
+ *out_type = 0;
+
+ *out_max_data_len = 0;
+ *data_out = NULL;
+ *out_data_len = 0;
+
+ DEBUG(5,("spoolss_enumprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+ result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ return result;
+
+ p_data = printer->info_2->data;
+ key_index = lookup_printerkey( p_data, SPOOL_PRINTERDATA_KEY );
+
+ result = WERR_OK;
+
+ /*
+ * The NT machine wants to know the biggest size of value and data
+ *
+ * cf: MSDN EnumPrinterData remark section
+ */
+
+ if ( !in_value_len && !in_data_len && (key_index != -1) )
+ {
+ DEBUGADD(6,("Activating NT mega-hack to find sizes\n"));
+
+ biggest_valuesize = 0;
+ biggest_datasize = 0;
+
+ num_values = regval_ctr_numvals( p_data->keys[key_index].values );
+
+ for ( i=0; i<num_values; i++ )
+ {
+ val = regval_ctr_specific_value( p_data->keys[key_index].values, i );
+
+ name_length = strlen(val->valuename);
+ if ( strlen(val->valuename) > biggest_valuesize )
+ biggest_valuesize = name_length;
+
+ if ( val->size > biggest_datasize )
+ biggest_datasize = val->size;
+
+ 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 */
+
+ *out_value_len = 2 * (1+biggest_valuesize);
+ *out_data_len = biggest_datasize;
+
+ DEBUG(6,("final values: [%d], [%d]\n", *out_value_len, *out_data_len));
+
+ goto done;
+ }
+
+ /*
+ * the value len is wrong in NT sp3
+ * that's the number of bytes not the number of unicode chars
+ */
+
+ if ( key_index != -1 )
+ val = regval_ctr_specific_value( p_data->keys[key_index].values, idx );
+
+ if ( !val )
+ {
+
+ /* out_value should default to "" or else NT4 has
+ problems unmarshalling the response */
+
+ *out_max_value_len=(in_value_len/sizeof(uint16));
+
+ if (in_value_len) {
+ if((*out_value=(uint16 *)TALLOC_ZERO(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL)
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ *out_value_len = (uint32)rpcstr_push((char *)*out_value, "", in_value_len, 0);
+ } else {
+ *out_value=NULL;
+ *out_value_len = 0;
+ }
+
+ /* the data is counted in bytes */
+
+ *out_max_data_len = in_data_len;
+ *out_data_len = in_data_len;
+
+ /* only allocate when given a non-zero data_len */
+
+ if ( in_data_len && ((*data_out=(uint8 *)TALLOC_ZERO(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) )
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ 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 */
+ *out_max_value_len=(in_value_len/sizeof(uint16));
+ if (in_value_len) {
+ if ( (*out_value = (uint16 *)TALLOC_ZERO(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL )
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ *out_value_len = (uint32)rpcstr_push((char *)*out_value, regval_name(val), (size_t)in_value_len, 0);
+ } else {
+ *out_value = NULL;
+ *out_value_len = 0;
+ }
+
+ /* type */
+
+ *out_type = regval_type( val );
+
+ /* data - counted in bytes */
+
+ *out_max_data_len = in_data_len;
+ if ( in_data_len && (*data_out = (uint8 *)TALLOC_ZERO(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL)
+ {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ data_len = regval_size(val);
+ if ( *data_out && data_len )
+ memcpy( *data_out, regval_data_p(val), data_len );
+ *out_data_len = data_len;
+ }
+
+done:
+ free_a_printer(&printer, 2);
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setprinterdata( pipes_struct *p, SPOOL_Q_SETPRINTERDATA *q_u, SPOOL_R_SETPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->value;
+ uint32 type = q_u->type;
+ uint8 *data = q_u->data;
+ uint32 real_len = q_u->real_len;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ fstring valuename;
+
+ DEBUG(5,("spoolss_setprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( Printer->printer_type == SPLHND_SERVER ) {
+ DEBUG(10,("_spoolss_setprinterdata: Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privildge.
+ * 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_setprinterdata: change denied by handle access permissions\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii(valuename, value, sizeof(valuename));
+
+ /*
+ * When client side code sets a magic printer data key, detect it and save
+ * the current printer data and the magic key's data (its the DEVMODE) for
+ * future printer/driver initializations.
+ */
+ if ( (type == REG_BINARY) && strequal( valuename, PHANTOM_DEVMODE_KEY))
+ {
+ /* Set devmode and printer initialization info */
+ status = save_driver_init( printer, 2, data, real_len );
+
+ srv_spoolss_reset_printerdata( printer->info_2->drivername );
+ }
+ else
+ {
+ status = set_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename,
+ type, data, real_len );
+ if ( W_ERROR_IS_OK(status) )
+ status = mod_a_printer(printer, 2);
+ }
+
+done:
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_resetprinter(pipes_struct *p, SPOOL_Q_RESETPRINTER *q_u, SPOOL_R_RESETPRINTER *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, 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(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+
+ /* blindly return success */
+ return WERR_OK;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteprinterdata(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATA *q_u, SPOOL_R_DELETEPRINTERDATA *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->valuename;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ char *valuename = NULL;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ DEBUG(5,("spoolss_deleteprinterdata\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterdata: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ valuename = unistr2_to_ascii_talloc(ctx, value);
+ if (!valuename) {
+ free_a_printer(&printer, 2);
+ return WERR_NOMEM;
+ }
+
+ status = delete_printer_dataex( printer, SPOOL_PRINTERDATA_KEY, valuename );
+
+ if ( W_ERROR_IS_OK(status) )
+ mod_a_printer( printer, 2 );
+
+ free_a_printer(&printer, 2);
+ TALLOC_FREE(valuename);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_addform( pipes_struct *p, SPOOL_Q_ADDFORM *q_u, SPOOL_R_ADDFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ FORM *form = &q_u->form;
+ nt_forms_struct tmpForm;
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("spoolss_addform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_addform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+
+ /* forms can be added on printer of on the print server handle */
+
+ if ( Printer->printer_type == SPLHND_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_addform: denied by handle permissions.\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't add if builtin */
+
+ if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+ status = WERR_ALREADY_EXISTS;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+
+ if(!add_a_form(&list, form, &count)) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ write_ntforms(&list, count);
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == SPLHND_PRINTER )
+ status = mod_a_printer(printer, 2);
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_deleteform( pipes_struct *p, SPOOL_Q_DELETEFORM *q_u, SPOOL_R_DELETEFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *form_name = &q_u->name;
+ nt_forms_struct tmpForm;
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ DEBUG(5,("spoolss_deleteform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* forms can be deleted on printer of on the print server handle */
+
+ if ( Printer->printer_type == SPLHND_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_deleteform: denied by handle permissions.\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't delete if builtin */
+
+ if (get_a_builtin_ntform(form_name,&tmpForm)) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+
+ if ( !delete_a_form(&list, form_name, &count, &status ))
+ goto done;
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == SPLHND_PRINTER )
+ status = mod_a_printer(printer, 2);
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_setform(pipes_struct *p, SPOOL_Q_SETFORM *q_u, SPOOL_R_SETFORM *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ FORM *form = &q_u->form;
+ nt_forms_struct tmpForm;
+ int snum;
+ WERROR status = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+ int count=0;
+ nt_forms_struct *list=NULL;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ DEBUG(5,("spoolss_setform\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_setform: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* forms can be modified on printer of on the print server handle */
+
+ if ( Printer->printer_type == SPLHND_PRINTER )
+ {
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ goto done;
+ }
+
+ if ( !(Printer->access_granted & (PRINTER_ACCESS_ADMINISTER|SERVER_ACCESS_ADMINISTER)) ) {
+ DEBUG(2,("_spoolss_setform: denied by handle permissions\n"));
+ status = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ /* can't set if builtin */
+ if (get_a_builtin_ntform(&form->name,&tmpForm)) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ count = get_ntforms(&list);
+ update_a_form(&list, form, count);
+ write_ntforms(&list, count);
+
+ /*
+ * ChangeID must always be set if this is a printer
+ */
+
+ if ( Printer->printer_type == SPLHND_PRINTER )
+ status = mod_a_printer(printer, 2);
+
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+ SAFE_FREE(list);
+
+ return status;
+}
+
+/****************************************************************************
+ enumprintprocessors level 1.
+****************************************************************************/
+
+static WERROR enumprintprocessors_level_1(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTPROCESSOR_1 *info_1=NULL;
+ WERROR result = WERR_OK;
+
+ if((info_1 = SMB_MALLOC_P(PRINTPROCESSOR_1)) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "winprint");
+
+ *needed += spoolss_size_printprocessor_info_1(info_1);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ smb_io_printprocessor_info_1("", buffer, info_1, 0);
+
+out:
+ SAFE_FREE(info_1);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocessors(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, SPOOL_R_ENUMPRINTPROCESSORS *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ 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.
+ */
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintprocessors_level_1(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+ enumprintprocdatatypes level 1.
+****************************************************************************/
+
+static WERROR enumprintprocdatatypes_level_1(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTPROCDATATYPE_1 *info_1=NULL;
+ WERROR result = WERR_OK;
+
+ if((info_1 = SMB_MALLOC_P(PRINTPROCDATATYPE_1)) == NULL)
+ return WERR_NOMEM;
+
+ (*returned) = 0x1;
+
+ init_unistr(&info_1->name, "RAW");
+
+ *needed += spoolss_size_printprocdatatype_info_1(info_1);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ smb_io_printprocdatatype_info_1("", buffer, info_1, 0);
+
+out:
+ SAFE_FREE(info_1);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintprocdatatypes(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("_spoolss_enumprintprocdatatypes\n"));
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintprocdatatypes_level_1(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+ enumprintmonitors level 1.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_1(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTMONITOR_1 *info_1;
+ WERROR result = WERR_OK;
+ int i;
+
+ if((info_1 = SMB_MALLOC_ARRAY(PRINTMONITOR_1, 2)) == NULL)
+ return WERR_NOMEM;
+
+ *returned = 2;
+
+ init_unistr(&(info_1[0].name), SPL_LOCAL_PORT );
+ init_unistr(&(info_1[1].name), SPL_TCPIP_PORT );
+
+ for ( i=0; i<*returned; i++ ) {
+ *needed += spoolss_size_printmonitor_info_1(&info_1[i]);
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ for ( i=0; i<*returned; i++ ) {
+ smb_io_printmonitor_info_1("", buffer, &info_1[i], 0);
+ }
+
+out:
+ SAFE_FREE(info_1);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+ enumprintmonitors level 2.
+****************************************************************************/
+
+static WERROR enumprintmonitors_level_2(RPC_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+{
+ PRINTMONITOR_2 *info_2;
+ WERROR result = WERR_OK;
+ int i;
+
+ if((info_2 = SMB_MALLOC_ARRAY(PRINTMONITOR_2, 2)) == NULL)
+ return WERR_NOMEM;
+
+ *returned = 2;
+
+ init_unistr( &(info_2[0].name), SPL_LOCAL_PORT );
+ init_unistr( &(info_2[0].environment), "Windows NT X86" );
+ init_unistr( &(info_2[0].dll_name), "localmon.dll" );
+
+ init_unistr( &(info_2[1].name), SPL_TCPIP_PORT );
+ init_unistr( &(info_2[1].environment), "Windows NT X86" );
+ init_unistr( &(info_2[1].dll_name), "tcpmon.dll" );
+
+ for ( i=0; i<*returned; i++ ) {
+ *needed += spoolss_size_printmonitor_info_2(&info_2[i]);
+ }
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ for ( i=0; i<*returned; i++ ) {
+ smb_io_printmonitor_info_2("", buffer, &info_2[i], 0);
+ }
+
+out:
+ SAFE_FREE(info_2);
+
+ if ( !W_ERROR_IS_OK(result) )
+ *returned = 0;
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_enumprintmonitors(pipes_struct *p, SPOOL_Q_ENUMPRINTMONITORS *q_u, SPOOL_R_ENUMPRINTMONITORS *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ uint32 *returned = &r_u->returned;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_enumprintmonitors\n"));
+
+ /*
+ * Enumerate the print monitors ...
+ *
+ * Just reply with "Local Port", to keep NT happy
+ * and I can use my nice printer checker.
+ */
+
+ *returned=0;
+ *needed=0;
+
+ switch (level) {
+ case 1:
+ return enumprintmonitors_level_1(buffer, offered, needed, returned);
+ case 2:
+ return enumprintmonitors_level_2(buffer, offered, needed, returned);
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_1(print_queue_struct **queue, int count, int snum,
+ NT_PRINTER_INFO_LEVEL *ntprinter,
+ uint32 jobid, RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed)
+{
+ int i=0;
+ bool found=False;
+ JOB_INFO_1 *info_1=NULL;
+ WERROR result = WERR_OK;
+
+ info_1=SMB_MALLOC_P(JOB_INFO_1);
+
+ if (info_1 == NULL) {
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i<count && found==False; i++) {
+ if ((*queue)[i].job==(int)jobid)
+ found=True;
+ }
+
+ if (found==False) {
+ SAFE_FREE(info_1);
+ /* NT treats not found as bad param... yet another bad choice */
+ return WERR_INVALID_PARAM;
+ }
+
+ fill_job_info_1( info_1, &((*queue)[i-1]), i, snum, ntprinter );
+
+ *needed += spoolss_size_job_info_1(info_1);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto out;
+ }
+
+ smb_io_job_info_1("", buffer, info_1, 0);
+
+out:
+ SAFE_FREE(info_1);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR getjob_level_2(print_queue_struct **queue, int count, int snum,
+ NT_PRINTER_INFO_LEVEL *ntprinter,
+ uint32 jobid, RPC_BUFFER *buffer, uint32 offered,
+ uint32 *needed)
+{
+ int i = 0;
+ bool found = False;
+ JOB_INFO_2 *info_2;
+ WERROR result;
+ DEVICEMODE *devmode = NULL;
+ NT_DEVICEMODE *nt_devmode = NULL;
+
+ if ( !(info_2=SMB_MALLOC_P(JOB_INFO_2)) )
+ return WERR_NOMEM;
+
+ ZERO_STRUCTP(info_2);
+
+ for ( i=0; i<count && found==False; i++ )
+ {
+ if ((*queue)[i].job == (int)jobid)
+ found = True;
+ }
+
+ if ( !found ) {
+ /* NT treats not found as bad param... yet another bad
+ choice */
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /*
+ * 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
+ */
+
+ if ( !(nt_devmode=print_job_devmode( lp_const_servicename(snum), jobid )) )
+ devmode = construct_dev_mode(lp_const_servicename(snum));
+ else {
+ if ((devmode = SMB_MALLOC_P(DEVICEMODE)) != NULL) {
+ ZERO_STRUCTP( devmode );
+ convert_nt_devicemode( devmode, nt_devmode );
+ }
+ }
+
+ fill_job_info_2(info_2, &((*queue)[i-1]), i, snum, ntprinter, devmode);
+
+ *needed += spoolss_size_job_info_2(info_2);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto done;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ smb_io_job_info_2("", buffer, info_2, 0);
+
+ result = WERR_OK;
+
+ done:
+ /* Cleanup allocated memory */
+
+ free_job_info_2(info_2); /* Also frees devmode */
+ SAFE_FREE(info_2);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR _spoolss_getjob( pipes_struct *p, SPOOL_Q_GETJOB *q_u, SPOOL_R_GETJOB *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 jobid = q_u->jobid;
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ WERROR wstatus = WERR_OK;
+ NT_PRINTER_INFO_LEVEL *ntprinter = NULL;
+ int snum;
+ int count;
+ print_queue_struct *queue = NULL;
+ print_status_struct prt_status;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("spoolss_getjob\n"));
+
+ *needed = 0;
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ wstatus = get_a_printer(NULL, &ntprinter, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(wstatus) )
+ return wstatus;
+
+ count = print_queue_status(snum, &queue, &prt_status);
+
+ DEBUGADD(4,("count:[%d], prt_status:[%d], [%s]\n",
+ count, prt_status.status, prt_status.message));
+
+ switch ( level ) {
+ case 1:
+ wstatus = getjob_level_1(&queue, count, snum, ntprinter, jobid,
+ buffer, offered, needed);
+ break;
+ case 2:
+ wstatus = getjob_level_2(&queue, count, snum, ntprinter, jobid,
+ buffer, offered, needed);
+ break;
+ default:
+ wstatus = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ SAFE_FREE(queue);
+ free_a_printer( &ntprinter, 2 );
+
+ return wstatus;
+}
+
+/********************************************************************
+ spoolss_getprinterdataex
+
+ From MSDN documentation of GetPrinterDataEx: pass request
+ to GetPrinterData if key is "PrinterDriverData".
+ ********************************************************************/
+
+WERROR _spoolss_getprinterdataex(pipes_struct *p, SPOOL_Q_GETPRINTERDATAEX *q_u, SPOOL_R_GETPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 in_size = q_u->size;
+ uint32 *type = &r_u->type;
+ uint32 *out_size = &r_u->size;
+ uint8 **data = &r_u->data;
+ uint32 *needed = &r_u->needed;
+ fstring keyname, valuename;
+
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_OK;
+
+ DEBUG(4,("_spoolss_getprinterdataex\n"));
+
+ unistr2_to_ascii(keyname, &q_u->keyname, sizeof(keyname));
+ unistr2_to_ascii(valuename, &q_u->valuename, sizeof(valuename));
+
+ DEBUG(10, ("_spoolss_getprinterdataex: key => [%s], value => [%s]\n",
+ keyname, valuename));
+
+ /* in case of problem, return some default values */
+
+ *needed = 0;
+ *type = 0;
+ *out_size = in_size;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_getprinterdataex: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ status = WERR_BADFID;
+ goto done;
+ }
+
+ /* Is the handle to a printer or to the server? */
+
+ if (Printer->printer_type == SPLHND_SERVER) {
+ DEBUG(10,("_spoolss_getprinterdataex: Not implemented for server handles yet\n"));
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum, NULL) )
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if ( !W_ERROR_IS_OK(status) )
+ goto done;
+
+ /* check to see if the keyname is valid */
+ if ( !strlen(keyname) ) {
+ status = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ if ( lookup_printerkey( printer->info_2->data, keyname ) == -1 ) {
+ DEBUG(4,("_spoolss_getprinterdataex: Invalid keyname [%s]\n", keyname ));
+ free_a_printer( &printer, 2 );
+ status = WERR_BADFILE;
+ goto done;
+ }
+
+ /* When given a new keyname, we should just create it */
+
+ status = get_printer_dataex( p->mem_ctx, printer, keyname, valuename, type, data, needed, in_size );
+
+ if (*needed > *out_size)
+ status = WERR_MORE_DATA;
+
+done:
+ if ( !W_ERROR_IS_OK(status) )
+ {
+ DEBUG(5, ("error: allocating %d\n", *out_size));
+
+ /* reply this param doesn't exist */
+
+ if ( *out_size )
+ {
+ if( (*data=(uint8 *)TALLOC_ZERO(p->mem_ctx, *out_size*sizeof(uint8))) == NULL ) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+ } else {
+ *data = NULL;
+ }
+ }
+
+ if ( printer )
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_setprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_setprinterdataex(pipes_struct *p, SPOOL_Q_SETPRINTERDATAEX *q_u, SPOOL_R_SETPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 type = q_u->type;
+ uint8 *data = q_u->data;
+ uint32 real_len = q_u->real_len;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ fstring valuename;
+ fstring keyname;
+ char *oid_string;
+
+ 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(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( Printer->printer_type == SPLHND_SERVER ) {
+ DEBUG(10,("_spoolss_setprinterdataex: Not implemented for server handles yet\n"));
+ return WERR_INVALID_PARAM;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum, NULL) )
+ return WERR_BADFID;
+
+ /*
+ * Access check : NT returns "access denied" if you make a
+ * SetPrinterData call without the necessary privildge.
+ * 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;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ unistr2_to_ascii( valuename, &q_u->value, sizeof(valuename));
+ unistr2_to_ascii( keyname, &q_u->key, sizeof(keyname));
+
+ /* check for OID in valuename */
+
+ if ( (oid_string = strchr( valuename, ',' )) != NULL )
+ {
+ *oid_string = '\0';
+ oid_string++;
+ }
+
+ /* save the registry data */
+
+ status = set_printer_dataex( printer, keyname, valuename, type, data, real_len );
+
+ if ( W_ERROR_IS_OK(status) )
+ {
+ /* save the OID if one was specified */
+ if ( oid_string ) {
+ fstrcat( keyname, "\\" );
+ fstrcat( keyname, SPOOL_OID_KEY );
+
+ /*
+ * 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
+ */
+
+ set_printer_dataex( printer, keyname, valuename,
+ REG_SZ, (uint8 *)oid_string,
+ strlen(oid_string)+1 );
+ }
+
+ status = mod_a_printer(printer, 2);
+ }
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+
+/********************************************************************
+ * spoolss_deleteprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdataex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATAEX *q_u, SPOOL_R_DELETEPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ UNISTR2 *value = &q_u->valuename;
+ UNISTR2 *key = &q_u->keyname;
+
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status = WERR_OK;
+ Printer_entry *Printer=find_printer_index_by_hnd(p, handle);
+ char *valuename = NULL;
+ char *keyname = NULL;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ DEBUG(5,("spoolss_deleteprinterdataex\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterdataex: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ valuename = unistr2_to_ascii_talloc(ctx, value);
+ keyname = unistr2_to_ascii_talloc(ctx, key);
+ if (!valuename || !keyname) {
+ return WERR_NOMEM;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ status = delete_printer_dataex( printer, keyname, valuename );
+
+ if ( W_ERROR_IS_OK(status) )
+ mod_a_printer( printer, 2 );
+
+ free_a_printer(&printer, 2);
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_enumprinterkey
+ ********************************************************************/
+
+
+WERROR _spoolss_enumprinterkey(pipes_struct *p, SPOOL_Q_ENUMPRINTERKEY *q_u, SPOOL_R_ENUMPRINTERKEY *r_u)
+{
+ fstring key;
+ fstring *keynames = NULL;
+ uint16 *enumkeys = NULL;
+ int num_keys;
+ int printerkey_len;
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ NT_PRINTER_DATA *data;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum = 0;
+ WERROR status = WERR_BADFILE;
+
+
+ DEBUG(4,("_spoolss_enumprinterkey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ if ( !get_printer_snum(p,handle, &snum, NULL) )
+ return WERR_BADFID;
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ /* get the list of subkey names */
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key));
+ data = printer->info_2->data;
+
+ num_keys = get_printer_subkeys( data, key, &keynames );
+
+ if ( num_keys == -1 ) {
+ status = WERR_BADFILE;
+ goto done;
+ }
+
+ printerkey_len = init_unistr_array( &enumkeys, keynames, NULL );
+
+ r_u->needed = printerkey_len*2;
+
+ if ( q_u->size < r_u->needed ) {
+ status = WERR_MORE_DATA;
+ goto done;
+ }
+
+ if (!make_spoolss_buffer5(p->mem_ctx, &r_u->keys, printerkey_len, enumkeys)) {
+ status = WERR_NOMEM;
+ goto done;
+ }
+
+ status = WERR_OK;
+
+ if ( q_u->size < r_u->needed )
+ status = WERR_MORE_DATA;
+
+done:
+ free_a_printer( &printer, 2 );
+ SAFE_FREE( keynames );
+
+ return status;
+}
+
+/********************************************************************
+ * spoolss_deleteprinterkey
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterkey(pipes_struct *p, SPOOL_Q_DELETEPRINTERKEY *q_u, SPOOL_R_DELETEPRINTERKEY *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, &q_u->handle);
+ fstring key;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ int snum=0;
+ WERROR status;
+
+ DEBUG(5,("spoolss_deleteprinterkey\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_deleteprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /* if keyname == NULL, return error */
+
+ if ( !q_u->keyname.buffer )
+ return WERR_INVALID_PARAM;
+
+ if (!get_printer_snum(p, handle, &snum, NULL))
+ return WERR_BADFID;
+
+ if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) {
+ DEBUG(3, ("_spoolss_deleteprinterkey: printer properties change denied by handle\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ status = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(status))
+ return status;
+
+ /* delete the key and all subneys */
+
+ unistr2_to_ascii(key, &q_u->keyname, sizeof(key));
+
+ status = delete_all_printer_data( printer->info_2, key );
+
+ if ( W_ERROR_IS_OK(status) )
+ status = mod_a_printer(printer, 2);
+
+ free_a_printer( &printer, 2 );
+
+ return status;
+}
+
+
+/********************************************************************
+ * spoolss_enumprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_enumprinterdataex(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATAEX *q_u, SPOOL_R_ENUMPRINTERDATAEX *r_u)
+{
+ POLICY_HND *handle = &q_u->handle;
+ uint32 in_size = q_u->size;
+ uint32 num_entries,
+ needed;
+ NT_PRINTER_INFO_LEVEL *printer = NULL;
+ PRINTER_ENUM_VALUES *enum_values = NULL;
+ NT_PRINTER_DATA *p_data;
+ fstring key;
+ Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
+ int snum;
+ WERROR result;
+ int key_index;
+ int i;
+ REGISTRY_VALUE *val;
+ char *value_name;
+ uint32 data_len;
+
+
+ DEBUG(4,("_spoolss_enumprinterdataex\n"));
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_enumprinterdataex: Invalid handle (%s:%u:%u1<).\n", OUR_HANDLE(handle)));
+ return WERR_BADFID;
+ }
+
+ /*
+ * first check for a keyname of NULL or "". Win2k seems to send
+ * this a lot and we should send back WERR_INVALID_PARAM
+ * no need to spend time looking up the printer in this case.
+ * --jerry
+ */
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key));
+ if ( !strlen(key) ) {
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ /* get the printer off of disk */
+
+ if (!get_printer_snum(p,handle, &snum, NULL))
+ return WERR_BADFID;
+
+ ZERO_STRUCT(printer);
+ result = get_a_printer(Printer, &printer, 2, lp_const_servicename(snum));
+ if (!W_ERROR_IS_OK(result))
+ return result;
+
+ /* now look for a match on the key name */
+
+ p_data = printer->info_2->data;
+
+ unistr2_to_ascii(key, &q_u->key, sizeof(key));
+ if ( (key_index = lookup_printerkey( p_data, key)) == -1 )
+ {
+ DEBUG(10,("_spoolss_enumprinterdataex: Unknown keyname [%s]\n", key));
+ result = WERR_INVALID_PARAM;
+ goto done;
+ }
+
+ result = WERR_OK;
+ needed = 0;
+
+ /* allocate the memory for the array of pointers -- if necessary */
+
+ num_entries = regval_ctr_numvals( p_data->keys[key_index].values );
+ if ( num_entries )
+ {
+ if ( (enum_values=TALLOC_ARRAY(p->mem_ctx, PRINTER_ENUM_VALUES, num_entries)) == NULL )
+ {
+ DEBUG(0,("_spoolss_enumprinterdataex: talloc() failed to allocate memory for [%lu] bytes!\n",
+ (unsigned long)num_entries*sizeof(PRINTER_ENUM_VALUES)));
+ result = WERR_NOMEM;
+ goto done;
+ }
+
+ memset( enum_values, 0x0, num_entries*sizeof(PRINTER_ENUM_VALUES) );
+ }
+
+ /*
+ * loop through all params and build the array to pass
+ * back to the client
+ */
+
+ for ( i=0; i<num_entries; i++ )
+ {
+ /* lookup the registry value */
+
+ val = regval_ctr_specific_value( p_data->keys[key_index].values, i );
+ DEBUG(10,("retrieved value number [%d] [%s]\n", i, regval_name(val) ));
+
+ /* copy the data */
+
+ value_name = regval_name( val );
+ init_unistr( &enum_values[i].valuename, value_name );
+ enum_values[i].value_len = (strlen(value_name)+1) * 2;
+ enum_values[i].type = regval_type( val );
+
+ data_len = regval_size( val );
+ if ( data_len ) {
+ if ( !(enum_values[i].data = (uint8 *)TALLOC_MEMDUP(p->mem_ctx, regval_data_p(val), data_len)) )
+ {
+ DEBUG(0,("TALLOC_MEMDUP failed to allocate memory [data_len=%d] for data!\n",
+ data_len ));
+ result = WERR_NOMEM;
+ goto done;
+ }
+ }
+ enum_values[i].data_len = data_len;
+
+ /* keep track of the size of the array in bytes */
+
+ needed += spoolss_size_printer_enum_values(&enum_values[i]);
+ }
+
+ /* housekeeping information in the reply */
+
+ /* Fix from Martin Zielinski <mz@seh.de> - ensure
+ * the hand marshalled container size is a multiple
+ * of 4 bytes for RPC alignment.
+ */
+
+ if (needed % 4) {
+ needed += 4-(needed % 4);
+ }
+
+ r_u->needed = needed;
+ r_u->returned = num_entries;
+
+ if (needed > in_size) {
+ result = WERR_MORE_DATA;
+ goto done;
+ }
+
+ /* copy data into the reply */
+
+ /* mz: Vista x64 returns 0x6f7 (The stub received bad data), if the
+ response buffer size is != the offered buffer size
+
+ r_u->ctr.size = r_u->needed;
+ */
+ r_u->ctr.size = in_size;
+
+ r_u->ctr.size_of_array = r_u->returned;
+ r_u->ctr.values = enum_values;
+
+done:
+ if ( printer )
+ free_a_printer(&printer, 2);
+
+ return result;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void fill_printprocessordirectory_1(PRINTPROCESSOR_DIRECTORY_1 *info, const char *name)
+{
+ init_unistr(&info->name, name);
+}
+
+static WERROR getprintprocessordirectory_level_1(UNISTR2 *name,
+ UNISTR2 *environment,
+ RPC_BUFFER *buffer,
+ uint32 offered,
+ uint32 *needed)
+{
+ char *long_archi = NULL;
+ PRINTPROCESSOR_DIRECTORY_1 *info=NULL;
+ WERROR result = WERR_OK;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ long_archi = unistr2_to_ascii_talloc(ctx, environment);
+ if (!long_archi) {
+ return WERR_NOMEM;
+ }
+
+ if (!get_short_archi(long_archi))
+ return WERR_INVALID_ENVIRONMENT;
+
+ if((info=SMB_MALLOC_P(PRINTPROCESSOR_DIRECTORY_1)) == NULL)
+ return WERR_NOMEM;
+
+ fill_printprocessordirectory_1(info, "C:\\WINNT\\System32\\spool\\PRTPROCS\\W32X86");
+
+ *needed += spoolss_size_printprocessordirectory_info_1(info);
+
+ if (*needed > offered) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ if (!rpcbuf_alloc_size(buffer, *needed)) {
+ result = WERR_INSUFFICIENT_BUFFER;
+ goto out;
+ }
+
+ smb_io_printprocessordirectory_1("", buffer, info, 0);
+
+out:
+ SAFE_FREE(info);
+
+ return result;
+}
+
+WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u)
+{
+ uint32 level = q_u->level;
+ RPC_BUFFER *buffer = NULL;
+ uint32 offered = q_u->offered;
+ uint32 *needed = &r_u->needed;
+ WERROR result;
+
+ /* that's an [in out] buffer */
+
+ if (!q_u->buffer && (offered!=0)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ rpcbuf_move(q_u->buffer, &r_u->buffer);
+ buffer = r_u->buffer;
+
+ DEBUG(5,("_spoolss_getprintprocessordirectory\n"));
+
+ *needed=0;
+
+ switch(level) {
+ case 1:
+ result = getprintprocessordirectory_level_1
+ (&q_u->name, &q_u->environment, buffer, offered, needed);
+ break;
+ default:
+ result = WERR_UNKNOWN_LEVEL;
+ }
+
+ return result;
+}
+
+/*******************************************************************
+ Streams the monitor UI DLL name in UNICODE
+*******************************************************************/
+
+static WERROR xcvtcp_monitorui( NT_USER_TOKEN *token, RPC_BUFFER *in,
+ RPC_BUFFER *out, uint32 *needed )
+{
+ const char *dllname = "tcpmonui.dll";
+
+ *needed = (strlen(dllname)+1) * 2;
+
+ if ( rpcbuf_get_size(out) < *needed ) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if ( !make_monitorui_buf( out, dllname ) ) {
+ return WERR_NOMEM;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Create a new TCP/IP port
+*******************************************************************/
+
+static WERROR xcvtcp_addport( NT_USER_TOKEN *token, RPC_BUFFER *in,
+ RPC_BUFFER *out, uint32 *needed )
+{
+ NT_PORT_DATA_1 port1;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *device_uri = NULL;
+
+ ZERO_STRUCT( port1 );
+
+ /* convert to our internal port data structure */
+
+ if ( !convert_port_data_1( &port1, in ) ) {
+ return WERR_NOMEM;
+ }
+
+ /* create the device URI and call the add_port_hook() */
+
+ switch ( port1.protocol ) {
+ case PORT_PROTOCOL_DIRECT:
+ device_uri = talloc_asprintf(ctx,
+ "socket://%s:%d/", port1.hostaddr, port1.port );
+ break;
+
+ case PORT_PROTOCOL_LPR:
+ device_uri = talloc_asprintf(ctx,
+ "lpr://%s/%s", port1.hostaddr, port1.queue );
+ break;
+
+ default:
+ return WERR_UNKNOWN_PORT;
+ }
+
+ if (!device_uri) {
+ return WERR_NOMEM;
+ }
+
+ return add_port_hook(ctx, token, port1.name, device_uri );
+}
+
+/*******************************************************************
+*******************************************************************/
+
+struct xcv_api_table xcvtcp_cmds[] = {
+ { "MonitorUI", xcvtcp_monitorui },
+ { "AddPort", xcvtcp_addport},
+ { NULL, NULL }
+};
+
+static WERROR process_xcvtcp_command( NT_USER_TOKEN *token, const char *command,
+ RPC_BUFFER *inbuf, RPC_BUFFER *outbuf,
+ uint32 *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( token, inbuf, outbuf, needed );
+ }
+
+ return WERR_BADFUNC;
+}
+
+/*******************************************************************
+*******************************************************************/
+#if 0 /* don't support management using the "Local Port" monitor */
+
+static WERROR xcvlocal_monitorui( NT_USER_TOKEN *token, RPC_BUFFER *in,
+ RPC_BUFFER *out, uint32 *needed )
+{
+ const char *dllname = "localui.dll";
+
+ *needed = (strlen(dllname)+1) * 2;
+
+ if ( rpcbuf_get_size(out) < *needed ) {
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ if ( !make_monitorui_buf( out, dllname )) {
+ return WERR_NOMEM;
+ }
+
+ 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( NT_USER_TOKEN *token, const char *command,
+ RPC_BUFFER *inbuf, RPC_BUFFER *outbuf,
+ uint32 *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( token, inbuf, outbuf , needed );
+ }
+ return WERR_BADFUNC;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+WERROR _spoolss_xcvdataport(pipes_struct *p, SPOOL_Q_XCVDATAPORT *q_u, SPOOL_R_XCVDATAPORT *r_u)
+{
+ Printer_entry *Printer = find_printer_index_by_hnd(p, &q_u->handle);
+ fstring command;
+
+ if (!Printer) {
+ DEBUG(2,("_spoolss_xcvdataport: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(&q_u->handle)));
+ return WERR_BADFID;
+ }
+
+ /* Has to be a handle to the TCP/IP port monitor */
+
+ if ( !(Printer->printer_type & (SPLHND_PORTMON_LOCAL|SPLHND_PORTMON_TCP)) ) {
+ DEBUG(2,("_spoolss_xcvdataport: Call only valid for Port Monitors\n"));
+ return WERR_BADFID;
+ }
+
+ /* requires administrative access to the server */
+
+ if ( !(Printer->access_granted & SERVER_ACCESS_ADMINISTER) ) {
+ DEBUG(2,("_spoolss_xcvdataport: denied by handle permissions.\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Get the command name. There's numerous commands supported by the
+ TCPMON interface. */
+
+ rpcstr_pull(command, q_u->dataname.buffer, sizeof(command),
+ q_u->dataname.uni_str_len*2, 0);
+
+ /* Allocate the outgoing buffer */
+
+ rpcbuf_init( &r_u->outdata, q_u->offered, p->mem_ctx );
+
+ switch ( Printer->printer_type ) {
+ case SPLHND_PORTMON_TCP:
+ return process_xcvtcp_command( p->pipe_user.nt_user_token, command,
+ &q_u->indata, &r_u->outdata, &r_u->needed );
+ case SPLHND_PORTMON_LOCAL:
+ return process_xcvlocal_command( p->pipe_user.nt_user_token, command,
+ &q_u->indata, &r_u->outdata, &r_u->needed );
+ }
+
+ return WERR_INVALID_PRINT_MONITOR;
+}
diff --git a/source3/rpc_server/srv_srvsvc_nt.c b/source3/rpc_server/srv_srvsvc_nt.c
new file mode 100644
index 0000000000..bb9c3687fb
--- /dev/null
+++ b/source3/rpc_server/srv_srvsvc_nt.c
@@ -0,0 +1,2624 @@
+/*
+ * 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"
+
+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 sess_file_count {
+ struct server_id pid;
+ uid_t uid;
+ int count;
+};
+
+/****************************************************************************
+ Count the entries belonging to a service in the connection db.
+****************************************************************************/
+
+static int pipe_enum_fn( struct db_record *rec, void *p)
+{
+ struct pipe_open_rec prec;
+ struct file_enum_count *fenum = (struct file_enum_count *)p;
+ struct srvsvc_NetFileInfo3 *f;
+ int i = fenum->ctr3->count;
+ char *fullpath = NULL;
+ const char *username;
+
+ if (rec->value.dsize != sizeof(struct pipe_open_rec))
+ return 0;
+
+ memcpy(&prec, rec->value.dptr, sizeof(struct pipe_open_rec));
+
+ if ( !process_exists(prec.pid) ) {
+ return 0;
+ }
+
+ username = uidtoname(prec.uid);
+
+ if ((fenum->username != NULL)
+ && !strequal(username, fenum->username)) {
+ return 0;
+ }
+
+ fullpath = talloc_asprintf(fenum->ctx, "\\PIPE\\%s", prec.name );
+ if (!fullpath) {
+ return 1;
+ }
+
+ f = TALLOC_REALLOC_ARRAY(fenum->ctx, fenum->ctr3->array,
+ struct srvsvc_NetFileInfo3, i+1);
+ if ( !f ) {
+ DEBUG(0,("conn_enum_fn: realloc failed for %d items\n", i+1));
+ return 1;
+ }
+ fenum->ctr3->array = f;
+
+ init_srvsvc_NetFileInfo3(&fenum->ctr3->array[i],
+ (((uint32_t)(procid_to_pid(&prec.pid))<<16) | prec.pnum),
+ (FILE_READ_DATA|FILE_WRITE_DATA),
+ 0,
+ fullpath,
+ username);
+
+ fenum->ctr3->count++;
+
+ return 0;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static WERROR net_enum_pipes(TALLOC_CTX *ctx,
+ const char *username,
+ struct srvsvc_NetFileCtr3 **ctr3,
+ uint32_t resume )
+{
+ struct file_enum_count fenum;
+
+ fenum.ctx = ctx;
+ fenum.username = username;
+ fenum.ctr3 = *ctr3;
+
+ if (connections_traverse(pipe_enum_fn, &fenum) == -1) {
+ DEBUG(0,("net_enum_pipes: traverse of connections.tdb "
+ "failed\n"));
+ return WERR_NOMEM;
+ }
+
+ *ctr3 = fenum.ctr3;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static void enum_file_fn( const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *private_data )
+{
+ struct file_enum_count *fenum =
+ (struct file_enum_count *)private_data;
+
+ struct srvsvc_NetFileInfo3 *f;
+ int i = fenum->ctr3->count;
+ files_struct fsp;
+ struct byte_range_lock *brl;
+ int num_locks = 0;
+ char *fullpath = NULL;
+ uint32 permissions;
+ const char *username;
+
+ /* If the pid was not found delete the entry from connections.tdb */
+
+ if ( !process_exists(e->pid) ) {
+ return;
+ }
+
+ username = uidtoname(e->uid);
+
+ if ((fenum->username != NULL)
+ && !strequal(username, fenum->username)) {
+ return;
+ }
+
+ f = TALLOC_REALLOC_ARRAY(fenum->ctx, fenum->ctr3->array,
+ struct srvsvc_NetFileInfo3, i+1);
+ if ( !f ) {
+ DEBUG(0,("conn_enum_fn: realloc failed for %d items\n", i+1));
+ return;
+ }
+ fenum->ctr3->array = f;
+
+ /* need to count the number of locks on a file */
+
+ ZERO_STRUCT( fsp );
+ fsp.file_id = e->id;
+
+ if ( (brl = brl_get_locks(talloc_tos(), &fsp)) != NULL ) {
+ num_locks = brl->num_locks;
+ TALLOC_FREE(brl);
+ }
+
+ if ( strcmp( fname, "." ) == 0 ) {
+ fullpath = talloc_asprintf(fenum->ctx, "C:%s", sharepath );
+ } else {
+ fullpath = talloc_asprintf(fenum->ctx, "C:%s/%s",
+ sharepath, fname );
+ }
+ if (!fullpath) {
+ return;
+ }
+ 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 */
+ init_srvsvc_NetFileInfo3(&fenum->ctr3->array[i],
+ (((uint32_t)(procid_to_pid(&e->pid))<<16) | e->share_file_id),
+ permissions,
+ num_locks,
+ fullpath,
+ username);
+ fenum->ctr3->count++;
+}
+
+/*******************************************************************
+********************************************************************/
+
+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;
+
+ f_enum_cnt.ctx = ctx;
+ f_enum_cnt.username = username;
+ f_enum_cnt.ctr3 = *ctr3;
+
+ share_mode_forall( enum_file_fn, (void *)&f_enum_cnt );
+
+ *ctr3 = f_enum_cnt.ctr3;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Utility function to get the 'type' of a share from an snum.
+ ********************************************************************/
+static uint32 get_share_type(int snum)
+{
+ /* work out the share type */
+ uint32 type = STYPE_DISKTREE;
+
+ if (lp_print_ok(snum))
+ type = STYPE_PRINTQ;
+ if (strequal(lp_fstype(snum), "IPC"))
+ type = STYPE_IPC;
+ if (lp_administrative_share(snum))
+ type |= STYPE_HIDDEN;
+
+ return type;
+}
+
+/*******************************************************************
+ Fill in a share info level 0 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_0(pipes_struct *p, struct srvsvc_NetShareInfo0 *r, int snum)
+{
+ const char *net_name = lp_servicename(snum);
+
+ init_srvsvc_NetShareInfo0(r, net_name);
+}
+
+/*******************************************************************
+ Fill in a share info level 1 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1(pipes_struct *p, struct srvsvc_NetShareInfo1 *r, int snum)
+{
+ char *net_name = lp_servicename(snum);
+ char *remark = talloc_strdup(p->mem_ctx, lp_comment(snum));
+
+ if (remark) {
+ remark = talloc_sub_advanced(
+ p->mem_ctx, lp_servicename(snum),
+ get_current_username(), lp_pathname(snum),
+ p->pipe_user.ut.uid, get_current_username(),
+ "", remark);
+ }
+
+ init_srvsvc_NetShareInfo1(r, net_name,
+ get_share_type(snum),
+ remark ? remark : "");
+}
+
+/*******************************************************************
+ Fill in a share info level 2 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_2(pipes_struct *p, struct srvsvc_NetShareInfo2 *r, int snum)
+{
+ char *remark = NULL;
+ char *path = NULL;
+ int max_connections = lp_max_connections(snum);
+ uint32_t max_uses = max_connections!=0 ? max_connections : (uint32_t)-1;
+ int count = 0;
+ char *net_name = lp_servicename(snum);
+
+ remark = talloc_strdup(p->mem_ctx, lp_comment(snum));
+ if (remark) {
+ remark = talloc_sub_advanced(
+ p->mem_ctx, lp_servicename(snum),
+ get_current_username(), lp_pathname(snum),
+ p->pipe_user.ut.uid, get_current_username(),
+ "", remark);
+ }
+ path = talloc_asprintf(p->mem_ctx,
+ "C:%s", lp_pathname(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, '/', '\\');
+ }
+
+ count = count_current_connections(net_name, false);
+
+ init_srvsvc_NetShareInfo2(r, net_name,
+ get_share_type(snum),
+ remark ? remark : "",
+ 0,
+ max_uses,
+ count,
+ path ? path : "",
+ "");
+}
+
+/*******************************************************************
+ Map any generic bits to file specific bits.
+********************************************************************/
+
+static void map_generic_share_sd_bits(SEC_DESC *psd)
+{
+ int i;
+ SEC_ACL *ps_dacl = NULL;
+
+ if (!psd)
+ return;
+
+ ps_dacl = psd->dacl;
+ if (!ps_dacl)
+ return;
+
+ for (i = 0; i < ps_dacl->num_aces; i++) {
+ SEC_ACE *psa = &ps_dacl->aces[i];
+ uint32 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(pipes_struct *p, struct srvsvc_NetShareInfo501 *r, int snum)
+{
+ const char *net_name = lp_servicename(snum);
+ char *remark = talloc_strdup(p->mem_ctx, lp_comment(snum));
+
+ if (remark) {
+ remark = talloc_sub_advanced(
+ p->mem_ctx, lp_servicename(snum),
+ get_current_username(), lp_pathname(snum),
+ p->pipe_user.ut.uid, get_current_username(),
+ "", remark);
+ }
+
+ init_srvsvc_NetShareInfo501(r, net_name,
+ get_share_type(snum),
+ remark ? remark : "",
+ (lp_csc_policy(snum) << 4));
+}
+
+/*******************************************************************
+ Fill in a share info level 502 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_502(pipes_struct *p, struct srvsvc_NetShareInfo502 *r, int snum)
+{
+ const char *net_name = lp_servicename(snum);
+ char *path = NULL;
+ SEC_DESC *sd = NULL;
+ struct sec_desc_buf *sd_buf = NULL;
+ size_t sd_size = 0;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ char *remark = talloc_strdup(ctx, lp_comment(snum));;
+
+ if (remark) {
+ remark = talloc_sub_advanced(
+ p->mem_ctx, lp_servicename(snum),
+ get_current_username(), lp_pathname(snum),
+ p->pipe_user.ut.uid, get_current_username(),
+ "", remark);
+ }
+ path = talloc_asprintf(ctx, "C:%s", lp_pathname(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(snum), &sd_size);
+
+ sd_buf = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+
+ init_srvsvc_NetShareInfo502(r, net_name,
+ get_share_type(snum),
+ remark ? remark : "",
+ 0,
+ (uint32_t)-1,
+ 1,
+ path ? path : "",
+ "",
+ sd_buf);
+}
+
+/***************************************************************************
+ Fill in a share info level 1004 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1004(pipes_struct *p, struct srvsvc_NetShareInfo1004 *r, int snum)
+{
+ char *remark = talloc_strdup(p->mem_ctx, lp_comment(snum));
+
+ if (remark) {
+ remark = talloc_sub_advanced(
+ p->mem_ctx, lp_servicename(snum),
+ get_current_username(), lp_pathname(snum),
+ p->pipe_user.ut.uid, get_current_username(),
+ "", remark);
+ }
+
+ init_srvsvc_NetShareInfo1004(r, remark ? remark : "");
+}
+
+/***************************************************************************
+ Fill in a share info level 1005 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1005(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;
+
+ init_srvsvc_NetShareInfo1005(r, dfs_flags);
+}
+
+/***************************************************************************
+ Fill in a share info level 1006 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1006(pipes_struct *p, struct srvsvc_NetShareInfo1006 *r, int snum)
+{
+ init_srvsvc_NetShareInfo1006(r, (uint32_t)-1);
+}
+
+/***************************************************************************
+ Fill in a share info level 1007 structure.
+ ***************************************************************************/
+
+static void init_srv_share_info_1007(pipes_struct *p, struct srvsvc_NetShareInfo1007 *r, int snum)
+{
+ uint32 flags = 0;
+
+ init_srvsvc_NetShareInfo1007(r, flags, "");
+}
+
+/*******************************************************************
+ Fill in a share info level 1501 structure.
+ ********************************************************************/
+
+static void init_srv_share_info_1501(pipes_struct *p, struct sec_desc_buf *r, int snum)
+{
+ SEC_DESC *sd;
+ size_t sd_size;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ sd = get_share_security(ctx, lp_servicename(snum), &sd_size);
+
+ r = make_sec_desc_buf(p->mem_ctx, sd_size, sd);
+}
+
+/*******************************************************************
+ True if it ends in '$'.
+ ********************************************************************/
+
+static bool is_hidden_share(int snum)
+{
+ const char *net_name = lp_servicename(snum);
+
+ return (net_name[strlen(net_name) - 1] == '$') ? True : False;
+}
+
+/*******************************************************************
+ Fill in a share info structure.
+ ********************************************************************/
+
+static WERROR init_srv_share_info_ctr(pipes_struct *p,
+ struct srvsvc_NetShareInfoCtr *info_ctr,
+ uint32_t *resume_handle_p,
+ uint32_t *total_entries,
+ bool all_shares)
+{
+ int num_entries = 0;
+ int alloc_entries = 0;
+ int num_services = 0;
+ int snum;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ int i = 0;
+ int valid_share_count = 0;
+ union srvsvc_NetShareCtr ctr;
+ uint32_t resume_handle = resume_handle_p ? *resume_handle_p : 0;
+
+ DEBUG(5,("init_srv_share_info_ctr\n"));
+
+ /* Ensure all the usershares are loaded. */
+ become_root();
+ load_usershare_shares();
+ load_registry_shares();
+ num_services = lp_numservices();
+ unbecome_root();
+
+ /* Count the number of entries. */
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) ) {
+ DEBUG(10, ("counting service %s\n", lp_servicename(snum)));
+ num_entries++;
+ } else {
+ DEBUG(10, ("NOT counting service %s\n", lp_servicename(snum)));
+ }
+ }
+
+ if (!num_entries || (resume_handle >= num_entries)) {
+ return WERR_OK;
+ }
+
+ /* Calculate alloc entries. */
+ alloc_entries = num_entries - resume_handle;
+ switch (info_ctr->level) {
+ case 0:
+ ctr.ctr0 = TALLOC_ZERO_P(ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr0);
+
+ ctr.ctr0->count = alloc_entries;
+ ctr.ctr0->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo0, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr0->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1);
+
+ ctr.ctr1->count = alloc_entries;
+ ctr.ctr1->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo1, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr2);
+
+ ctr.ctr2->count = alloc_entries;
+ ctr.ctr2->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo2, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr2->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_2(p, &ctr.ctr2->array[i++], snum);
+ }
+ }
+
+ break;
+
+ case 501:
+ ctr.ctr501 = TALLOC_ZERO_P(ctx, struct srvsvc_NetShareCtr501);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr501);
+
+ ctr.ctr501->count = alloc_entries;
+ ctr.ctr501->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo501, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr501->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr502);
+
+ ctr.ctr502->count = alloc_entries;
+ ctr.ctr502->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo502, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr502->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr1004);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1004);
+
+ ctr.ctr1004->count = alloc_entries;
+ ctr.ctr1004->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo1004, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1004->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr1005);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1005);
+
+ ctr.ctr1005->count = alloc_entries;
+ ctr.ctr1005->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo1005, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1005->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr1006);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1006);
+
+ ctr.ctr1006->count = alloc_entries;
+ ctr.ctr1006->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo1006, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1006->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr1007);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1007);
+
+ ctr.ctr1007->count = alloc_entries;
+ ctr.ctr1007->array = TALLOC_ZERO_ARRAY(ctx, struct srvsvc_NetShareInfo1007, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1007->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(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_P(ctx, struct srvsvc_NetShareCtr1501);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1501);
+
+ ctr.ctr1501->count = alloc_entries;
+ ctr.ctr1501->array = TALLOC_ZERO_ARRAY(ctx, struct sec_desc_buf, alloc_entries);
+ W_ERROR_HAVE_NO_MEMORY(ctr.ctr1501->array);
+
+ for (snum = 0; snum < num_services; snum++) {
+ if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_hidden_share(snum)) &&
+ (resume_handle <= (i + valid_share_count++)) ) {
+ init_srv_share_info_1501(p, &ctr.ctr1501->array[i++], snum);
+ }
+ }
+
+ break;
+
+ default:
+ DEBUG(5,("init_srv_share_info_ctr: unsupported switch value %d\n",
+ info_ctr->level));
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *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;
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ fill in a sess info level 0 structure.
+ ********************************************************************/
+
+static WERROR init_srv_sess_info_0(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_ARRAY(p->mem_ctx,
+ ctr0->array,
+ struct srvsvc_NetSessInfo0,
+ num_entries+1);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ init_srvsvc_NetSessInfo0(&ctr0->array[num_entries],
+ 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;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static void sess_file_fn( const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *data )
+{
+ struct sess_file_count *sess = (struct sess_file_count *)data;
+
+ if ( procid_equal(&e->pid, &sess->pid) && (sess->uid == e->uid) ) {
+ sess->count++;
+ }
+
+ return;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static int net_count_files( uid_t uid, struct server_id pid )
+{
+ struct sess_file_count s_file_cnt;
+
+ s_file_cnt.count = 0;
+ s_file_cnt.uid = uid;
+ s_file_cnt.pid = pid;
+
+ share_mode_forall( sess_file_fn, &s_file_cnt );
+
+ return s_file_cnt.count;
+}
+
+/*******************************************************************
+ fill in a sess info level 1 structure.
+ ********************************************************************/
+
+static WERROR init_srv_sess_info_1(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);
+
+ for (; resume_handle < *total_entries; resume_handle++) {
+ uint32 num_files;
+ uint32 connect_time;
+ struct passwd *pw = sys_getpwnam(session_list[resume_handle].username);
+ bool guest;
+
+ if ( !pw ) {
+ DEBUG(10,("init_srv_sess_info_1: failed to find owner: %s\n",
+ session_list[resume_handle].username));
+ continue;
+ }
+
+ connect_time = (uint32_t)(now - session_list[resume_handle].connect_start);
+ num_files = net_count_files(pw->pw_uid, session_list[resume_handle].pid);
+ guest = strequal( session_list[resume_handle].username, lp_guestaccount() );
+
+ ctr1->array = TALLOC_REALLOC_ARRAY(p->mem_ctx,
+ ctr1->array,
+ struct srvsvc_NetSessInfo1,
+ num_entries+1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ init_srvsvc_NetSessInfo1(&ctr1->array[num_entries],
+ session_list[resume_handle].remote_machine,
+ session_list[resume_handle].username,
+ num_files,
+ connect_time,
+ 0,
+ guest);
+ num_entries++;
+ }
+
+ ctr1->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;
+}
+
+/*******************************************************************
+ 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_ARRAY(talloc_tos(),
+ ctr0->array,
+ struct srvsvc_NetConnInfo0,
+ num_entries+1);
+ if (!ctr0->array) {
+ return WERR_NOMEM;
+ }
+
+ init_srvsvc_NetConnInfo0(&ctr0->array[num_entries],
+ (*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(struct srvsvc_NetConnCtr1 *ctr1,
+ 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_1\n"));
+
+ if (ctr1 == NULL) {
+ if (resume_handle_p) {
+ *resume_handle_p = 0;
+ }
+ return WERR_OK;
+ }
+
+ *total_entries = 1;
+
+ ZERO_STRUCTP(ctr1);
+
+ for (; resume_handle < *total_entries; resume_handle++) {
+
+ ctr1->array = TALLOC_REALLOC_ARRAY(talloc_tos(),
+ ctr1->array,
+ struct srvsvc_NetConnInfo1,
+ num_entries+1);
+ if (!ctr1->array) {
+ return WERR_NOMEM;
+ }
+
+ init_srvsvc_NetConnInfo1(&ctr1->array[num_entries],
+ (*total_entries),
+ 0x3,
+ 1,
+ 1,
+ 3,
+ "dummy_user",
+ "IPC$");
+
+ /* move on to creating next connection */
+ num_entries++;
+ }
+
+ ctr1->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;
+}
+
+/*******************************************************************
+ _srvsvc_NetFileEnum
+*******************************************************************/
+
+WERROR _srvsvc_NetFileEnum(pipes_struct *p,
+ struct srvsvc_NetFileEnum *r)
+{
+ 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_UNKNOWN_LEVEL;
+ }
+
+ ctx = talloc_tos();
+ ctr3 = r->in.info_ctr->ctr.ctr3;
+ if (!ctr3) {
+ werr = WERR_INVALID_PARAM;
+ 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;
+ }
+
+ werr = net_enum_pipes(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(pipes_struct *p,
+ struct srvsvc_NetSrvGetInfo *r)
+{
+ 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(p->mem_ctx, struct srvsvc_NetSrvInfo102);
+ if (!info102) {
+ return WERR_NOMEM;
+ }
+
+ init_srvsvc_NetSrvInfo102(info102,
+ PLATFORM_ID_NT,
+ global_myname(),
+ lp_major_announce_version(),
+ lp_minor_announce_version(),
+ lp_default_server_announce(),
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH),
+ 0xffffffff, /* users */
+ 0xf, /* disc */
+ 0, /* hidden */
+ 240, /* announce */
+ 3000, /* announce delta */
+ 100000, /* licenses */
+ "c:\\"); /* user path */
+ r->out.info->info102 = info102;
+ break;
+ }
+ case 101: {
+ struct srvsvc_NetSrvInfo101 *info101;
+
+ info101 = TALLOC_P(p->mem_ctx, struct srvsvc_NetSrvInfo101);
+ if (!info101) {
+ return WERR_NOMEM;
+ }
+
+ init_srvsvc_NetSrvInfo101(info101,
+ PLATFORM_ID_NT,
+ global_myname(),
+ lp_major_announce_version(),
+ lp_minor_announce_version(),
+ lp_default_server_announce(),
+ string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH));
+ r->out.info->info101 = info101;
+ break;
+ }
+ case 100: {
+ struct srvsvc_NetSrvInfo100 *info100;
+
+ info100 = TALLOC_P(p->mem_ctx, struct srvsvc_NetSrvInfo100);
+ if (!info100) {
+ return WERR_NOMEM;
+ }
+
+ init_srvsvc_NetSrvInfo100(info100,
+ PLATFORM_ID_NT,
+ global_myname());
+ r->out.info->info100 = info100;
+
+ break;
+ }
+ default:
+ status = WERR_UNKNOWN_LEVEL;
+ break;
+ }
+
+ DEBUG(5,("_srvsvc_NetSrvGetInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ _srvsvc_NetSrvSetInfo
+********************************************************************/
+
+WERROR _srvsvc_NetSrvSetInfo(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(pipes_struct *p,
+ struct srvsvc_NetConnEnum *r)
+{
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetConnEnum: %d\n", __LINE__));
+
+ 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.info_ctr->ctr.ctr1,
+ r->in.resume_handle,
+ r->out.totalentries);
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ DEBUG(5,("_srvsvc_NetConnEnum: %d\n", __LINE__));
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetSessEnum
+********************************************************************/
+
+WERROR _srvsvc_NetSessEnum(pipes_struct *p,
+ struct srvsvc_NetSessEnum *r)
+{
+ WERROR werr;
+
+ DEBUG(5,("_srvsvc_NetSessEnum: %d\n", __LINE__));
+
+ 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_UNKNOWN_LEVEL;
+ }
+
+ DEBUG(5,("_srvsvc_NetSessEnum: %d\n", __LINE__));
+
+ return werr;
+}
+
+/*******************************************************************
+ _srvsvc_NetSessDel
+********************************************************************/
+
+WERROR _srvsvc_NetSessDel(pipes_struct *p,
+ struct srvsvc_NetSessDel *r)
+{
+ struct sessionid *session_list;
+ struct current_user user;
+ int num_sessions, snum;
+ const char *username;
+ const char *machine;
+ bool not_root = False;
+ WERROR werr;
+
+ username = r->in.user;
+ machine = r->in.client;
+
+ /* strip leading backslashes if any */
+ if (machine && machine[0] == '\\' && machine[1] == '\\') {
+ machine += 2;
+ }
+
+ num_sessions = list_sessions(p->mem_ctx, &session_list);
+
+ DEBUG(5,("_srvsvc_NetSessDel: %d\n", __LINE__));
+
+ werr = WERR_ACCESS_DENIED;
+
+ get_current_user(&user, p);
+
+ /* fail out now if you are not root or not a domain admin */
+
+ if ((user.ut.uid != sec_initial_uid()) &&
+ ( ! nt_token_check_domain_rid(p->pipe_user.nt_user_token, DOMAIN_GROUP_RID_ADMINS))) {
+
+ goto done;
+ }
+
+ for (snum = 0; snum < num_sessions; snum++) {
+
+ if ((strequal(session_list[snum].username, username) || username[0] == '\0' ) &&
+ strequal(session_list[snum].remote_machine, machine)) {
+
+ NTSTATUS ntstat;
+
+ if (user.ut.uid != sec_initial_uid()) {
+ not_root = True;
+ become_root();
+ }
+
+ ntstat = messaging_send(smbd_messaging_context(),
+ 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(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(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(pipes_struct *p,
+ struct srvsvc_NetShareGetInfo *r)
+{
+ WERROR status = WERR_OK;
+ fstring share_name;
+ int snum;
+ union srvsvc_NetShareInfo *info = r->out.info;
+
+ DEBUG(5,("_srvsvc_NetShareGetInfo: %d\n", __LINE__));
+
+ fstrcpy(share_name, r->in.share_name);
+
+ snum = find_service(share_name);
+ if (snum < 0) {
+ return WERR_INVALID_NAME;
+ }
+
+ switch (r->in.level) {
+ case 0:
+ info->info0 = TALLOC_P(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(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(p->mem_ctx, struct srvsvc_NetShareInfo2);
+ W_ERROR_HAVE_NO_MEMORY(info->info2);
+ init_srv_share_info_2(p, info->info2, snum);
+ break;
+ case 501:
+ info->info501 = TALLOC_P(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(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(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(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(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(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_UNKNOWN_LEVEL;
+ break;
+ }
+
+ DEBUG(5,("_srvsvc_NetShareGetInfo: %d\n", __LINE__));
+
+ return status;
+}
+
+/*******************************************************************
+ Check a given DOS pathname is valid for a share.
+********************************************************************/
+
+char *valid_share_pathname(TALLOC_CTX *ctx, const char *dos_pathname)
+{
+ char *ptr = NULL;
+
+ if (!dos_pathname) {
+ return NULL;
+ }
+
+ ptr = talloc_strdup(ctx, dos_pathname);
+ if (!ptr) {
+ return NULL;
+ }
+ /* Convert any '\' paths to '/' */
+ unix_format(ptr);
+ ptr = unix_clean_name(ctx, ptr);
+ if (!ptr) {
+ return NULL;
+ }
+
+ /* NT is braindead - it wants a C: prefix to a pathname ! So strip it. */
+ if (strlen(ptr) > 2 && ptr[1] == ':' && ptr[0] != '/')
+ ptr += 2;
+
+ /* Only absolute paths allowed. */
+ if (*ptr != '/')
+ return NULL;
+
+ return ptr;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareSetInfo. Modify share details.
+********************************************************************/
+
+WERROR _srvsvc_NetShareSetInfo(pipes_struct *p,
+ struct srvsvc_NetShareSetInfo *r)
+{
+ struct current_user user;
+ char *command = NULL;
+ char *share_name = NULL;
+ char *comment = NULL;
+ const char *pathname = NULL;
+ int type;
+ int snum;
+ int ret;
+ char *path = NULL;
+ SEC_DESC *psd = NULL;
+ SE_PRIV se_diskop = SE_DISK_OPERATOR;
+ bool is_disk_op = False;
+ int max_connections = 0;
+ TALLOC_CTX *ctx = p->mem_ctx;
+ union srvsvc_NetShareInfo *info = r->in.info;
+
+ DEBUG(5,("_srvsvc_NetShareSetInfo: %d\n", __LINE__));
+
+ share_name = talloc_strdup(p->mem_ctx, r->in.share_name);
+ if (!share_name) {
+ return WERR_NOMEM;
+ }
+
+ if (r->out.parm_error) {
+ *r->out.parm_error = 0;
+ }
+
+ if ( strequal(share_name,"IPC$")
+ || ( lp_enable_asu_support() && strequal(share_name,"ADMIN$") )
+ || strequal(share_name,"global") )
+ {
+ return WERR_ACCESS_DENIED;
+ }
+
+ snum = find_service(share_name);
+
+ /* Does this share exist ? */
+ if (snum < 0)
+ return WERR_NET_NAME_NOT_FOUND;
+
+ /* No change to printer shares. */
+ if (lp_print_ok(snum))
+ return WERR_ACCESS_DENIED;
+
+ get_current_user(&user,p);
+
+ is_disk_op = user_has_privileges( p->pipe_user.nt_user_token, &se_diskop );
+
+ /* fail out now if you are not root and not a disk op */
+
+ if ( user.ut.uid != sec_initial_uid() && !is_disk_op )
+ return WERR_ACCESS_DENIED;
+
+ switch (r->in.level) {
+ case 1:
+ pathname = talloc_strdup(ctx, lp_pathname(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 = talloc_strdup(ctx, lp_pathname(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 contine other ops like setting
+ ACLs on a share */
+ if (((info->info1005->dfs_flags &
+ SHARE_1005_CSC_POLICY_MASK) >>
+ SHARE_1005_CSC_POLICY_SHIFT) == lp_csc_policy(snum))
+ return WERR_OK;
+ else {
+ DEBUG(3, ("_srvsvc_NetShareSetInfo: client is trying to change csc policy from the network; must be done with smb.conf\n"));
+ return WERR_ACCESS_DENIED;
+ }
+ case 1006:
+ case 1007:
+ return WERR_ACCESS_DENIED;
+ case 1501:
+ pathname = talloc_strdup(ctx, lp_pathname(snum));
+ comment = talloc_strdup(ctx, lp_comment(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_UNKNOWN_LEVEL;
+ }
+
+ /* We can only modify disk shares. */
+ if (type != STYPE_DISKTREE)
+ return WERR_ACCESS_DENIED;
+
+ if (comment == NULL) {
+ return WERR_NOMEM;
+ }
+
+ /* Check if the pathname is valid. */
+ if (!(path = valid_share_pathname(p->mem_ctx, pathname )))
+ return WERR_OBJECT_PATH_INVALID;
+
+ /* 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_cmd() ? lp_change_share_cmd() : "NULL" ));
+
+ /* Only call modify function if something changed. */
+
+ if (strcmp(path, lp_pathname(snum)) || strcmp(comment, lp_comment(snum))
+ || (lp_max_connections(snum) != max_connections)) {
+ if (!lp_change_share_cmd() || !*lp_change_share_cmd()) {
+ 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",
+ lp_change_share_cmd(),
+ get_dyn_CONFIGFILE(),
+ share_name,
+ path,
+ comment ? comment : "",
+ max_connections);
+ if (!command) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(10,("_srvsvc_NetShareSetInfo: Running [%s]\n", command ));
+
+ /********* BEGIN SeDiskOperatorPrivilege BLOCK *********/
+
+ if (is_disk_op)
+ become_root();
+
+ if ( (ret = smbrun(command, NULL)) == 0 ) {
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(smbd_messaging_context(),
+ MSG_SMB_CONF_UPDATED, NULL, 0,
+ NULL);
+ }
+
+ 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) {
+ SEC_DESC *old_sd;
+ size_t sd_size;
+
+ old_sd = get_share_security(p->mem_ctx, lp_servicename(snum), &sd_size);
+
+ if (old_sd && !sec_desc_equal(old_sd, psd)) {
+ if (!set_share_security(share_name, psd))
+ 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(pipes_struct *p,
+ struct srvsvc_NetShareAdd *r)
+{
+ struct current_user user;
+ char *command = NULL;
+ char *share_name = NULL;
+ char *comment = NULL;
+ char *pathname = NULL;
+ int type;
+ int snum;
+ int ret;
+ char *path;
+ SEC_DESC *psd = NULL;
+ SE_PRIV se_diskop = SE_DISK_OPERATOR;
+ bool is_disk_op;
+ int max_connections = 0;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ DEBUG(5,("_srvsvc_NetShareAdd: %d\n", __LINE__));
+
+ *r->out.parm_error = 0;
+
+ get_current_user(&user,p);
+
+ is_disk_op = user_has_privileges( p->pipe_user.nt_user_token, &se_diskop );
+
+ if (user.ut.uid != sec_initial_uid() && !is_disk_op )
+ return WERR_ACCESS_DENIED;
+
+ if (!lp_add_share_cmd() || !*lp_add_share_cmd()) {
+ DEBUG(10,("_srvsvc_NetShareAdd: No add share command\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 = 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 = 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_UNKNOWN_LEVEL;
+ }
+
+ /* check for invalid share names */
+
+ if (!share_name || !validate_net_name(share_name,
+ INVALID_SHARENAME_CHARS,
+ strlen(share_name))) {
+ DEBUG(5,("_srvsvc_NetShareAdd: Bad sharename \"%s\"\n",
+ share_name ? share_name : ""));
+ return WERR_INVALID_NAME;
+ }
+
+ if (strequal(share_name,"IPC$") || strequal(share_name,"global")
+ || (lp_enable_asu_support() &&
+ strequal(share_name,"ADMIN$"))) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ snum = find_service(share_name);
+
+ /* Share already exists. */
+ if (snum >= 0) {
+ return WERR_ALREADY_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_OBJECT_PATH_INVALID;
+ }
+
+ /* Ensure share name, pathname and comment don't contain '"' characters. */
+ 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_cmd(),
+ get_dyn_CONFIGFILE(),
+ share_name,
+ path,
+ comment ? comment : "",
+ max_connections);
+ if (!command) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(10,("_srvsvc_NetShareAdd: Running [%s]\n", command ));
+
+ /********* BEGIN SeDiskOperatorPrivilege BLOCK *********/
+
+ if ( is_disk_op )
+ become_root();
+
+ /* FIXME: use libnetconf here - gd */
+
+ if ( (ret = smbrun(command, NULL)) == 0 ) {
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(smbd_messaging_context(),
+ MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+ }
+
+ 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) {
+ if (!set_share_security(share_name, psd)) {
+ 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(pipes_struct *p,
+ struct srvsvc_NetShareDel *r)
+{
+ struct current_user user;
+ char *command = NULL;
+ char *share_name = NULL;
+ int ret;
+ int snum;
+ SE_PRIV se_diskop = SE_DISK_OPERATOR;
+ bool is_disk_op;
+ struct share_params *params;
+ TALLOC_CTX *ctx = p->mem_ctx;
+
+ DEBUG(5,("_srvsvc_NetShareDel: %d\n", __LINE__));
+
+ share_name = talloc_strdup(p->mem_ctx, r->in.share_name);
+ if (!share_name) {
+ return WERR_NET_NAME_NOT_FOUND;
+ }
+ if ( strequal(share_name,"IPC$")
+ || ( lp_enable_asu_support() && strequal(share_name,"ADMIN$") )
+ || strequal(share_name,"global") )
+ {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!(params = get_share_params(p->mem_ctx, share_name))) {
+ return WERR_NO_SUCH_SHARE;
+ }
+
+ snum = find_service(share_name);
+
+ /* No change to printer shares. */
+ if (lp_print_ok(snum))
+ return WERR_ACCESS_DENIED;
+
+ get_current_user(&user,p);
+
+ is_disk_op = user_has_privileges( p->pipe_user.nt_user_token, &se_diskop );
+
+ if (user.ut.uid != sec_initial_uid() && !is_disk_op )
+ return WERR_ACCESS_DENIED;
+
+ if (!lp_delete_share_cmd() || !*lp_delete_share_cmd()) {
+ DEBUG(10,("_srvsvc_NetShareDel: No delete share command\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ command = talloc_asprintf(ctx,
+ "%s \"%s\" \"%s\"",
+ lp_delete_share_cmd(),
+ get_dyn_CONFIGFILE(),
+ lp_servicename(snum));
+ if (!command) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(10,("_srvsvc_NetShareDel: Running [%s]\n", command ));
+
+ /********* BEGIN SeDiskOperatorPrivilege BLOCK *********/
+
+ if ( is_disk_op )
+ become_root();
+
+ if ( (ret = smbrun(command, NULL)) == 0 ) {
+ /* Tell everyone we updated smb.conf. */
+ message_send_all(smbd_messaging_context(),
+ MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+ }
+
+ 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(lp_servicename(params->service));
+
+ lp_killservice(params->service);
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ _srvsvc_NetShareDelSticky
+********************************************************************/
+
+WERROR _srvsvc_NetShareDelSticky(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(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 zone = get_time_zone(unixdate)/60;
+
+ DEBUG(5,("_srvsvc_NetRemoteTOD: %d\n", __LINE__));
+
+ if ( !(tod = TALLOC_ZERO_P(p->mem_ctx, struct srvsvc_NetRemoteTODInfo)) )
+ return WERR_NOMEM;
+
+ *r->out.info = tod;
+
+ DEBUG(5,("_srvsvc_NetRemoteTOD: %d\n", __LINE__));
+
+ t = gmtime(&unixdate);
+
+ /* set up the */
+ init_srvsvc_NetRemoteTODInfo(tod,
+ unixdate,
+ 0,
+ t->tm_hour,
+ t->tm_min,
+ t->tm_sec,
+ 0,
+ zone,
+ 10000,
+ t->tm_mday,
+ t->tm_mon + 1,
+ 1900+t->tm_year,
+ t->tm_wday);
+
+ DEBUG(5,("_srvsvc_NetRemoteTOD: %d\n", __LINE__));
+
+ return WERR_OK;
+}
+
+/***********************************************************************************
+ _srvsvc_NetGetFileSecurity
+ Win9x NT tools get security descriptor.
+***********************************************************************************/
+
+WERROR _srvsvc_NetGetFileSecurity(pipes_struct *p,
+ struct srvsvc_NetGetFileSecurity *r)
+{
+ SEC_DESC *psd = NULL;
+ size_t sd_size;
+ fstring servicename;
+ SMB_STRUCT_STAT st;
+ NTSTATUS nt_status;
+ WERROR werr;
+ connection_struct *conn = NULL;
+ struct sec_desc_buf *sd_buf = NULL;
+ files_struct *fsp = NULL;
+ int snum;
+ char *oldcwd = NULL;
+
+ ZERO_STRUCT(st);
+
+ fstrcpy(servicename, r->in.share);
+
+ snum = find_service(servicename);
+ if (snum == -1) {
+ DEBUG(10, ("Could not find service %s\n", servicename));
+ werr = WERR_NET_NAME_NOT_FOUND;
+ goto error_exit;
+ }
+
+ nt_status = create_conn_struct(talloc_tos(), &conn, snum,
+ lp_pathname(snum), &oldcwd);
+ 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->server_info = p->server_info;
+
+ nt_status = create_file(
+ conn, /* conn */
+ NULL, /* req */
+ 0, /* root_dir_fid */
+ r->in.file, /* 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 */
+ 0, /* allocation_size */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL); /* psbuf */
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("_srvsvc_NetGetFileSecurity: can't open %s\n",
+ r->in.file));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ nt_status = SMB_VFS_FGET_NT_ACL(fsp,
+ (OWNER_SECURITY_INFORMATION
+ |GROUP_SECURITY_INFORMATION
+ |DACL_SECURITY_INFORMATION), &psd);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("_srvsvc_NetGetFileSecurity: Unable to get NT ACL "
+ "for file %s\n", r->in.file));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ sd_size = ndr_size_security_descriptor(psd, 0);
+
+ sd_buf = TALLOC_ZERO_P(p->mem_ctx, struct sec_desc_buf);
+ if (!sd_buf) {
+ werr = WERR_NOMEM;
+ goto error_exit;
+ }
+
+ sd_buf->sd_size = sd_size;
+ sd_buf->sd = psd;
+
+ *r->out.sd_buf = sd_buf;
+
+ psd->dacl->revision = NT4_ACL_REVISION;
+
+ close_file(fsp, NORMAL_CLOSE);
+ vfs_ChDir(conn, oldcwd);
+ conn_free_internal(conn);
+ return WERR_OK;
+
+error_exit:
+
+ if (fsp) {
+ close_file(fsp, NORMAL_CLOSE);
+ }
+
+ if (oldcwd) {
+ vfs_ChDir(conn, oldcwd);
+ }
+
+ if (conn) {
+ conn_free_internal(conn);
+ }
+
+ return werr;
+}
+
+/***********************************************************************************
+ _srvsvc_NetSetFileSecurity
+ Win9x NT tools set security descriptor.
+***********************************************************************************/
+
+WERROR _srvsvc_NetSetFileSecurity(pipes_struct *p,
+ struct srvsvc_NetSetFileSecurity *r)
+{
+ fstring servicename;
+ files_struct *fsp = NULL;
+ SMB_STRUCT_STAT st;
+ NTSTATUS nt_status;
+ WERROR werr;
+ connection_struct *conn = NULL;
+ int snum;
+ char *oldcwd = NULL;
+
+ ZERO_STRUCT(st);
+
+ fstrcpy(servicename, r->in.share);
+
+ snum = find_service(servicename);
+ if (snum == -1) {
+ DEBUG(10, ("Could not find service %s\n", servicename));
+ werr = WERR_NET_NAME_NOT_FOUND;
+ goto error_exit;
+ }
+
+ nt_status = create_conn_struct(talloc_tos(), &conn, snum,
+ lp_pathname(snum), &oldcwd);
+ 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->server_info = p->server_info;
+
+ nt_status = create_file(
+ conn, /* conn */
+ NULL, /* req */
+ 0, /* root_dir_fid */
+ r->in.file, /* 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 */
+ 0, /* allocation_size */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL); /* psbuf */
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("_srvsvc_NetSetFileSecurity: can't open %s\n",
+ r->in.file));
+ werr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ nt_status = SMB_VFS_FSET_NT_ACL(fsp,
+ r->in.securityinformation,
+ r->in.sd_buf->sd);
+
+ 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;
+ }
+
+ close_file(fsp, NORMAL_CLOSE);
+ vfs_ChDir(conn, oldcwd);
+ conn_free_internal(conn);
+ return WERR_OK;
+
+error_exit:
+
+ if (fsp) {
+ close_file(fsp, NORMAL_CLOSE);
+ }
+
+ if (oldcwd) {
+ vfs_ChDir(conn, oldcwd);
+ }
+
+ if (conn) {
+ conn_free_internal(conn);
+ }
+
+ 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 get_server_disk_count(void)
+{
+ return sizeof(server_disks)/sizeof(server_disks[0]);
+}
+
+static uint32 init_server_disk_enum(uint32 *resume)
+{
+ uint32 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 *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(pipes_struct *p,
+ struct srvsvc_NetDiskEnum *r)
+{
+ uint32 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*/
+
+ 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(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_UNKNOWN_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static void enum_file_close_fn( const struct share_mode_entry *e,
+ const char *sharepath, const char *fname,
+ void *private_data )
+{
+ char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+ struct srvsvc_NetFileClose *r =
+ (struct srvsvc_NetFileClose *)private_data;
+ uint32_t fid = (((uint32_t)(procid_to_pid(&e->pid))<<16) | e->share_file_id);
+
+ if (fid != r->in.fid) {
+ return; /* Not this file. */
+ }
+
+ if (!process_exists(e->pid) ) {
+ return;
+ }
+
+ /* Ok - send the close message. */
+ DEBUG(10,("enum_file_close_fn: request to close file %s, %s\n",
+ sharepath,
+ share_mode_str(talloc_tos(), 0, e) ));
+
+ share_mode_entry_to_message(msg, e);
+
+ r->out.result = ntstatus_to_werror(
+ messaging_send_buf(smbd_messaging_context(),
+ e->pid, MSG_SMB_CLOSE_FILE,
+ (uint8 *)msg,
+ MSG_SMB_SHARE_MODE_ENTRY_SIZE));
+}
+
+/********************************************************************
+ Close a file given a 32-bit file id.
+********************************************************************/
+
+WERROR _srvsvc_NetFileClose(pipes_struct *p, struct srvsvc_NetFileClose *r)
+{
+ struct current_user user;
+ SE_PRIV se_diskop = SE_DISK_OPERATOR;
+ bool is_disk_op;
+
+ DEBUG(5,("_srvsvc_NetFileClose: %d\n", __LINE__));
+
+ get_current_user(&user,p);
+
+ is_disk_op = user_has_privileges( p->pipe_user.nt_user_token, &se_diskop );
+
+ if (user.ut.uid != sec_initial_uid() && !is_disk_op) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* enum_file_close_fn sends the close message to
+ * the relevent smbd process. */
+
+ r->out.result = WERR_BADFILE;
+ share_mode_forall( enum_file_close_fn, (void *)r);
+ return r->out.result;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _srvsvc_NetCharDevEnum(pipes_struct *p, struct srvsvc_NetCharDevEnum *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevGetInfo(pipes_struct *p, struct srvsvc_NetCharDevGetInfo *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevControl(pipes_struct *p, struct srvsvc_NetCharDevControl *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQEnum(pipes_struct *p, struct srvsvc_NetCharDevQEnum *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQGetInfo(pipes_struct *p, struct srvsvc_NetCharDevQGetInfo *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQSetInfo(pipes_struct *p, struct srvsvc_NetCharDevQSetInfo *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQPurge(pipes_struct *p, struct srvsvc_NetCharDevQPurge *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetCharDevQPurgeSelf(pipes_struct *p, struct srvsvc_NetCharDevQPurgeSelf *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetFileGetInfo(pipes_struct *p, struct srvsvc_NetFileGetInfo *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetShareCheck(pipes_struct *p, struct srvsvc_NetShareCheck *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetServerStatisticsGet(pipes_struct *p, struct srvsvc_NetServerStatisticsGet *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetTransportAdd(pipes_struct *p, struct srvsvc_NetTransportAdd *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetTransportEnum(pipes_struct *p, struct srvsvc_NetTransportEnum *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetTransportDel(pipes_struct *p, struct srvsvc_NetTransportDel *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetSetServiceBits(pipes_struct *p, struct srvsvc_NetSetServiceBits *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPathType(pipes_struct *p, struct srvsvc_NetPathType *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPathCanonicalize(pipes_struct *p, struct srvsvc_NetPathCanonicalize *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPathCompare(pipes_struct *p, struct srvsvc_NetPathCompare *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRPRNAMECANONICALIZE(pipes_struct *p, struct srvsvc_NETRPRNAMECANONICALIZE *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetPRNameCompare(pipes_struct *p, struct srvsvc_NetPRNameCompare *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetShareDelStart(pipes_struct *p, struct srvsvc_NetShareDelStart *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetShareDelCommit(pipes_struct *p, struct srvsvc_NetShareDelCommit *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetServerTransportAddEx(pipes_struct *p, struct srvsvc_NetServerTransportAddEx *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NetServerSetServiceBitsEx(pipes_struct *p, struct srvsvc_NetServerSetServiceBitsEx *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSGETVERSION(pipes_struct *p, struct srvsvc_NETRDFSGETVERSION *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSCREATELOCALPARTITION(pipes_struct *p, struct srvsvc_NETRDFSCREATELOCALPARTITION *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSDELETELOCALPARTITION(pipes_struct *p, struct srvsvc_NETRDFSDELETELOCALPARTITION *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSSETLOCALVOLUMESTATE(pipes_struct *p, struct srvsvc_NETRDFSSETLOCALVOLUMESTATE *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSSETSERVERINFO(pipes_struct *p, struct srvsvc_NETRDFSSETSERVERINFO *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSCREATEEXITPOINT(pipes_struct *p, struct srvsvc_NETRDFSCREATEEXITPOINT *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSDELETEEXITPOINT(pipes_struct *p, struct srvsvc_NETRDFSDELETEEXITPOINT *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSMODIFYPREFIX(pipes_struct *p, struct srvsvc_NETRDFSMODIFYPREFIX *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSFIXLOCALVOLUME(pipes_struct *p, struct srvsvc_NETRDFSFIXLOCALVOLUME *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRDFSMANAGERREPORTSITEINFO(pipes_struct *p, struct srvsvc_NETRDFSMANAGERREPORTSITEINFO *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _srvsvc_NETRSERVERTRANSPORTDELEX(pipes_struct *p, struct srvsvc_NETRSERVERTRANSPORTDELEX *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
diff --git a/source3/rpc_server/srv_svcctl.c b/source3/rpc_server/srv_svcctl.c
new file mode 100644
index 0000000000..483fb8e1e9
--- /dev/null
+++ b/source3/rpc_server/srv_svcctl.c
@@ -0,0 +1,273 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2005 - 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+static bool proxy_svcctl_call(pipes_struct *p, uint8 opnum)
+{
+ struct api_struct *fns;
+ int n_fns;
+
+ svcctl_get_pipe_fns(&fns, &n_fns);
+
+ if (opnum >= n_fns)
+ return False;
+
+ if (fns[opnum].opnum != opnum) {
+ smb_panic("SVCCTL function table not sorted\n");
+ }
+
+ return fns[opnum].fn(p);
+}
+
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_close_service(pipes_struct *p)
+{
+ return proxy_svcctl_call( p, NDR_SVCCTL_CLOSESERVICEHANDLE );
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_open_scmanager(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_OPENSCMANAGERW);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_open_service(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_OPENSERVICEW);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_get_display_name(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_GETSERVICEDISPLAYNAMEW);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_query_status(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_QUERYSERVICESTATUS);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_enum_services_status(pipes_struct *p)
+{
+ SVCCTL_Q_ENUM_SERVICES_STATUS q_u;
+ SVCCTL_R_ENUM_SERVICES_STATUS r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!svcctl_io_q_enum_services_status("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _svcctl_enum_services_status(p, &q_u, &r_u);
+
+ if(!svcctl_io_r_enum_services_status("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_query_service_status_ex(pipes_struct *p)
+{
+ SVCCTL_Q_QUERY_SERVICE_STATUSEX q_u;
+ SVCCTL_R_QUERY_SERVICE_STATUSEX r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!svcctl_io_q_query_service_status_ex("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _svcctl_query_service_status_ex(p, &q_u, &r_u);
+
+ if(!svcctl_io_r_query_service_status_ex("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_enum_dependent_services(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_ENUMDEPENDENTSERVICESW);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_start_service(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_STARTSERVICEW);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_control_service(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_CONTROLSERVICE);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_query_service_config(pipes_struct *p)
+{
+ SVCCTL_Q_QUERY_SERVICE_CONFIG q_u;
+ SVCCTL_R_QUERY_SERVICE_CONFIG r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!svcctl_io_q_query_service_config("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _svcctl_query_service_config(p, &q_u, &r_u);
+
+ if(!svcctl_io_r_query_service_config("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_query_service_config2(pipes_struct *p)
+{
+ SVCCTL_Q_QUERY_SERVICE_CONFIG2 q_u;
+ SVCCTL_R_QUERY_SERVICE_CONFIG2 r_u;
+ prs_struct *data = &p->in_data.data;
+ prs_struct *rdata = &p->out_data.rdata;
+
+ ZERO_STRUCT(q_u);
+ ZERO_STRUCT(r_u);
+
+ if(!svcctl_io_q_query_service_config2("", &q_u, data, 0))
+ return False;
+
+ r_u.status = _svcctl_query_service_config2(p, &q_u, &r_u);
+
+ if(!svcctl_io_r_query_service_config2("", &r_u, rdata, 0))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_lock_service_db(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_LOCKSERVICEDATABASE);
+}
+
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_unlock_service_db(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_UNLOCKSERVICEDATABASE);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_query_security_sec(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_QUERYSERVICEOBJECTSECURITY);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static bool api_svcctl_set_security_sec(pipes_struct *p)
+{
+ return proxy_svcctl_call(p, NDR_SVCCTL_SETSERVICEOBJECTSECURITY);
+}
+
+
+/*******************************************************************
+ \PIPE\svcctl commands
+ ********************************************************************/
+
+static struct api_struct api_svcctl_cmds[] =
+{
+ { "SVCCTL_CLOSE_SERVICE" , SVCCTL_CLOSE_SERVICE , api_svcctl_close_service },
+ { "SVCCTL_OPEN_SCMANAGER_W" , SVCCTL_OPEN_SCMANAGER_W , api_svcctl_open_scmanager },
+ { "SVCCTL_OPEN_SERVICE_W" , SVCCTL_OPEN_SERVICE_W , api_svcctl_open_service },
+ { "SVCCTL_GET_DISPLAY_NAME" , SVCCTL_GET_DISPLAY_NAME , api_svcctl_get_display_name },
+ { "SVCCTL_QUERY_STATUS" , SVCCTL_QUERY_STATUS , api_svcctl_query_status },
+ { "SVCCTL_QUERY_SERVICE_CONFIG_W" , SVCCTL_QUERY_SERVICE_CONFIG_W , api_svcctl_query_service_config },
+ { "SVCCTL_QUERY_SERVICE_CONFIG2_W" , SVCCTL_QUERY_SERVICE_CONFIG2_W , api_svcctl_query_service_config2 },
+ { "SVCCTL_ENUM_SERVICES_STATUS_W" , SVCCTL_ENUM_SERVICES_STATUS_W , api_svcctl_enum_services_status },
+ { "SVCCTL_ENUM_DEPENDENT_SERVICES_W" , SVCCTL_ENUM_DEPENDENT_SERVICES_W , api_svcctl_enum_dependent_services },
+ { "SVCCTL_START_SERVICE_W" , SVCCTL_START_SERVICE_W , api_svcctl_start_service },
+ { "SVCCTL_CONTROL_SERVICE" , SVCCTL_CONTROL_SERVICE , api_svcctl_control_service },
+ { "SVCCTL_QUERY_SERVICE_STATUSEX_W" , SVCCTL_QUERY_SERVICE_STATUSEX_W , api_svcctl_query_service_status_ex },
+ { "SVCCTL_LOCK_SERVICE_DB" , SVCCTL_LOCK_SERVICE_DB , api_svcctl_lock_service_db },
+ { "SVCCTL_UNLOCK_SERVICE_DB" , SVCCTL_UNLOCK_SERVICE_DB , api_svcctl_unlock_service_db },
+ { "SVCCTL_QUERY_SERVICE_SEC" , SVCCTL_QUERY_SERVICE_SEC , api_svcctl_query_security_sec },
+ { "SVCCTL_SET_SERVICE_SEC" , SVCCTL_SET_SERVICE_SEC , api_svcctl_set_security_sec }
+};
+
+
+void svcctl2_get_pipe_fns( struct api_struct **fns, int *n_fns )
+{
+ *fns = api_svcctl_cmds;
+ *n_fns = sizeof(api_svcctl_cmds) / sizeof(struct api_struct);
+}
+
+NTSTATUS rpc_svcctl2_init(void)
+{
+ return rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION,
+ "svcctl", "ntsvcs",
+ &ndr_table_svcctl.syntax_id,
+ api_svcctl_cmds,
+ sizeof(api_svcctl_cmds) / sizeof(struct api_struct));
+}
diff --git a/source3/rpc_server/srv_svcctl_nt.c b/source3/rpc_server/srv_svcctl_nt.c
new file mode 100644
index 0000000000..6bb538a311
--- /dev/null
+++ b/source3/rpc_server/srv_svcctl_nt.c
@@ -0,0 +1,1109 @@
+/*
+ * 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.
+ *
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+struct service_control_op {
+ const char *name;
+ SERVICE_CONTROL_OPS *ops;
+};
+
+#define SVCCTL_NUM_INTERNAL_SERVICES 4
+
+/* 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_count( 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;
+}
+
+/********************************************************************
+********************************************************************/
+
+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( SEC_DESC *sec_desc, NT_USER_TOKEN *token,
+ uint32 access_desired, uint32 *access_granted )
+{
+ NTSTATUS result;
+
+ if ( geteuid() == sec_initial_uid() ) {
+ DEBUG(5,("svcctl_access_check: using root's token\n"));
+ token = get_root_nt_token();
+ }
+
+ se_access_check( sec_desc, token, access_desired, access_granted, &result );
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static SEC_DESC* construct_scm_sd( TALLOC_CTX *ctx )
+{
+ SEC_ACE ace[2];
+ SEC_ACCESS mask;
+ size_t i = 0;
+ SEC_DESC *sd;
+ SEC_ACL *acl;
+ size_t sd_size;
+
+ /* basic access for Everyone */
+
+ init_sec_access(&mask, SC_MANAGER_READ_ACCESS );
+ init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+ /* Full Access 'BUILTIN\Administrators' */
+
+ init_sec_access(&mask,SC_MANAGER_ALL_ACCESS );
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+
+ /* create the security descriptor */
+
+ if ( !(acl = 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,
+ acl, &sd_size)) )
+ return NULL;
+
+ return sd;
+}
+
+/******************************************************************
+ free() function for REGISTRY_KEY
+ *****************************************************************/
+
+static void free_service_handle_info(void *ptr)
+{
+ TALLOC_FREE( ptr );
+}
+
+/******************************************************************
+ Find a registry key handle and return a SERVICE_INFO
+ *****************************************************************/
+
+static SERVICE_INFO *find_service_info_by_hnd(pipes_struct *p, POLICY_HND *hnd)
+{
+ SERVICE_INFO *service_info = NULL;
+
+ if( !find_policy_by_hnd( p, hnd, (void **)(void *)&service_info) ) {
+ DEBUG(2,("find_service_info_by_hnd: handle not found\n"));
+ return NULL;
+ }
+
+ return service_info;
+}
+
+/******************************************************************
+ *****************************************************************/
+
+static WERROR create_open_service_handle( pipes_struct *p, POLICY_HND *handle, uint32 type,
+ const char *service, uint32 access_granted )
+{
+ SERVICE_INFO *info = NULL;
+ WERROR result = WERR_OK;
+ struct service_control_op *s_op;
+
+ if ( !(info = TALLOC_ZERO_P( NULL, SERVICE_INFO )) )
+ return WERR_NOMEM;
+
+ /* 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_NO_SUCH_SERVICE;
+ goto done;
+ }
+
+ info->ops = s_op->ops;
+
+ if ( !(info->name = talloc_strdup( info, s_op->name )) ) {
+ result = WERR_NOMEM;
+ goto done;
+ }
+ break;
+
+ default:
+ result = WERR_NO_SUCH_SERVICE;
+ goto done;
+ }
+
+ info->access_granted = access_granted;
+
+ /* store the SERVICE_INFO and create an open handle */
+
+ if ( !create_policy_hnd( p, handle, free_service_handle_info, info ) ) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+done:
+ if ( !W_ERROR_IS_OK(result) )
+ free_service_handle_info( info );
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _svcctl_OpenSCManagerW(pipes_struct *p,
+ struct svcctl_OpenSCManagerW *r)
+{
+ SEC_DESC *sec_desc;
+ uint32 access_granted = 0;
+ NTSTATUS status;
+
+ /* perform access checks */
+
+ if ( !(sec_desc = construct_scm_sd( p->mem_ctx )) )
+ return WERR_NOMEM;
+
+ se_map_generic( &r->in.access_mask, &scm_generic_map );
+ status = svcctl_access_check( sec_desc, p->pipe_user.nt_user_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(pipes_struct *p,
+ struct svcctl_OpenServiceW *r)
+{
+ SEC_DESC *sec_desc;
+ uint32 access_granted = 0;
+ NTSTATUS status;
+ const char *service = NULL;
+
+ service = r->in.ServiceName;
+ if (!service) {
+ return WERR_NOMEM;
+ }
+ 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_BADFID;
+
+ /* perform access checks. Use the root token in order to ensure that we
+ retrieve the security descriptor */
+
+ if ( !(sec_desc = svcctl_get_secdesc( p->mem_ctx, service, get_root_nt_token() )) )
+ return WERR_NOMEM;
+
+ se_map_generic( &r->in.access_mask, &svc_generic_map );
+ status = svcctl_access_check( sec_desc, p->pipe_user.nt_user_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 );
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _svcctl_CloseServiceHandle(pipes_struct *p, struct svcctl_CloseServiceHandle *r)
+{
+ if ( !close_policy_hnd( p, r->in.handle ) )
+ return WERR_BADFID;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_GetServiceDisplayNameW
+********************************************************************/
+
+WERROR _svcctl_GetServiceDisplayNameW(pipes_struct *p,
+ struct svcctl_GetServiceDisplayNameW *r)
+{
+ 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_BADFID;
+
+ service = r->in.service_name;
+
+ display_name = svcctl_lookup_dispname(p->mem_ctx, service, p->pipe_user.nt_user_token );
+ 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(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_BADFID;
+
+ 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, ENUM_SERVICES_STATUS **status, NT_USER_TOKEN *token )
+{
+ int num_services = 0;
+ int i;
+ ENUM_SERVICES_STATUS *st;
+ const char *display_name;
+
+ /* just count */
+ while ( svcctl_ops[num_services].name )
+ num_services++;
+
+ if ( !(st = TALLOC_ARRAY( ctx, ENUM_SERVICES_STATUS, num_services )) ) {
+ DEBUG(0,("enumerate_status: talloc() failed!\n"));
+ return -1;
+ }
+
+ for ( i=0; i<num_services; i++ ) {
+ init_unistr( &st[i].servicename, svcctl_ops[i].name );
+
+ display_name = svcctl_lookup_dispname(ctx, svcctl_ops[i].name, token );
+ init_unistr( &st[i].displayname, display_name ? display_name : "");
+
+ svcctl_ops[i].ops->service_status( svcctl_ops[i].name, &st[i].status );
+ }
+
+ *status = st;
+
+ return num_services;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _svcctl_enum_services_status(pipes_struct *p, SVCCTL_Q_ENUM_SERVICES_STATUS *q_u, SVCCTL_R_ENUM_SERVICES_STATUS *r_u)
+{
+ ENUM_SERVICES_STATUS *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, &q_u->handle );
+ NT_USER_TOKEN *token = p->pipe_user.nt_user_token;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SCM) )
+ return WERR_BADFID;
+
+ if ( !(info->access_granted & SC_RIGHT_MGR_ENUMERATE_SERVICE) ) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ num_services = enumerate_status( p->mem_ctx, &services, token );
+ if (num_services == -1 ) {
+ return WERR_NOMEM;
+ }
+
+ for ( i=0; i<num_services; i++ ) {
+ buffer_size += svcctl_sizeof_enum_services_status(&services[i]);
+ }
+
+ buffer_size += buffer_size % 4;
+
+ if (buffer_size > q_u->buffer_size ) {
+ num_services = 0;
+ result = WERR_MORE_DATA;
+ }
+
+ rpcbuf_init(&r_u->buffer, q_u->buffer_size, p->mem_ctx);
+
+ if ( W_ERROR_IS_OK(result) ) {
+ for ( i=0; i<num_services; i++ )
+ svcctl_io_enum_services_status( "", &services[i], &r_u->buffer, 0 );
+ }
+
+ r_u->needed = (buffer_size > q_u->buffer_size) ? buffer_size : q_u->buffer_size;
+ r_u->returned = (uint32)num_services;
+
+ if ( !(r_u->resume = TALLOC_P( p->mem_ctx, uint32 )) )
+ return WERR_NOMEM;
+
+ *r_u->resume = 0x0;
+
+ return result;
+}
+
+/********************************************************************
+ _svcctl_StartServiceW
+********************************************************************/
+
+WERROR _svcctl_StartServiceW(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_BADFID;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_START) )
+ return WERR_ACCESS_DENIED;
+
+ return info->ops->start_service( info->name );
+}
+
+/********************************************************************
+ _svcctl_ControlService
+********************************************************************/
+
+WERROR _svcctl_ControlService(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_BADFID;
+
+ 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 control action */
+
+ return WERR_ACCESS_DENIED;
+}
+
+/********************************************************************
+ _svcctl_EnumDependentServicesW
+********************************************************************/
+
+WERROR _svcctl_EnumDependentServicesW(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_BADFID;
+
+ if ( !(info->access_granted & SC_RIGHT_SVC_ENUMERATE_DEPENDENTS) )
+ 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 */
+ /* this is done in the autogenerated server already - gd */
+
+ *r->out.bytes_needed = r->in.buf_size;
+
+ /* no dependent services...basically a stub function */
+ *r->out.services_returned = 0;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _svcctl_query_service_status_ex( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_STATUSEX *q_u, SVCCTL_R_QUERY_SERVICE_STATUSEX *r_u )
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, &q_u->handle );
+ uint32 buffer_size;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_BADFID;
+
+ 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) */
+
+ rpcbuf_init( &r_u->buffer, q_u->buffer_size, p->mem_ctx );
+ r_u->needed = q_u->buffer_size;
+
+ switch ( q_u->level ) {
+ case SVC_STATUS_PROCESS_INFO:
+ {
+ SERVICE_STATUS_PROCESS svc_stat_proc;
+
+ /* Get the status of the service.. */
+ info->ops->service_status( info->name, &svc_stat_proc.status );
+ svc_stat_proc.process_id = sys_getpid();
+ svc_stat_proc.service_flags = 0x0;
+
+ svcctl_io_service_status_process( "", &svc_stat_proc, &r_u->buffer, 0 );
+ buffer_size = sizeof(SERVICE_STATUS_PROCESS);
+ break;
+ }
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+
+ buffer_size += buffer_size % 4;
+ r_u->needed = (buffer_size > q_u->buffer_size) ? buffer_size : q_u->buffer_size;
+
+ if (buffer_size > q_u->buffer_size )
+ return WERR_MORE_DATA;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR fill_svc_config( TALLOC_CTX *ctx, const char *name, SERVICE_CONFIG *config, NT_USER_TOKEN *token )
+{
+ REGVAL_CTR *values;
+ REGISTRY_VALUE *val;
+
+ /* retrieve the registry values for this service */
+
+ if ( !(values = svcctl_fetch_regvalues( name, token )) )
+ return WERR_REG_CORRUPT;
+
+ /* now fill in the individual values */
+
+ config->displayname = TALLOC_ZERO_P( ctx, UNISTR2 );
+ if ( (val = regval_ctr_getvalue( values, "DisplayName" )) != NULL )
+ init_unistr2( config->displayname, regval_sz( val ), UNI_STR_TERMINATE );
+ else
+ init_unistr2( config->displayname, name, UNI_STR_TERMINATE );
+
+ if ( (val = regval_ctr_getvalue( values, "ObjectName" )) != NULL ) {
+ config->startname = TALLOC_ZERO_P( ctx, UNISTR2 );
+ init_unistr2( config->startname, regval_sz( val ), UNI_STR_TERMINATE );
+ }
+
+ if ( (val = regval_ctr_getvalue( values, "ImagePath" )) != NULL ) {
+ config->executablepath = TALLOC_ZERO_P( ctx, UNISTR2 );
+ init_unistr2( config->executablepath, regval_sz( val ), UNI_STR_TERMINATE );
+ }
+
+ /* a few hard coded values */
+ /* loadordergroup and dependencies are empty */
+
+ config->tag_id = 0x00000000; /* unassigned loadorder group */
+ config->service_type = SVCCTL_WIN32_OWN_PROC;
+ 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_wins_support() ))
+ config->start_type = SVCCTL_DISABLED;
+ else
+ config->start_type = SVCCTL_DEMAND_START;
+
+
+ TALLOC_FREE( values );
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _svcctl_query_service_config( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_CONFIG *q_u, SVCCTL_R_QUERY_SERVICE_CONFIG *r_u )
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, &q_u->handle );
+ uint32 buffer_size;
+ WERROR wresult;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_BADFID;
+
+ 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_u->needed = q_u->buffer_size;
+
+ wresult = fill_svc_config( p->mem_ctx, info->name, &r_u->config, p->pipe_user.nt_user_token );
+ if ( !W_ERROR_IS_OK(wresult) )
+ return wresult;
+
+ buffer_size = svcctl_sizeof_service_config( &r_u->config );
+ r_u->needed = (buffer_size > q_u->buffer_size) ? buffer_size : q_u->buffer_size;
+
+ if (buffer_size > q_u->buffer_size ) {
+ ZERO_STRUCTP( &r_u->config );
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+WERROR _svcctl_query_service_config2( pipes_struct *p, SVCCTL_Q_QUERY_SERVICE_CONFIG2 *q_u, SVCCTL_R_QUERY_SERVICE_CONFIG2 *r_u )
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, &q_u->handle );
+ uint32 buffer_size;
+
+ /* perform access checks */
+
+ if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) )
+ return WERR_BADFID;
+
+ 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 */
+
+ rpcbuf_init( &r_u->buffer, q_u->buffer_size, p->mem_ctx );
+ r_u->needed = q_u->buffer_size;
+
+ switch ( q_u->level ) {
+ case SERVICE_CONFIG_DESCRIPTION:
+ {
+ SERVICE_DESCRIPTION desc_buf;
+ const char *description;
+
+ description = svcctl_lookup_description(p->mem_ctx, info->name, p->pipe_user.nt_user_token );
+
+ ZERO_STRUCTP( &desc_buf );
+
+ init_service_description_buffer( &desc_buf, description ? description : "");
+ svcctl_io_service_description( "", &desc_buf, &r_u->buffer, 0 );
+ buffer_size = svcctl_sizeof_service_description( &desc_buf );
+
+ break;
+ }
+ break;
+ case SERVICE_CONFIG_FAILURE_ACTIONS:
+ {
+ SERVICE_FAILURE_ACTIONS actions;
+
+ /* nothing to say...just service the request */
+
+ ZERO_STRUCTP( &actions );
+ svcctl_io_service_fa( "", &actions, &r_u->buffer, 0 );
+ buffer_size = svcctl_sizeof_service_fa( &actions );
+
+ break;
+ }
+ break;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ buffer_size += buffer_size % 4;
+ r_u->needed = (buffer_size > q_u->buffer_size) ? buffer_size : q_u->buffer_size;
+
+ if (buffer_size > q_u->buffer_size )
+ return WERR_INSUFFICIENT_BUFFER;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_LockServiceDatabase
+********************************************************************/
+
+WERROR _svcctl_LockServiceDatabase(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_BADFID;
+
+ 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(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_BADFID;
+
+ return close_policy_hnd( p, r->out.lock) ? WERR_OK : WERR_BADFID;
+}
+
+/********************************************************************
+ _svcctl_QueryServiceObjectSecurity
+********************************************************************/
+
+WERROR _svcctl_QueryServiceObjectSecurity(pipes_struct *p,
+ struct svcctl_QueryServiceObjectSecurity *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ SEC_DESC *sec_desc;
+ NTSTATUS status;
+ uint8_t *buffer = NULL;
+ size_t len = 0;
+
+
+ /* only support the SCM and individual services */
+
+ if ( !info || !(info->type & (SVC_HANDLE_IS_SERVICE|SVC_HANDLE_IS_SCM)) )
+ return WERR_BADFID;
+
+ /* check access reights (according to MSDN) */
+
+ if ( !(info->access_granted & STD_RIGHT_READ_CONTROL_ACCESS) )
+ return WERR_ACCESS_DENIED;
+
+ /* TODO: handle something besides DACL_SECURITY_INFORMATION */
+
+ if ( (r->in.security_flags & DACL_SECURITY_INFORMATION) != DACL_SECURITY_INFORMATION )
+ return WERR_INVALID_PARAM;
+
+ /* lookup the security descriptor and marshall it up for a reply */
+
+ if ( !(sec_desc = svcctl_get_secdesc( p->mem_ctx, info->name, get_root_nt_token() )) )
+ return WERR_NOMEM;
+
+ *r->out.needed = ndr_size_security_descriptor( sec_desc, 0 );
+
+ if ( *r->out.needed > r->in.buffer_size ) {
+ ZERO_STRUCTP( &r->out.buffer );
+ 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;
+ r->out.buffer = buffer;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ _svcctl_SetServiceObjectSecurity
+********************************************************************/
+
+WERROR _svcctl_SetServiceObjectSecurity(pipes_struct *p,
+ struct svcctl_SetServiceObjectSecurity *r)
+{
+ SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle );
+ SEC_DESC *sec_desc = NULL;
+ uint32 required_access;
+ NTSTATUS status;
+
+ if ( !info || !(info->type & (SVC_HANDLE_IS_SERVICE|SVC_HANDLE_IS_SCM)) )
+ return WERR_BADFID;
+
+ /* 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 DACL_SECURITY_INFORMATION:
+ required_access = STD_RIGHT_WRITE_DAC_ACCESS;
+ break;
+
+ case OWNER_SECURITY_INFORMATION:
+ case GROUP_SECURITY_INFORMATION:
+ required_access = STD_RIGHT_WRITE_OWNER_ACCESS;
+ break;
+
+ case SACL_SECURITY_INFORMATION:
+ return WERR_INVALID_PARAM;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+
+ 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.buffer_size,
+ &sec_desc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ /* store the new SD */
+
+ if ( !svcctl_set_secdesc( p->mem_ctx, info->name, sec_desc, p->pipe_user.nt_user_token ) )
+ return WERR_ACCESS_DENIED;
+
+ return WERR_OK;
+}
+
+
+WERROR _svcctl_DeleteService(pipes_struct *p, struct svcctl_DeleteService *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SetServiceStatus(pipes_struct *p, struct svcctl_SetServiceStatus *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_NotifyBootConfigStatus(pipes_struct *p, struct svcctl_NotifyBootConfigStatus *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SCSetServiceBitsW(pipes_struct *p, struct svcctl_SCSetServiceBitsW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfigW(pipes_struct *p, struct svcctl_ChangeServiceConfigW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_CreateServiceW(pipes_struct *p, struct svcctl_CreateServiceW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServicesStatusW(pipes_struct *p, struct svcctl_EnumServicesStatusW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceConfigW(pipes_struct *p, struct svcctl_QueryServiceConfigW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceLockStatusW(pipes_struct *p, struct svcctl_QueryServiceLockStatusW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetServiceKeyNameW(pipes_struct *p, struct svcctl_GetServiceKeyNameW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SCSetServiceBitsA(pipes_struct *p, struct svcctl_SCSetServiceBitsA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfigA(pipes_struct *p, struct svcctl_ChangeServiceConfigA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_CreateServiceA(pipes_struct *p, struct svcctl_CreateServiceA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumDependentServicesA(pipes_struct *p, struct svcctl_EnumDependentServicesA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServicesStatusA(pipes_struct *p, struct svcctl_EnumServicesStatusA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_OpenSCManagerA(pipes_struct *p, struct svcctl_OpenSCManagerA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_OpenServiceA(pipes_struct *p, struct svcctl_OpenServiceA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceConfigA(pipes_struct *p, struct svcctl_QueryServiceConfigA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceLockStatusA(pipes_struct *p, struct svcctl_QueryServiceLockStatusA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_StartServiceA(pipes_struct *p, struct svcctl_StartServiceA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetServiceDisplayNameA(pipes_struct *p, struct svcctl_GetServiceDisplayNameA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetServiceKeyNameA(pipes_struct *p, struct svcctl_GetServiceKeyNameA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_GetCurrentGroupeStateW(pipes_struct *p, struct svcctl_GetCurrentGroupeStateW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_EnumServiceGroupW(pipes_struct *p, struct svcctl_EnumServiceGroupW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfig2A(pipes_struct *p, struct svcctl_ChangeServiceConfig2A *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_ChangeServiceConfig2W(pipes_struct *p, struct svcctl_ChangeServiceConfig2W *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceConfig2A(pipes_struct *p, struct svcctl_QueryServiceConfig2A *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceConfig2W(pipes_struct *p, struct svcctl_QueryServiceConfig2W *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_QueryServiceStatusEx(pipes_struct *p, struct svcctl_QueryServiceStatusEx *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _EnumServicesStatusExA(pipes_struct *p, struct EnumServicesStatusExA *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _EnumServicesStatusExW(pipes_struct *p, struct EnumServicesStatusExW *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _svcctl_SCSendTSMessage(pipes_struct *p, struct svcctl_SCSendTSMessage *r)
+{
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
diff --git a/source3/rpc_server/srv_util.c b/source3/rpc_server/srv_util.c
new file mode 100644
index 0000000000..d4804b98ad
--- /dev/null
+++ b/source3/rpc_server/srv_util.c
@@ -0,0 +1,82 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-1998
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
+ * Copyright (C) Paul Ashton 1997-1998,
+ * Copyright (C) Andrew Bartlett 2004.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* this module apparently provides an implementation of DCE/RPC over a
+ * named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
+ * documentation are available (in on-line form) from the X-Open group.
+ *
+ * this module should provide a level of abstraction between SMB
+ * and DCE/RPC, while minimising the amount of mallocs, unnecessary
+ * data copies, and network traffic.
+ *
+ * in this version, which takes a "let's learn what's going on and
+ * get something running" approach, there is additional network
+ * traffic generated, but the code should be easier to understand...
+ *
+ * ... if you read the docs. or stare at packets for weeks on end.
+ *
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+#if 0 /* these aren't used currently but are here if you need them */
+/*
+ * A list of the rids of well known BUILTIN and Domain users
+ * and groups.
+ */
+
+static const rid_name builtin_alias_rids[] =
+{
+ { BUILTIN_ALIAS_RID_ADMINS , "Administrators" },
+ { BUILTIN_ALIAS_RID_USERS , "Users" },
+ { BUILTIN_ALIAS_RID_GUESTS , "Guests" },
+ { BUILTIN_ALIAS_RID_POWER_USERS , "Power Users" },
+
+ { BUILTIN_ALIAS_RID_ACCOUNT_OPS , "Account Operators" },
+ { BUILTIN_ALIAS_RID_SYSTEM_OPS , "System Operators" },
+ { BUILTIN_ALIAS_RID_PRINT_OPS , "Print Operators" },
+ { BUILTIN_ALIAS_RID_BACKUP_OPS , "Backup Operators" },
+ { BUILTIN_ALIAS_RID_REPLICATOR , "Replicator" },
+ { 0 , NULL }
+};
+
+/* array lookup of well-known Domain RID users. */
+static const rid_name domain_user_rids[] =
+{
+ { DOMAIN_USER_RID_ADMIN , "Administrator" },
+ { DOMAIN_USER_RID_GUEST , "Guest" },
+ { 0 , NULL }
+};
+
+/* array lookup of well-known Domain RID groups. */
+static const rid_name domain_group_rids[] =
+{
+ { DOMAIN_GROUP_RID_ADMINS , "Domain Admins" },
+ { DOMAIN_GROUP_RID_USERS , "Domain Users" },
+ { DOMAIN_GROUP_RID_GUESTS , "Domain Guests" },
+ { 0 , NULL }
+};
+#endif
+
diff --git a/source3/rpc_server/srv_winreg_nt.c b/source3/rpc_server/srv_winreg_nt.c
new file mode 100644
index 0000000000..3991c5ae02
--- /dev/null
+++ b/source3/rpc_server/srv_winreg_nt.c
@@ -0,0 +1,965 @@
+/*
+ * 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/******************************************************************
+ free() function for struct registry_key
+ *****************************************************************/
+
+static void free_regkey(void *ptr)
+{
+ struct registry_key *key = (struct registry_key *)ptr;
+ TALLOC_FREE(key);
+}
+
+/******************************************************************
+ Find a registry key handle and return a struct registry_key *
+ *****************************************************************/
+
+static struct registry_key *find_regkey_by_hnd(pipes_struct *p,
+ POLICY_HND *hnd)
+{
+ struct registry_key *regkey = NULL;
+
+ if(!find_policy_by_hnd(p,hnd,(void **)(void *)&regkey)) {
+ DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: "));
+ 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( pipes_struct *p, POLICY_HND *hnd,
+ struct registry_key *parent,
+ const char *subkeyname,
+ uint32 access_desired )
+{
+ WERROR result = WERR_OK;
+ struct registry_key *key;
+
+ if (parent == NULL) {
+ result = reg_openhive(NULL, subkeyname, access_desired,
+ p->pipe_user.nt_user_token, &key);
+ }
+ else {
+ result = reg_openkey(NULL, parent, subkeyname, access_desired,
+ &key);
+ }
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ return result;
+ }
+
+ if ( !create_policy_hnd( p, hnd, free_regkey, key ) ) {
+ return WERR_BADFILE;
+ }
+
+ 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(pipes_struct *p, POLICY_HND *hnd)
+{
+ struct registry_key *regkey = find_regkey_by_hnd(p, hnd);
+
+ 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;
+}
+
+/********************************************************************
+ reg_close
+ ********************************************************************/
+
+WERROR _winreg_CloseKey(pipes_struct *p, struct winreg_CloseKey *r)
+{
+ /* close the policy handle */
+
+ if (!close_registry_key(p, r->in.handle))
+ return WERR_BADFID;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKLM(pipes_struct *p, struct winreg_OpenHKLM *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKLM, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKPD(pipes_struct *p, struct winreg_OpenHKPD *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKPD, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKPT(pipes_struct *p, struct winreg_OpenHKPT *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKPT, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKCR(pipes_struct *p, struct winreg_OpenHKCR *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKCR, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKU(pipes_struct *p, struct winreg_OpenHKU *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKU, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKCU(pipes_struct *p, struct winreg_OpenHKCU *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKCU, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKCC(pipes_struct *p, struct winreg_OpenHKCC *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKCC, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKDD(pipes_struct *p, struct winreg_OpenHKDD *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKDD, r->in.access_mask);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_OpenHKPN(pipes_struct *p, struct winreg_OpenHKPN *r)
+{
+ return open_registry_key(p, r->out.handle, NULL, KEY_HKPN, r->in.access_mask);
+}
+
+/*******************************************************************
+ reg_reply_open_entry
+ ********************************************************************/
+
+WERROR _winreg_OpenKey(pipes_struct *p, struct winreg_OpenKey *r)
+{
+ struct registry_key *parent = find_regkey_by_hnd(p, r->in.parent_handle );
+
+ if ( !parent )
+ return WERR_BADFID;
+
+ return open_registry_key(p, r->out.handle, parent, r->in.keyname.name, r->in.access_mask);
+}
+
+/*******************************************************************
+ reg_reply_info
+ ********************************************************************/
+
+WERROR _winreg_QueryValue(pipes_struct *p, struct winreg_QueryValue *r)
+{
+ WERROR status = WERR_BADFILE;
+ struct registry_key *regkey = find_regkey_by_hnd( p, r->in.handle );
+ prs_struct prs_hkpd;
+
+ uint8_t *outbuf;
+ uint32_t outbuf_size;
+
+ DATA_BLOB val_blob;
+ bool free_buf = False;
+ bool free_prs = False;
+
+ if ( !regkey )
+ return WERR_BADFID;
+
+ if ((r->out.value_length == NULL) || (r->out.type == NULL)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ *r->out.value_length = *r->out.type = REG_NONE;
+
+ DEBUG(7,("_reg_info: policy key name = [%s]\n", regkey->key->name));
+ DEBUG(7,("_reg_info: 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_NOMEM;
+ 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_NOMEM;
+ 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_BADFILE;
+ }
+
+ *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)) {
+ if (r->out.data_size) {
+ *r->out.data_size = 0;
+ }
+ if (r->out.value_length) {
+ *r->out.value_length = 0;
+ }
+ return status;
+ }
+
+ status = registry_push_value(p->mem_ctx, val, &val_blob);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ outbuf = val_blob.data;
+ outbuf_size = val_blob.length;
+ *r->out.type = val->type;
+ }
+
+ *r->out.value_length = outbuf_size;
+
+ if ( *r->in.data_size == 0 || !r->out.data ) {
+ status = WERR_OK;
+ } else if ( *r->out.value_length > *r->in.data_size ) {
+ status = WERR_MORE_DATA;
+ } else {
+ memcpy( r->out.data, outbuf, *r->out.value_length );
+ status = WERR_OK;
+ }
+
+ *r->out.data_size = *r->out.value_length;
+
+ if (free_prs) prs_mem_free(&prs_hkpd);
+ if (free_buf) SAFE_FREE(outbuf);
+
+ return status;
+}
+
+/*****************************************************************************
+ Implementation of REG_QUERY_KEY
+ ****************************************************************************/
+
+WERROR _winreg_QueryInfoKey(pipes_struct *p, struct winreg_QueryInfoKey *r)
+{
+ WERROR status = WERR_OK;
+ struct registry_key *regkey = find_regkey_by_hnd( p, r->in.handle );
+
+ if ( !regkey )
+ return WERR_BADFID;
+
+ 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;
+}
+
+
+/*****************************************************************************
+ Implementation of REG_GETVERSION
+ ****************************************************************************/
+
+WERROR _winreg_GetVersion(pipes_struct *p, struct winreg_GetVersion *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd( p, r->in.handle );
+
+ if ( !regkey )
+ return WERR_BADFID;
+
+ return reg_getversion(r->out.version);
+}
+
+
+/*****************************************************************************
+ Implementation of REG_ENUM_KEY
+ ****************************************************************************/
+
+WERROR _winreg_EnumKey(pipes_struct *p, struct winreg_EnumKey *r)
+{
+ WERROR err;
+ struct registry_key *key = find_regkey_by_hnd( p, r->in.handle );
+
+ if ( !key )
+ return WERR_BADFID;
+
+ if ( !r->in.name || !r->in.keyclass )
+ return WERR_INVALID_PARAM;
+
+ DEBUG(8,("_reg_enum_key: enumerating key [%s]\n", key->key->name));
+
+ err = reg_enumkey(p->mem_ctx, key, r->in.enum_index, (char **)&r->out.name->name,
+ r->out.last_changed_time);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+ r->out.keyclass->name = "";
+ return WERR_OK;
+}
+
+/*****************************************************************************
+ Implementation of REG_ENUM_VALUE
+ ****************************************************************************/
+
+WERROR _winreg_EnumValue(pipes_struct *p, struct winreg_EnumValue *r)
+{
+ WERROR err;
+ struct registry_key *key = find_regkey_by_hnd( p, r->in.handle );
+ char *valname;
+ struct registry_value *val;
+ DATA_BLOB value_blob;
+
+ if ( !key )
+ return WERR_BADFID;
+
+ if ( !r->in.name )
+ return WERR_INVALID_PARAM;
+
+ 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;
+ }
+
+ err = registry_push_value(p->mem_ctx, val, &value_blob);
+ 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_PARAM;
+ }
+
+ if (value_blob.length > *r->out.size) {
+ return WERR_MORE_DATA;
+ }
+
+ memcpy( r->out.value, value_blob.data, value_blob.length );
+ }
+
+ if (r->out.length != NULL) {
+ *r->out.length = value_blob.length;
+ }
+ if (r->out.size != NULL) {
+ *r->out.size = value_blob.length;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ reg_shutdwon
+ ********************************************************************/
+
+WERROR _winreg_InitiateSystemShutdown(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 );
+}
+
+/*******************************************************************
+ reg_shutdown_ex
+ ********************************************************************/
+
+#define SHUTDOWN_R_STRING "-r"
+#define SHUTDOWN_F_STRING "-f"
+
+
+WERROR _winreg_InitiateSystemShutdownEx(pipes_struct *p, struct winreg_InitiateSystemShutdownEx *r)
+{
+ char *shutdown_script = NULL;
+ char *msg = NULL;
+ char *chkmsg = NULL;
+ fstring str_timeout;
+ fstring str_reason;
+ fstring do_reboot;
+ fstring f;
+ int ret;
+ bool can_shutdown;
+
+ shutdown_script = talloc_strdup(p->mem_ctx, lp_shutdown_script());
+ if (!shutdown_script) {
+ return WERR_NOMEM;
+ }
+ 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->name && r->in.message->name->name ) {
+ if ( (msg = talloc_strdup(p->mem_ctx, r->in.message->name->name )) == NULL ) {
+ return WERR_NOMEM;
+ }
+ chkmsg = TALLOC_ARRAY(p->mem_ctx, char, strlen(msg)+1);
+ if (!chkmsg) {
+ return WERR_NOMEM;
+ }
+ alpha_strcpy(chkmsg, msg, NULL, strlen(msg)+1);
+ }
+
+ 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_NOMEM;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%t", str_timeout);
+ if (!shutdown_script) {
+ return WERR_NOMEM;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%r", do_reboot);
+ if (!shutdown_script) {
+ return WERR_NOMEM;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%f", f);
+ if (!shutdown_script) {
+ return WERR_NOMEM;
+ }
+ shutdown_script = talloc_all_string_sub(p->mem_ctx,
+ shutdown_script, "%x", str_reason);
+ if (!shutdown_script) {
+ return WERR_NOMEM;
+ }
+
+ can_shutdown = user_has_privileges( p->pipe_user.nt_user_token, &se_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 );
+
+ 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;
+}
+
+/*******************************************************************
+ reg_abort_shutdwon
+ ********************************************************************/
+
+WERROR _winreg_AbortSystemShutdown(pipes_struct *p, struct winreg_AbortSystemShutdown *r)
+{
+ const char *abort_shutdown_script;
+ int ret;
+ bool can_shutdown;
+
+ abort_shutdown_script = lp_abort_shutdown_script();
+
+ if (!*abort_shutdown_script)
+ return WERR_ACCESS_DENIED;
+
+ can_shutdown = user_has_privileges( p->pipe_user.nt_user_token, &se_remote_shutdown );
+
+ /********** BEGIN SeRemoteShutdownPrivilege BLOCK **********/
+
+ if ( can_shutdown )
+ become_root();
+
+ ret = smbrun( abort_shutdown_script, NULL );
+
+ if ( can_shutdown )
+ unbecome_root();
+
+ /********** END SeRemoteShutdownPrivilege BLOCK **********/
+
+ DEBUG(3,("_reg_abort_shutdown: Running the command `%s' gave %d\n",
+ abort_shutdown_script, ret));
+
+ return (ret == 0) ? WERR_OK : WERR_ACCESS_DENIED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static int validate_reg_filename(TALLOC_CTX *ctx, char **pp_fname )
+{
+ char *p = NULL;
+ int num_services = lp_numservices();
+ int snum = -1;
+ const char *share_path;
+ char *fname = *pp_fname;
+
+ /* convert to a unix path, stripping the C:\ along the way */
+
+ if (!(p = valid_share_pathname(ctx, fname))) {
+ return -1;
+ }
+
+ /* has to exist within a valid file share */
+
+ for (snum=0; snum<num_services; snum++) {
+ if (!lp_snum_ok(snum) || lp_print_ok(snum)) {
+ continue;
+ }
+
+ share_path = lp_pathname(snum);
+
+ /* make sure we have a path (e.g. [homes] ) */
+ if (strlen(share_path) == 0) {
+ continue;
+ }
+
+ if (strncmp(share_path, p, strlen(share_path)) == 0) {
+ break;
+ }
+ }
+
+ *pp_fname = p;
+ return (snum < num_services) ? snum : -1;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_RestoreKey(pipes_struct *p, struct winreg_RestoreKey *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd( p, r->in.handle );
+ char *fname = NULL;
+ int snum;
+
+ if ( !regkey )
+ return WERR_BADFID;
+
+ if ( !r->in.filename || !r->in.filename->name )
+ return WERR_INVALID_PARAM;
+
+ fname = talloc_strdup(p->mem_ctx, r->in.filename->name);
+ if (!fname) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(8,("_winreg_RestoreKey: verifying restore of key [%s] from "
+ "\"%s\"\n", regkey->key->name, fname));
+
+ if ((snum = validate_reg_filename(p->mem_ctx, &fname)) == -1)
+ return WERR_OBJECT_PATH_INVALID;
+
+ /* user must posses SeRestorePrivilege for this this proceed */
+
+ if ( !user_has_privileges( p->pipe_user.nt_user_token, &se_restore ) )
+ return WERR_ACCESS_DENIED;
+
+ DEBUG(2,("_winreg_RestoreKey: Restoring [%s] from %s in share %s\n",
+ regkey->key->name, fname, lp_servicename(snum) ));
+
+ return reg_restorekey(regkey, fname);
+}
+
+WERROR _winreg_SaveKey(pipes_struct *p, struct winreg_SaveKey *r)
+{
+ struct registry_key *regkey = find_regkey_by_hnd( p, r->in.handle );
+ char *fname = NULL;
+ int snum = -1;
+
+ if ( !regkey )
+ return WERR_BADFID;
+
+ if ( !r->in.filename || !r->in.filename->name )
+ return WERR_INVALID_PARAM;
+
+ fname = talloc_strdup(p->mem_ctx, r->in.filename->name);
+ if (!fname) {
+ return WERR_NOMEM;
+ }
+
+ DEBUG(8,("_winreg_SaveKey: verifying backup of key [%s] to \"%s\"\n",
+ regkey->key->name, fname));
+
+ if ((snum = validate_reg_filename(p->mem_ctx, &fname)) == -1 )
+ return WERR_OBJECT_PATH_INVALID;
+
+ DEBUG(2,("_winreg_SaveKey: Saving [%s] to %s in share %s\n",
+ regkey->key->name, fname, lp_servicename(snum) ));
+
+ return reg_savekey(regkey, fname);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_SaveKeyEx(pipes_struct *p, struct winreg_SaveKeyEx *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_CreateKey( pipes_struct *p, struct winreg_CreateKey *r)
+{
+ struct registry_key *parent = find_regkey_by_hnd(p, r->in.handle);
+ struct registry_key *new_key;
+ WERROR result;
+
+ if ( !parent )
+ return WERR_BADFID;
+
+ 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, free_regkey, new_key)) {
+ TALLOC_FREE(new_key);
+ return WERR_BADFILE;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_SetValue(pipes_struct *p, struct winreg_SetValue *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p, r->in.handle);
+ struct registry_value *val;
+ WERROR status;
+
+ if ( !key )
+ return WERR_BADFID;
+
+ DEBUG(8,("_reg_set_value: Setting value for [%s:%s]\n",
+ key->key->name, r->in.name.name));
+
+ status = registry_pull_value(p->mem_ctx, &val, r->in.type, r->in.data,
+ r->in.size, r->in.size);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ return reg_setvalue(key, r->in.name.name, val);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_DeleteKey(pipes_struct *p, struct winreg_DeleteKey *r)
+{
+ struct registry_key *parent = find_regkey_by_hnd(p, r->in.handle);
+
+ if ( !parent )
+ return WERR_BADFID;
+
+ return reg_deletekey(parent, r->in.key.name);
+}
+
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_DeleteValue(pipes_struct *p, struct winreg_DeleteValue *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p, r->in.handle);
+
+ if ( !key )
+ return WERR_BADFID;
+
+ return reg_deletevalue(key, r->in.value.name);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_GetKeySecurity(pipes_struct *p, struct winreg_GetKeySecurity *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p, r->in.handle);
+ WERROR err;
+ struct security_descriptor *secdesc;
+ uint8 *data;
+ size_t len;
+
+ if ( !key )
+ return WERR_BADFID;
+
+ /* access checks first */
+
+ if ( !(key->key->access_granted & STD_RIGHT_READ_CONTROL_ACCESS) )
+ 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;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_SetKeySecurity(pipes_struct *p, struct winreg_SetKeySecurity *r)
+{
+ struct registry_key *key = find_regkey_by_hnd(p, r->in.handle);
+ struct security_descriptor *secdesc;
+ WERROR err;
+
+ if ( !key )
+ return WERR_BADFID;
+
+ /* access checks first */
+
+ if ( !(key->key->access_granted & STD_RIGHT_WRITE_DAC_ACCESS) )
+ 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);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_FlushKey(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;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_UnLoadKey(pipes_struct *p, struct winreg_UnLoadKey *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_ReplaceKey(pipes_struct *p, struct winreg_ReplaceKey *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_LoadKey(pipes_struct *p, struct winreg_LoadKey *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_NotifyChangeKeyValue(pipes_struct *p, struct winreg_NotifyChangeKeyValue *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_QueryMultipleValues(pipes_struct *p, struct winreg_QueryMultipleValues *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+WERROR _winreg_QueryMultipleValues2(pipes_struct *p, struct winreg_QueryMultipleValues2 *r)
+{
+ /* fill in your code here if you think this call should
+ do anything */
+
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
diff --git a/source3/rpc_server/srv_wkssvc_nt.c b/source3/rpc_server/srv_wkssvc_nt.c
new file mode 100644
index 0000000000..c96439cc1a
--- /dev/null
+++ b/source3/rpc_server/srv_wkssvc_nt.c
@@ -0,0 +1,494 @@
+/*
+ * 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 "libnet/libnet.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/*******************************************************************
+ Fill in the values for the struct wkssvc_NetWkstaInfo100.
+ ********************************************************************/
+
+static void create_wks_info_100(struct wkssvc_NetWkstaInfo100 *info100)
+{
+ info100->platform_id = PLATFORM_ID_NT; /* unknown */
+ info100->version_major = lp_major_announce_version();
+ info100->version_minor = lp_minor_announce_version();
+
+ info100->server_name = talloc_asprintf_strupper_m(
+ info100, "%s", global_myname());
+ info100->domain_name = talloc_asprintf_strupper_m(
+ info100, "%s", lp_workgroup());
+
+ return;
+}
+
+/********************************************************************
+ only supports info level 100 at the moment.
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaGetInfo(pipes_struct *p, struct wkssvc_NetWkstaGetInfo *r)
+{
+ struct wkssvc_NetWkstaInfo100 *wks100 = NULL;
+
+ /* We only support info level 100 currently */
+
+ if ( r->in.level != 100 ) {
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ if ( (wks100 = TALLOC_ZERO_P(p->mem_ctx, struct wkssvc_NetWkstaInfo100)) == NULL ) {
+ return WERR_NOMEM;
+ }
+
+ create_wks_info_100( wks100 );
+
+ r->out.info->info100 = wks100;
+
+ return WERR_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaSetInfo(pipes_struct *p, struct wkssvc_NetWkstaSetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaEnumUsers(pipes_struct *p, struct wkssvc_NetWkstaEnumUsers *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaUserGetInfo(pipes_struct *p, struct wkssvc_NetrWkstaUserGetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaUserSetInfo(pipes_struct *p, struct wkssvc_NetrWkstaUserSetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetWkstaTransportEnum(pipes_struct *p, struct wkssvc_NetWkstaTransportEnum *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaTransportAdd(pipes_struct *p, struct wkssvc_NetrWkstaTransportAdd *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWkstaTransportDel(pipes_struct *p, struct wkssvc_NetrWkstaTransportDel *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseAdd(pipes_struct *p, struct wkssvc_NetrUseAdd *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseGetInfo(pipes_struct *p, struct wkssvc_NetrUseGetInfo *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseDel(pipes_struct *p, struct wkssvc_NetrUseDel *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUseEnum(pipes_struct *p, struct wkssvc_NetrUseEnum *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrMessageBufferSend(pipes_struct *p, struct wkssvc_NetrMessageBufferSend *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrWorkstationStatisticsGet(pipes_struct *p, struct wkssvc_NetrWorkstationStatisticsGet *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrLogonDomainNameAdd(pipes_struct *p, struct wkssvc_NetrLogonDomainNameAdd *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrLogonDomainNameDel(pipes_struct *p, struct wkssvc_NetrLogonDomainNameDel *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrJoinDomain(pipes_struct *p, struct wkssvc_NetrJoinDomain *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUnjoinDomain(pipes_struct *p, struct wkssvc_NetrUnjoinDomain *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrRenameMachineInDomain(pipes_struct *p, struct wkssvc_NetrRenameMachineInDomain *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrValidateName(pipes_struct *p, struct wkssvc_NetrValidateName *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrGetJoinInformation(pipes_struct *p, struct wkssvc_NetrGetJoinInformation *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrGetJoinableOus(pipes_struct *p, struct wkssvc_NetrGetJoinableOus *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ _wkssvc_NetrJoinDomain2
+ ********************************************************************/
+
+WERROR _wkssvc_NetrJoinDomain2(pipes_struct *p,
+ struct wkssvc_NetrJoinDomain2 *r)
+{
+ struct libnet_JoinCtx *j = NULL;
+ char *cleartext_pwd = NULL;
+ char *admin_domain = NULL;
+ char *admin_account = NULL;
+ WERROR werr;
+ struct nt_user_token *token = p->pipe_user.nt_user_token;
+
+ if (!r->in.domain_name) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!r->in.admin_account || !r->in.encrypted_password) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!user_has_privileges(token, &se_machine_account) &&
+ !nt_token_check_domain_rid(token, DOMAIN_GROUP_RID_ADMINS) &&
+ !nt_token_check_domain_rid(token, BUILTIN_ALIAS_RID_ADMINS)) {
+ 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;
+ }
+
+ werr = decode_wkssvc_join_password_buffer(
+ p->mem_ctx, r->in.encrypted_password,
+ &p->server_info->user_session_key, &cleartext_pwd);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ split_domain_user(p->mem_ctx,
+ r->in.admin_account,
+ &admin_domain,
+ &admin_account);
+
+ 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 = smbd_messaging_context();
+
+ become_root();
+ werr = libnet_Join(p->mem_ctx, j);
+ 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 :
+ dos_errstr(werr)));
+ }
+
+ TALLOC_FREE(j);
+ return werr;
+}
+
+/********************************************************************
+ _wkssvc_NetrUnjoinDomain2
+ ********************************************************************/
+
+WERROR _wkssvc_NetrUnjoinDomain2(pipes_struct *p,
+ struct wkssvc_NetrUnjoinDomain2 *r)
+{
+ struct libnet_UnjoinCtx *u = NULL;
+ char *cleartext_pwd = NULL;
+ char *admin_domain = NULL;
+ char *admin_account = NULL;
+ WERROR werr;
+ struct nt_user_token *token = p->pipe_user.nt_user_token;
+
+ if (!r->in.account || !r->in.encrypted_password) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!user_has_privileges(token, &se_machine_account) &&
+ !nt_token_check_domain_rid(token, DOMAIN_GROUP_RID_ADMINS) &&
+ !nt_token_check_domain_rid(token, BUILTIN_ALIAS_RID_ADMINS)) {
+ DEBUG(5,("_wkssvc_NetrUnjoinDomain2: account doesn't have "
+ "sufficient privileges\n"));
+ return WERR_ACCESS_DENIED;
+ }
+
+ werr = decode_wkssvc_join_password_buffer(
+ p->mem_ctx, r->in.encrypted_password,
+ &p->server_info->user_session_key, &cleartext_pwd);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ split_domain_user(p->mem_ctx,
+ r->in.account,
+ &admin_domain,
+ &admin_account);
+
+ 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 = smbd_messaging_context();
+
+ become_root();
+ werr = libnet_Unjoin(p->mem_ctx, u);
+ 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 :
+ dos_errstr(werr)));
+ }
+
+ TALLOC_FREE(u);
+ return werr;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrRenameMachineInDomain2(pipes_struct *p, struct wkssvc_NetrRenameMachineInDomain2 *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrValidateName2(pipes_struct *p, struct wkssvc_NetrValidateName2 *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrGetJoinableOus2(pipes_struct *p, struct wkssvc_NetrGetJoinableOus2 *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrAddAlternateComputerName(pipes_struct *p, struct wkssvc_NetrAddAlternateComputerName *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrRemoveAlternateComputerName(pipes_struct *p, struct wkssvc_NetrRemoveAlternateComputerName *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrSetPrimaryComputername(pipes_struct *p, struct wkssvc_NetrSetPrimaryComputername *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+WERROR _wkssvc_NetrEnumerateComputerNames(pipes_struct *p, struct wkssvc_NetrEnumerateComputerNames *r)
+{
+ /* FIXME: Add implementation code here */
+ p->rng_fault_state = True;
+ return WERR_NOT_SUPPORTED;
+}
+