diff options
Diffstat (limited to 'source3/rpc_server')
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( ¤t_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, ¤t_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, ¤t_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, ¶meters); + 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, ¶meters); + 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( ¬ify, &msg_tv, msg_ptr, msg_len ); + msg_ptr += msg_len; + + /* add to correct list in container */ + + notify_msg_ctr_addmsg( &messages, ¬ify ); + + /* 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( ¬ify_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, ¤t_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], ¤t_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, ¤t_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], ¤t_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 *)®key)) { + 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; +} + |