diff options
Diffstat (limited to 'source3/rpc_server')
-rw-r--r-- | source3/rpc_server/.cvsignore | 0 | ||||
-rw-r--r-- | source3/rpc_server/srv_dfs.c | 176 | ||||
-rw-r--r-- | source3/rpc_server/srv_dfs_nt.c | 366 | ||||
-rw-r--r-- | source3/rpc_server/srv_lsa.c | 641 | ||||
-rw-r--r-- | source3/rpc_server/srv_lsa_hnd.c | 234 | ||||
-rw-r--r-- | source3/rpc_server/srv_lsa_nt.c | 1148 | ||||
-rw-r--r-- | source3/rpc_server/srv_netlog.c | 340 | ||||
-rw-r--r-- | source3/rpc_server/srv_netlog_nt.c | 715 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe.c | 1225 | ||||
-rw-r--r-- | source3/rpc_server/srv_pipe_hnd.c | 1131 | ||||
-rw-r--r-- | source3/rpc_server/srv_reg.c | 208 | ||||
-rw-r--r-- | source3/rpc_server/srv_reg_nt.c | 236 | ||||
-rw-r--r-- | source3/rpc_server/srv_samr.c | 1442 | ||||
-rw-r--r-- | source3/rpc_server/srv_samr_nt.c | 3826 | ||||
-rwxr-xr-x | source3/rpc_server/srv_spoolss.c | 1468 | ||||
-rw-r--r-- | source3/rpc_server/srv_spoolss_nt.c | 7837 | ||||
-rw-r--r-- | source3/rpc_server/srv_srvsvc.c | 522 | ||||
-rw-r--r-- | source3/rpc_server/srv_srvsvc_nt.c | 1935 | ||||
-rw-r--r-- | source3/rpc_server/srv_util.c | 514 | ||||
-rw-r--r-- | source3/rpc_server/srv_wkssvc.c | 70 | ||||
-rw-r--r-- | source3/rpc_server/srv_wkssvc_nt.c | 78 |
21 files changed, 24112 insertions, 0 deletions
diff --git a/source3/rpc_server/.cvsignore b/source3/rpc_server/.cvsignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/source3/rpc_server/.cvsignore diff --git a/source3/rpc_server/srv_dfs.c b/source3/rpc_server/srv_dfs.c new file mode 100644 index 0000000000..4351fd088e --- /dev/null +++ b/source3/rpc_server/srv_dfs.c @@ -0,0 +1,176 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines for Dfs + * Copyright (C) Andrew Tridgell 1992-1997, + * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, + * Copyright (C) Shirish Kalele 2000. + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the interface to the dfs pipe. */ + +#include "includes.h" +#include "nterr.h" + +#define MAX_MSDFS_JUNCTIONS 256 + +extern pstring global_myname; + +/********************************************************************** + api_dfs_exist + **********************************************************************/ + +static BOOL api_dfs_exist(pipes_struct *p) +{ + DFS_Q_DFS_EXIST q_u; + DFS_R_DFS_EXIST r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + if(!dfs_io_q_dfs_exist("", &q_u, data, 0)) + return False; + + r_u.status = _dfs_exist(p, &q_u, &r_u); + + if (!dfs_io_r_dfs_exist("", &r_u, rdata, 0)) + return False; + + return True; +} + +/***************************************************************** + api_dfs_add + *****************************************************************/ + +static BOOL api_dfs_add(pipes_struct *p) +{ + DFS_Q_DFS_ADD q_u; + DFS_R_DFS_ADD 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(!dfs_io_q_dfs_add("", &q_u, data, 0)) + return False; + + r_u.status = _dfs_add(p, &q_u, &r_u); + + if (!dfs_io_r_dfs_add("", &r_u, rdata, 0)) + return False; + + return True; +} + +/***************************************************************** + api_dfs_remove + *****************************************************************/ + +static BOOL api_dfs_remove(pipes_struct *p) +{ + DFS_Q_DFS_REMOVE q_u; + DFS_R_DFS_REMOVE 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(!dfs_io_q_dfs_remove("", &q_u, data, 0)) + return False; + + r_u.status = _dfs_remove(p, &q_u, &r_u); + + if (!dfs_io_r_dfs_remove("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_dfs_get_info + *******************************************************************/ + +static BOOL api_dfs_get_info(pipes_struct *p) +{ + DFS_Q_DFS_GET_INFO q_u; + DFS_R_DFS_GET_INFO 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(!dfs_io_q_dfs_get_info("", &q_u, data, 0)) + return False; + + r_u.status = _dfs_get_info(p, &q_u, &r_u); + + if(!dfs_io_r_dfs_get_info("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_dfs_enum + *******************************************************************/ + +static BOOL api_dfs_enum(pipes_struct *p) +{ + DFS_Q_DFS_ENUM q_u; + DFS_R_DFS_ENUM 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(!dfs_io_q_dfs_enum("", &q_u, data, 0)) + return False; + + r_u.status = _dfs_enum(p, &q_u, &r_u); + + if(!dfs_io_r_dfs_enum("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* +\pipe\netdfs commands +********************************************************************/ + +struct api_struct api_netdfs_cmds[] = +{ + {"DFS_EXIST", DFS_EXIST, api_dfs_exist }, + {"DFS_ADD", DFS_ADD, api_dfs_add }, + {"DFS_REMOVE", DFS_REMOVE, api_dfs_remove }, + {"DFS_GET_INFO", DFS_GET_INFO, api_dfs_get_info }, + {"DFS_ENUM", DFS_ENUM, api_dfs_enum }, + {NULL, 0, NULL } +}; + +/******************************************************************* +receives a netdfs pipe and responds. +********************************************************************/ + +BOOL api_netdfs_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_netdfs_rpc", api_netdfs_cmds); +} diff --git a/source3/rpc_server/srv_dfs_nt.c b/source3/rpc_server/srv_dfs_nt.c new file mode 100644 index 0000000000..4db6c61a3c --- /dev/null +++ b/source3/rpc_server/srv_dfs_nt.c @@ -0,0 +1,366 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines for Dfs + * Copyright (C) Andrew Tridgell 1992-1997, + * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, + * Copyright (C) Shirish Kalele 2000. + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the implementation of the dfs pipe. */ + +#include "includes.h" +#include "nterr.h" + +extern pstring global_myname; + +#define MAX_MSDFS_JUNCTIONS 256 + +/* This function does not return a WERROR or NTSTATUS code but rather 1 if + dfs exists, or 0 otherwise. */ + +uint32 _dfs_exist(pipes_struct *p, DFS_Q_DFS_EXIST *q_u, DFS_R_DFS_EXIST *r_u) +{ + if(lp_host_msdfs()) + return 1; + else + return 0; +} + +WERROR _dfs_add(pipes_struct *p, DFS_Q_DFS_ADD* q_u, DFS_R_DFS_ADD *r_u) +{ + struct current_user user; + struct junction_map jn; + struct referral* old_referral_list = NULL; + BOOL exists = False; + + pstring dfspath, servername, sharename; + pstring altpath; + + get_current_user(&user,p); + + if (user.uid != 0) { + DEBUG(10,("_dfs_add: uid != 0. Access denied.\n")); + return WERR_ACCESS_DENIED; + } + + unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1); + unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1); + unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1); + + DEBUG(5,("init_reply_dfs_add: Request to add %s -> %s\\%s.\n", + dfspath, servername, sharename)); + + pstrcpy(altpath, servername); + pstrcat(altpath, "\\"); + pstrcat(altpath, sharename); + + if(!create_junction(dfspath, &jn)) + return WERR_DFS_NO_SUCH_SERVER; + + if(get_referred_path(&jn)) + { + exists = True; + jn.referral_count += 1; + old_referral_list = jn.referral_list; + } + else + jn.referral_count = 1; + + jn.referral_list = (struct referral*) talloc(p->mem_ctx, jn.referral_count + * sizeof(struct referral)); + + 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) + { + memcpy(jn.referral_list, old_referral_list, + sizeof(struct referral)*jn.referral_count-1); + SAFE_FREE(old_referral_list); + } + + jn.referral_list[jn.referral_count-1].proximity = 0; + jn.referral_list[jn.referral_count-1].ttl = REFERRAL_TTL; + + pstrcpy(jn.referral_list[jn.referral_count-1].alternate_path, altpath); + + if(!create_msdfs_link(&jn, exists)) + return WERR_DFS_CANT_CREATE_JUNCT; + + return WERR_OK; +} + +WERROR _dfs_remove(pipes_struct *p, DFS_Q_DFS_REMOVE *q_u, + DFS_R_DFS_REMOVE *r_u) +{ + struct current_user user; + struct junction_map jn; + BOOL found = False; + + pstring dfspath, servername, sharename; + pstring altpath; + + get_current_user(&user,p); + + if (user.uid != 0) { + DEBUG(10,("_dfs_remove: uid != 0. Access denied.\n")); + return WERR_ACCESS_DENIED; + } + + unistr2_to_ascii(dfspath, &q_u->DfsEntryPath, sizeof(dfspath)-1); + if(q_u->ptr_ServerName) + unistr2_to_ascii(servername, &q_u->ServerName, sizeof(servername)-1); + + if(q_u->ptr_ShareName) + unistr2_to_ascii(sharename, &q_u->ShareName, sizeof(sharename)-1); + + if(q_u->ptr_ServerName && q_u->ptr_ShareName) + { + pstrcpy(altpath, servername); + pstrcat(altpath, "\\"); + pstrcat(altpath, sharename); + } + + DEBUG(5,("init_reply_dfs_remove: Request to remove %s -> %s\\%s.\n", + dfspath, servername, sharename)); + + if(!create_junction(dfspath, &jn)) + return WERR_DFS_NO_SUCH_SERVER; + + if(!get_referred_path(&jn)) + return WERR_DFS_NO_SUCH_VOL; + + /* if no server-share pair given, remove the msdfs link completely */ + if(!q_u->ptr_ServerName && !q_u->ptr_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 */ + for(i=0;i<jn.referral_count;i++) + { + pstring refpath; + pstrcpy(refpath,jn.referral_list[i].alternate_path); + trim_string(refpath, "\\", "\\"); + if(strequal(refpath, altpath)) + { + *(jn.referral_list[i].alternate_path)='\0'; + 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, True)) + return WERR_DFS_CANT_CREATE_JUNCT; + } + } + + return WERR_OK; +} + +static BOOL init_reply_dfs_info_1(struct junction_map* j, DFS_INFO_1* dfs1, int num_j) +{ + int i=0; + for(i=0;i<num_j;i++) + { + pstring str; + dfs1[i].ptr_entrypath = 1; + slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname, + j[i].service_name, j[i].volume_name); + DEBUG(5,("init_reply_dfs_info_1: %d) initing entrypath: %s\n",i,str)); + init_unistr2(&dfs1[i].entrypath,str,strlen(str)+1); + } + return True; +} + +static BOOL init_reply_dfs_info_2(struct junction_map* j, DFS_INFO_2* dfs2, int num_j) +{ + int i=0; + for(i=0;i<num_j;i++) + { + pstring str; + dfs2[i].ptr_entrypath = 1; + slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname, + j[i].service_name, j[i].volume_name); + init_unistr2(&dfs2[i].entrypath, str, strlen(str)+1); + dfs2[i].ptr_comment = 0; + dfs2[i].state = 1; /* set up state of dfs junction as OK */ + dfs2[i].num_storages = j[i].referral_count; + } + return True; +} + +static BOOL init_reply_dfs_info_3(TALLOC_CTX *ctx, struct junction_map* j, DFS_INFO_3* dfs3, int num_j) +{ + int i=0,ii=0; + for(i=0;i<num_j;i++) + { + pstring str; + dfs3[i].ptr_entrypath = 1; + slprintf(str, sizeof(pstring)-1, "\\\\%s\\%s\\%s", global_myname, + j[i].service_name, j[i].volume_name); + init_unistr2(&dfs3[i].entrypath, str, strlen(str)+1); + dfs3[i].ptr_comment = 1; + init_unistr2(&dfs3[i].comment, "", 1); + dfs3[i].state = 1; + dfs3[i].num_storages = dfs3[i].num_storage_infos = j[i].referral_count; + dfs3[i].ptr_storages = 1; + + /* also enumerate the storages */ + dfs3[i].storages = (DFS_STORAGE_INFO*) talloc(ctx, j[i].referral_count * + sizeof(DFS_STORAGE_INFO)); + if (!dfs3[i].storages) + return False; + + memset(dfs3[i].storages, '\0', j[i].referral_count * sizeof(DFS_STORAGE_INFO)); + + for(ii=0;ii<j[i].referral_count;ii++) + { + char* p; + pstring path; + DFS_STORAGE_INFO* stor = &(dfs3[i].storages[ii]); + struct referral* ref = &(j[i].referral_list[ii]); + + pstrcpy(path, ref->alternate_path); + trim_string(path,"\\",""); + 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 storages as ONLINE */ + init_unistr2(&stor->servername, path, strlen(path)+1); + init_unistr2(&stor->sharename, p+1, strlen(p+1)+1); + stor->ptr_servername = stor->ptr_sharename = 1; + } + } + return True; +} + +static WERROR init_reply_dfs_ctr(TALLOC_CTX *ctx, uint32 level, + DFS_INFO_CTR* ctr, struct junction_map* jn, + int num_jn) +{ + /* do the levels */ + switch(level) + { + case 1: + { + DFS_INFO_1* dfs1; + dfs1 = (DFS_INFO_1*) talloc(ctx, num_jn * sizeof(DFS_INFO_1)); + if (!dfs1) + return WERR_NOMEM; + init_reply_dfs_info_1(jn, dfs1, num_jn); + ctr->dfs.info1 = dfs1; + break; + } + case 2: + { + DFS_INFO_2* dfs2; + dfs2 = (DFS_INFO_2*) talloc(ctx, num_jn * sizeof(DFS_INFO_2)); + if (!dfs2) + return WERR_NOMEM; + init_reply_dfs_info_2(jn, dfs2, num_jn); + ctr->dfs.info2 = dfs2; + break; + } + case 3: + { + DFS_INFO_3* dfs3; + dfs3 = (DFS_INFO_3*) talloc(ctx, num_jn * sizeof(DFS_INFO_3)); + if (!dfs3) + return WERR_NOMEM; + init_reply_dfs_info_3(ctx, jn, dfs3, num_jn); + ctr->dfs.info3 = dfs3; + break; + } + default: + return WERR_INVALID_PARAM; + } + return WERR_OK; +} + +WERROR _dfs_enum(pipes_struct *p, DFS_Q_DFS_ENUM *q_u, DFS_R_DFS_ENUM *r_u) +{ + uint32 level = q_u->level; + struct junction_map jn[MAX_MSDFS_JUNCTIONS]; + int num_jn = 0; + + num_jn = enum_msdfs_links(jn); + + DEBUG(5,("make_reply_dfs_enum: %d junctions found in Dfs, doing level %d\n", num_jn, level)); + + r_u->ptr_buffer = level; + r_u->level = r_u->level2 = level; + r_u->ptr_num_entries = r_u->ptr_num_entries2 = 1; + r_u->num_entries = r_u->num_entries2 = num_jn; + r_u->reshnd.ptr_hnd = 1; + r_u->reshnd.handle = num_jn; + + r_u->ctr = (DFS_INFO_CTR*)talloc(p->mem_ctx, sizeof(DFS_INFO_CTR)); + if (!r_u->ctr) + return WERR_NOMEM; + ZERO_STRUCTP(r_u->ctr); + r_u->ctr->switch_value = level; + r_u->ctr->num_entries = num_jn; + r_u->ctr->ptr_dfs_ctr = 1; + + r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, r_u->ctr, jn, num_jn); + + return r_u->status; +} + +WERROR _dfs_get_info(pipes_struct *p, DFS_Q_DFS_GET_INFO *q_u, + DFS_R_DFS_GET_INFO *r_u) +{ + UNISTR2* uni_path = &q_u->uni_path; + uint32 level = q_u->level; + pstring path; + struct junction_map jn; + + unistr2_to_ascii(path, uni_path, sizeof(path)-1); + if(!create_junction(path, &jn)) + return WERR_DFS_NO_SUCH_SERVER; + + if(!get_referred_path(&jn)) + return WERR_DFS_NO_SUCH_VOL; + + r_u->level = level; + r_u->ptr_ctr = 1; + r_u->status = init_reply_dfs_ctr(p->mem_ctx, level, &r_u->ctr, &jn, 1); + + return r_u->status; +} diff --git a/source3/rpc_server/srv_lsa.c b/source3/rpc_server/srv_lsa.c new file mode 100644 index 0000000000..fcd4be0212 --- /dev/null +++ b/source3/rpc_server/srv_lsa.c @@ -0,0 +1,641 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the interface to the lsa server code. */ + +#include "includes.h" + +/*************************************************************************** + api_lsa_open_policy2 + ***************************************************************************/ + +static BOOL api_lsa_open_policy2(pipes_struct *p) +{ + LSA_Q_OPEN_POL2 q_u; + LSA_R_OPEN_POL2 r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the server, object attributes and desired access flag...*/ + if(!lsa_io_q_open_pol2("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_open_policy2: unable to unmarshall LSA_Q_OPEN_POL2.\n")); + return False; + } + + r_u.status = _lsa_open_policy2(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_open_pol2("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_open_policy2: unable to marshall LSA_R_OPEN_POL2.\n")); + return False; + } + + return True; +} + +/*************************************************************************** +api_lsa_open_policy + ***************************************************************************/ + +static BOOL api_lsa_open_policy(pipes_struct *p) +{ + LSA_Q_OPEN_POL q_u; + LSA_R_OPEN_POL r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the server, object attributes and desired access flag...*/ + if(!lsa_io_q_open_pol("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_open_policy: unable to unmarshall LSA_Q_OPEN_POL.\n")); + return False; + } + + r_u.status = _lsa_open_policy(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_open_pol("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_open_policy: unable to marshall LSA_R_OPEN_POL.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_enum_trust_dom + ***************************************************************************/ + +static BOOL api_lsa_enum_trust_dom(pipes_struct *p) +{ + LSA_Q_ENUM_TRUST_DOM q_u; + LSA_R_ENUM_TRUST_DOM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the enum trust domain context etc. */ + if(!lsa_io_q_enum_trust_dom("", &q_u, data, 0)) + return False; + + r_u.status = _lsa_enum_trust_dom(p, &q_u, &r_u); + + if(!lsa_io_r_enum_trust_dom("", &r_u, rdata, 0)) + return False; + + return True; +} + +/*************************************************************************** + api_lsa_query_info + ***************************************************************************/ + +static BOOL api_lsa_query_info(pipes_struct *p) +{ + LSA_Q_QUERY_INFO q_u; + LSA_R_QUERY_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the info class and policy handle */ + if(!lsa_io_q_query("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_query_info: failed to unmarshall LSA_Q_QUERY_INFO.\n")); + return False; + } + + r_u.status = _lsa_query_info(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_query("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_query_info: failed to marshall LSA_R_QUERY_INFO.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_lookup_sids + ***************************************************************************/ + +static BOOL api_lsa_lookup_sids(pipes_struct *p) +{ + LSA_Q_LOOKUP_SIDS q_u; + LSA_R_LOOKUP_SIDS r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the info class and policy handle */ + if(!lsa_io_q_lookup_sids("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_lookup_sids: failed to unmarshall LSA_Q_LOOKUP_SIDS.\n")); + return False; + } + + r_u.status = _lsa_lookup_sids(p, &q_u, &r_u); + + if(!lsa_io_r_lookup_sids("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_lookup_sids: Failed to marshall LSA_R_LOOKUP_SIDS.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_lookup_names + ***************************************************************************/ + +static BOOL api_lsa_lookup_names(pipes_struct *p) +{ + LSA_Q_LOOKUP_NAMES q_u; + LSA_R_LOOKUP_NAMES r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the info class and policy handle */ + if(!lsa_io_q_lookup_names("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_lookup_names: failed to unmarshall LSA_Q_LOOKUP_NAMES.\n")); + return False; + } + + r_u.status = _lsa_lookup_names(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_lookup_names("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_lookup_names: Failed to marshall LSA_R_LOOKUP_NAMES.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_close. + ***************************************************************************/ + +static BOOL api_lsa_close(pipes_struct *p) +{ + LSA_Q_CLOSE q_u; + LSA_R_CLOSE 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 (!lsa_io_q_close("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_close: lsa_io_q_close failed.\n")); + return False; + } + + r_u.status = _lsa_close(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if (!lsa_io_r_close("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_close: lsa_io_r_close failed.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_open_secret. + ***************************************************************************/ + +static BOOL api_lsa_open_secret(pipes_struct *p) +{ + LSA_Q_OPEN_SECRET q_u; + LSA_R_OPEN_SECRET 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(!lsa_io_q_open_secret("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_open_secret: failed to unmarshall LSA_Q_OPEN_SECRET.\n")); + return False; + } + + r_u.status = _lsa_open_secret(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_open_secret("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_open_secret: Failed to marshall LSA_R_OPEN_SECRET.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_open_secret. + ***************************************************************************/ + +static BOOL api_lsa_enum_privs(pipes_struct *p) +{ + LSA_Q_ENUM_PRIVS q_u; + LSA_R_ENUM_PRIVS 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(!lsa_io_q_enum_privs("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_enum_privs: failed to unmarshall LSA_Q_ENUM_PRIVS.\n")); + return False; + } + + r_u.status = _lsa_enum_privs(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_enum_privs("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_enum_privs: Failed to marshall LSA_R_ENUM_PRIVS.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_open_secret. + ***************************************************************************/ + +static BOOL api_lsa_priv_get_dispname(pipes_struct *p) +{ + LSA_Q_PRIV_GET_DISPNAME q_u; + LSA_R_PRIV_GET_DISPNAME 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(!lsa_io_q_priv_get_dispname("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_priv_get_dispname: failed to unmarshall LSA_Q_PRIV_GET_DISPNAME.\n")); + return False; + } + + r_u.status = _lsa_priv_get_dispname(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_priv_get_dispname("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_priv_get_dispname: Failed to marshall LSA_R_PRIV_GET_DISPNAME.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_open_secret. + ***************************************************************************/ + +static BOOL api_lsa_enum_accounts(pipes_struct *p) +{ + LSA_Q_ENUM_ACCOUNTS q_u; + LSA_R_ENUM_ACCOUNTS 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(!lsa_io_q_enum_accounts("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_enum_accounts: failed to unmarshall LSA_Q_ENUM_ACCOUNTS.\n")); + return False; + } + + r_u.status = _lsa_enum_accounts(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_enum_accounts("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_enum_accounts: Failed to marshall LSA_R_ENUM_ACCOUNTS.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_UNK_GET_CONNUSER + ***************************************************************************/ + +static BOOL api_lsa_unk_get_connuser(pipes_struct *p) +{ + LSA_Q_UNK_GET_CONNUSER q_u; + LSA_R_UNK_GET_CONNUSER 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(!lsa_io_q_unk_get_connuser("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_unk_get_connuser: failed to unmarshall LSA_Q_UNK_GET_CONNUSER.\n")); + return False; + } + + r_u.status = _lsa_unk_get_connuser(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_unk_get_connuser("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_unk_get_connuser: Failed to marshall LSA_R_UNK_GET_CONNUSER.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_open_user + ***************************************************************************/ + +static BOOL api_lsa_open_account(pipes_struct *p) +{ + LSA_Q_OPENACCOUNT q_u; + LSA_R_OPENACCOUNT 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(!lsa_io_q_open_account("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_open_account: failed to unmarshall LSA_Q_OPENACCOUNT.\n")); + return False; + } + + r_u.status = _lsa_open_account(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_open_account("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_open_account: Failed to marshall LSA_R_OPENACCOUNT.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_get_privs + ***************************************************************************/ + +static BOOL api_lsa_enum_privsaccount(pipes_struct *p) +{ + LSA_Q_ENUMPRIVSACCOUNT q_u; + LSA_R_ENUMPRIVSACCOUNT 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(!lsa_io_q_enum_privsaccount("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_enum_privsaccount: failed to unmarshall LSA_Q_ENUMPRIVSACCOUNT.\n")); + return False; + } + + r_u.status = _lsa_enum_privsaccount(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_enum_privsaccount("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_enum_privsaccount: Failed to marshall LSA_R_ENUMPRIVSACCOUNT.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_getsystemaccount + ***************************************************************************/ + +static BOOL api_lsa_getsystemaccount(pipes_struct *p) +{ + LSA_Q_GETSYSTEMACCOUNT q_u; + LSA_R_GETSYSTEMACCOUNT 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(!lsa_io_q_getsystemaccount("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_getsystemaccount: failed to unmarshall LSA_Q_GETSYSTEMACCOUNT.\n")); + return False; + } + + r_u.status = _lsa_getsystemaccount(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_getsystemaccount("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_getsystemaccount: Failed to marshall LSA_R_GETSYSTEMACCOUNT.\n")); + return False; + } + + return True; +} + + +/*************************************************************************** + api_lsa_setsystemaccount + ***************************************************************************/ + +static BOOL api_lsa_setsystemaccount(pipes_struct *p) +{ + LSA_Q_SETSYSTEMACCOUNT q_u; + LSA_R_SETSYSTEMACCOUNT 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(!lsa_io_q_setsystemaccount("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_setsystemaccount: failed to unmarshall LSA_Q_SETSYSTEMACCOUNT.\n")); + return False; + } + + r_u.status = _lsa_setsystemaccount(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_setsystemaccount("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_setsystemaccount: Failed to marshall LSA_R_SETSYSTEMACCOUNT.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_addprivs + ***************************************************************************/ + +static BOOL api_lsa_addprivs(pipes_struct *p) +{ + LSA_Q_ADDPRIVS q_u; + LSA_R_ADDPRIVS 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(!lsa_io_q_addprivs("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_addprivs: failed to unmarshall LSA_Q_ADDPRIVS.\n")); + return False; + } + + r_u.status = _lsa_addprivs(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_addprivs("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_addprivs: Failed to marshall LSA_R_ADDPRIVS.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_removeprivs + ***************************************************************************/ + +static BOOL api_lsa_removeprivs(pipes_struct *p) +{ + LSA_Q_REMOVEPRIVS q_u; + LSA_R_REMOVEPRIVS 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(!lsa_io_q_removeprivs("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_removeprivs: failed to unmarshall LSA_Q_REMOVEPRIVS.\n")); + return False; + } + + r_u.status = _lsa_removeprivs(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_removeprivs("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_removeprivs: Failed to marshall LSA_R_REMOVEPRIVS.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + api_lsa_query_secobj + ***************************************************************************/ + +static BOOL api_lsa_query_secobj(pipes_struct *p) +{ + LSA_Q_QUERY_SEC_OBJ q_u; + LSA_R_QUERY_SEC_OBJ 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(!lsa_io_q_query_sec_obj("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_query_secobj: failed to unmarshall LSA_Q_QUERY_SEC_OBJ.\n")); + return False; + } + + r_u.status = _lsa_query_secobj(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_query_sec_obj("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_query_secobj: Failed to marshall LSA_R_QUERY_SEC_OBJ.\n")); + return False; + } + + return True; +} + +/*************************************************************************** + \PIPE\ntlsa commands + ***************************************************************************/ + +static struct api_struct api_lsa_cmds[] = +{ + { "LSA_OPENPOLICY2" , LSA_OPENPOLICY2 , api_lsa_open_policy2 }, + { "LSA_OPENPOLICY" , LSA_OPENPOLICY , api_lsa_open_policy }, + { "LSA_QUERYINFOPOLICY" , LSA_QUERYINFOPOLICY , api_lsa_query_info }, + { "LSA_ENUMTRUSTDOM" , LSA_ENUMTRUSTDOM , api_lsa_enum_trust_dom }, + { "LSA_CLOSE" , LSA_CLOSE , api_lsa_close }, + { "LSA_OPENSECRET" , LSA_OPENSECRET , api_lsa_open_secret }, + { "LSA_LOOKUPSIDS" , LSA_LOOKUPSIDS , api_lsa_lookup_sids }, + { "LSA_LOOKUPNAMES" , LSA_LOOKUPNAMES , api_lsa_lookup_names }, + { "LSA_ENUM_PRIVS" , LSA_ENUM_PRIVS , api_lsa_enum_privs }, + { "LSA_PRIV_GET_DISPNAME",LSA_PRIV_GET_DISPNAME,api_lsa_priv_get_dispname}, + { "LSA_ENUM_ACCOUNTS" , LSA_ENUM_ACCOUNTS , api_lsa_enum_accounts }, + { "LSA_UNK_GET_CONNUSER", LSA_UNK_GET_CONNUSER, api_lsa_unk_get_connuser }, + { "LSA_OPENACCOUNT" , LSA_OPENACCOUNT , api_lsa_open_account }, + { "LSA_ENUMPRIVSACCOUNT", LSA_ENUMPRIVSACCOUNT, api_lsa_enum_privsaccount}, + { "LSA_GETSYSTEMACCOUNT", LSA_GETSYSTEMACCOUNT, api_lsa_getsystemaccount }, + { "LSA_SETSYSTEMACCOUNT", LSA_SETSYSTEMACCOUNT, api_lsa_setsystemaccount }, + { "LSA_ADDPRIVS" , LSA_ADDPRIVS , api_lsa_addprivs }, + { "LSA_REMOVEPRIVS" , LSA_REMOVEPRIVS , api_lsa_removeprivs }, + { "LSA_QUERYSECOBJ" , LSA_QUERYSECOBJ , api_lsa_query_secobj }, + { NULL , 0 , NULL } +}; + +/*************************************************************************** + api_ntLsarpcTNP + ***************************************************************************/ +BOOL api_ntlsa_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_ntlsa_rpc", api_lsa_cmds); +} diff --git a/source3/rpc_server/srv_lsa_hnd.c b/source3/rpc_server/srv_lsa_hnd.c new file mode 100644 index 0000000000..84c3c5a959 --- /dev/null +++ b/source3/rpc_server/srv_lsa_hnd.c @@ -0,0 +1,234 @@ +/* + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +/* 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, 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) { + pstring msg; + slprintf(msg, sizeof(msg)-1, "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 = (struct handle_list *)malloc(sizeof(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 = %u for pipe %s\n", + 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; + + 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 = (struct policy *)malloc(sizeof(*p)); + 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.data1, 0 , 0); /* first bit must be null */ + SIVAL(&pol->pol_hnd.data2, 0 , pol_hnd_low ); /* second bit is incrementing */ + SSVAL(&pol->pol_hnd.data3, 0 , pol_hnd_high); /* second bit is incrementing */ + SSVAL(&pol->pol_hnd.data4, 0 , (pol_hnd_high>>16)); /* second bit is incrementing */ + SIVAL(pol->pol_hnd.data5, 0, time(NULL)); /* something random */ + SIVAL(pol->pol_hnd.data5, 4, 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, (char *)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, (char *)hnd, sizeof(*hnd)); + if (data_p) + *data_p = pol->data_ptr; + return pol; + } + } + + DEBUG(4,("Policy not found: ")); + dump_data(4, (char *)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 )); + } +} diff --git a/source3/rpc_server/srv_lsa_nt.c b/source3/rpc_server/srv_lsa_nt.c new file mode 100644 index 0000000000..84ab44bc30 --- /dev/null +++ b/source3/rpc_server/srv_lsa_nt.c @@ -0,0 +1,1148 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the implementation of the lsa server code. */ + +#include "includes.h" + +extern DOM_SID global_sam_sid; +extern fstring global_myworkgroup; +extern pstring global_myname; +extern PRIVS privs[]; + +struct lsa_info { + DOM_SID sid; + uint32 access; +}; + +struct generic_mapping lsa_generic_mapping = { + POLICY_READ, + POLICY_WRITE, + POLICY_EXECUTE, + POLICY_ALL_ACCESS +}; + +/******************************************************************* + 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); +} + +/*************************************************************************** +Init dom_query + ***************************************************************************/ + +static void init_dom_query(DOM_QUERY *d_q, char *dom_name, DOM_SID *dom_sid) +{ + int domlen = (dom_name != NULL) ? strlen(dom_name) : 0; + + /* + * I'm not sure why this really odd combination of length + * values works, but it does appear to. I need to look at + * this *much* more closely - but at the moment leave alone + * until it's understood. This allows a W2k client to join + * a domain with both odd and even length names... JRA. + */ + + d_q->uni_dom_str_len = domlen ? ((domlen + 1) * 2) : 0; + d_q->uni_dom_max_len = domlen * 2; + d_q->buffer_dom_name = domlen != 0 ? 1 : 0; /* domain buffer pointer */ + d_q->buffer_dom_sid = dom_sid != NULL ? 1 : 0; /* domain sid pointer */ + + /* this string is supposed to be character short */ + init_unistr2(&d_q->uni_domain_name, dom_name, domlen); + d_q->uni_domain_name.uni_max_len++; + + if (dom_sid != NULL) + init_dom_sid2(&d_q->dom_sid, dom_sid); +} + +/*************************************************************************** + init_dom_ref - adds a domain if it's not already in, returns the index. +***************************************************************************/ + +static int init_dom_ref(DOM_R_REF *ref, char *dom_name, DOM_SID *dom_sid) +{ + int num = 0; + int len; + + if (dom_name != NULL) { + for (num = 0; num < ref->num_ref_doms_1; num++) { + fstring domname; + rpcstr_pull(domname, &ref->ref_dom[num].uni_dom_name, sizeof(domname), -1, 0); + if (strequal(domname, dom_name)) + return num; + } + } else { + num = ref->num_ref_doms_1; + } + + if (num >= MAX_REF_DOMAINS) { + /* index not found, already at maximum domain limit */ + return -1; + } + + ref->num_ref_doms_1 = num+1; + ref->ptr_ref_dom = 1; + ref->max_entries = MAX_REF_DOMAINS; + ref->num_ref_doms_2 = num+1; + + len = (dom_name != NULL) ? strlen(dom_name) : 0; + if(dom_name != NULL && len == 0) + len = 1; + + init_uni_hdr(&ref->hdr_ref_dom[num].hdr_dom_name, len); + ref->hdr_ref_dom[num].ptr_dom_sid = dom_sid != NULL ? 1 : 0; + + init_unistr2(&ref->ref_dom[num].uni_dom_name, dom_name, len); + init_dom_sid2(&ref->ref_dom[num].ref_dom, dom_sid ); + + return num; +} + +/*************************************************************************** + init_lsa_rid2s + ***************************************************************************/ + +static void init_lsa_rid2s(DOM_R_REF *ref, DOM_RID2 *rid2, + int num_entries, UNISTR2 *name, + uint32 *mapped_count, BOOL endian) +{ + int i; + int total = 0; + *mapped_count = 0; + + SMB_ASSERT(num_entries <= MAX_LOOKUP_SIDS); + + become_root(); /* lookup_name can require root privs */ + + for (i = 0; i < num_entries; i++) { + BOOL status = False; + DOM_SID sid; + uint32 rid = 0xffffffff; + int dom_idx = -1; + pstring full_name; + fstring dom_name, user; + enum SID_NAME_USE name_type = SID_NAME_UNKNOWN; + + /* Split name into domain and user component */ + + unistr2_to_ascii(full_name, &name[i], sizeof(full_name)); + split_domain_name(full_name, dom_name, user); + + /* Lookup name */ + + DEBUG(5, ("init_lsa_rid2s: looking up name %s\n", full_name)); + + status = lookup_name(dom_name, user, &sid, &name_type); + + DEBUG(5, ("init_lsa_rid2s: %s\n", status ? "found" : + "not found")); + + if (status) { + sid_split_rid(&sid, &rid); + dom_idx = init_dom_ref(ref, dom_name, &sid); + (*mapped_count)++; + } else { + dom_idx = -1; + rid = 0xffffffff; + name_type = SID_NAME_UNKNOWN; + } + + init_dom_rid2(&rid2[total], rid, name_type, dom_idx); + total++; + } + + unbecome_root(); +} + +/*************************************************************************** + init_reply_lookup_names + ***************************************************************************/ + +static void init_reply_lookup_names(LSA_R_LOOKUP_NAMES *r_l, + DOM_R_REF *ref, uint32 num_entries, + DOM_RID2 *rid2, uint32 mapped_count) +{ + r_l->ptr_dom_ref = 1; + r_l->dom_ref = ref; + + r_l->num_entries = num_entries; + r_l->ptr_entries = 1; + r_l->num_entries2 = num_entries; + r_l->dom_rid = rid2; + + r_l->mapped_count = mapped_count; + + if (mapped_count == 0) + r_l->status = NT_STATUS_NONE_MAPPED; + else + r_l->status = NT_STATUS_OK; +} + +/*************************************************************************** + Init lsa_trans_names. + ***************************************************************************/ + +static void init_lsa_trans_names(TALLOC_CTX *ctx, DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *trn, + int num_entries, DOM_SID2 *sid, + uint32 *mapped_count) +{ + int i; + int total = 0; + *mapped_count = 0; + + /* Allocate memory for list of names */ + + if (num_entries > 0) { + if (!(trn->name = (LSA_TRANS_NAME *)talloc(ctx, sizeof(LSA_TRANS_NAME) * + num_entries))) { + DEBUG(0, ("init_lsa_trans_names(): out of memory\n")); + return; + } + + if (!(trn->uni_name = (UNISTR2 *)talloc(ctx, sizeof(UNISTR2) * + num_entries))) { + DEBUG(0, ("init_lsa_trans_names(): out of memory\n")); + return; + } + } + + become_root(); /* Need root to get to passdb to for local sids */ + + for (i = 0; i < num_entries; i++) { + BOOL status = False; + DOM_SID find_sid = sid[i].sid; + uint32 rid = 0xffffffff; + int dom_idx = -1; + fstring name, dom_name; + enum SID_NAME_USE sid_name_use = (enum SID_NAME_USE)0; + + sid_to_string(name, &find_sid); + DEBUG(5, ("init_lsa_trans_names: looking up sid %s\n", name)); + + /* Lookup sid from winbindd */ + + memset(dom_name, '\0', sizeof(dom_name)); + memset(name, '\0', sizeof(name)); + + status = lookup_sid(&find_sid, dom_name, name, &sid_name_use); + + DEBUG(5, ("init_lsa_trans_names: %s\n", status ? "found" : + "not found")); + + if (!status) { + sid_name_use = SID_NAME_UNKNOWN; + } + + /* Store domain sid in ref array */ + + if (find_sid.num_auths == 5) { + sid_split_rid(&find_sid, &rid); + } + + dom_idx = init_dom_ref(ref, dom_name, &find_sid); + + DEBUG(10,("init_lsa_trans_names: added user '%s\\%s' to " + "referenced list.\n", dom_name, name )); + + (*mapped_count)++; + + init_lsa_trans_name(&trn->name[total], &trn->uni_name[total], + sid_name_use, name, dom_idx); + total++; + } + + unbecome_root(); + + trn->num_entries = total; + trn->ptr_trans_names = 1; + trn->num_entries2 = total; +} + +/*************************************************************************** + Init_reply_lookup_sids. + ***************************************************************************/ + +static void init_reply_lookup_sids(LSA_R_LOOKUP_SIDS *r_l, + DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *names, + uint32 mapped_count) +{ + r_l->ptr_dom_ref = 1; + r_l->dom_ref = ref; + r_l->names = names; + r_l->mapped_count = mapped_count; + + if (mapped_count == 0) + r_l->status = NT_STATUS_NONE_MAPPED; + else + r_l->status = NT_STATUS_OK; +} + +static NTSTATUS lsa_get_generic_sd(TALLOC_CTX *mem_ctx, SEC_DESC **sd, size_t *sd_size) +{ + extern DOM_SID global_sid_World; + extern DOM_SID global_sid_Builtin; + DOM_SID local_adm_sid; + DOM_SID adm_sid; + + SEC_ACE ace[3]; + SEC_ACCESS mask; + + SEC_ACL *psa = NULL; + + init_sec_access(&mask, POLICY_EXECUTE); + init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + sid_copy(&adm_sid, &global_sam_sid); + sid_append_rid(&adm_sid, DOMAIN_GROUP_RID_ADMINS); + init_sec_access(&mask, 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, 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, SEC_DESC_REVISION, &adm_sid, NULL, NULL, psa, sd_size)) == NULL) + return NT_STATUS_NO_MEMORY; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_open_policy2. + ***************************************************************************/ + +NTSTATUS _lsa_open_policy2(pipes_struct *p, LSA_Q_OPEN_POL2 *q_u, LSA_R_OPEN_POL2 *r_u) +{ + struct lsa_info *info; + SEC_DESC *psd = NULL; + size_t sd_size; + uint32 des_access=q_u->des_access; + 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)) + return status; + + /* associate the domain SID with the (unique) handle. */ + if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(info); + info->sid = global_sam_sid; + info->access = acc_granted; + + /* set up the LSA QUERY INFO response */ + if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_open_policy + ***************************************************************************/ + +NTSTATUS _lsa_open_policy(pipes_struct *p, LSA_Q_OPEN_POL *q_u, LSA_R_OPEN_POL *r_u) +{ + struct lsa_info *info; + SEC_DESC *psd = NULL; + size_t sd_size; + uint32 des_access=q_u->des_access; + 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)) + return status; + + /* associate the domain SID with the (unique) handle. */ + if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(info); + info->sid = global_sam_sid; + info->access = acc_granted; + + /* set up the LSA QUERY INFO response */ + if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return NT_STATUS_OK; +} + +/*************************************************************************** + _lsa_enum_trust_dom - this needs fixing to do more than return NULL ! JRA. + ***************************************************************************/ + +NTSTATUS _lsa_enum_trust_dom(pipes_struct *p, LSA_Q_ENUM_TRUST_DOM *q_u, LSA_R_ENUM_TRUST_DOM *r_u) +{ + struct lsa_info *info; + uint32 enum_context = 0; + char *dom_name = NULL; + DOM_SID *dom_sid = NULL; + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + /* check if the user have enough rights */ + if (!(info->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* set up the LSA QUERY INFO response */ + init_r_enum_trust_dom(p->mem_ctx, r_u, enum_context, dom_name, dom_sid, + dom_name != NULL ? NT_STATUS_OK : NT_STATUS_NO_MORE_ENTRIES); + + return r_u->status; +} + +/*************************************************************************** + _lsa_query_info. See the POLICY_INFOMATION_CLASS docs at msdn. + ***************************************************************************/ + +NTSTATUS _lsa_query_info(pipes_struct *p, LSA_Q_QUERY_INFO *q_u, LSA_R_QUERY_INFO *r_u) +{ + struct lsa_info *handle; + LSA_INFO_UNION *info = &r_u->dom; + DOM_SID domain_sid; + char *name = NULL; + DOM_SID *sid = NULL; + + r_u->status = NT_STATUS_OK; + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) + return NT_STATUS_INVALID_HANDLE; + + switch (q_u->info_class) { + case 0x02: + { + unsigned int i; + /* check if the user have enough rights */ + if (!(handle->access & POLICY_VIEW_AUDIT_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* fake info: We audit everything. ;) */ + info->id2.auditing_enabled = 1; + info->id2.count1 = 7; + info->id2.count2 = 7; + if ((info->id2.auditsettings = (uint32 *)talloc(p->mem_ctx,7*sizeof(uint32))) == NULL) + return NT_STATUS_NO_MEMORY; + for (i = 0; i < 7; i++) + info->id2.auditsettings[i] = 3; + break; + } + case 0x03: + /* check if the user have enough rights */ + if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* Request PolicyPrimaryDomainInformation. */ + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + name = global_myworkgroup; + sid = &global_sam_sid; + break; + case ROLE_DOMAIN_MEMBER: + name = global_myworkgroup; + /* We need to return the Domain SID here. */ + if (secrets_fetch_domain_sid(global_myworkgroup, + &domain_sid)) + sid = &domain_sid; + else + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + break; + case ROLE_STANDALONE: + name = global_myworkgroup; + sid = NULL; + break; + default: + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + init_dom_query(&r_u->dom.id3, name, sid); + break; + case 0x05: + /* check if the user have enough rights */ + if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* Request PolicyAccountDomainInformation. */ + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + name = global_myworkgroup; + sid = &global_sam_sid; + break; + case ROLE_DOMAIN_MEMBER: + name = global_myname; + sid = &global_sam_sid; + break; + case ROLE_STANDALONE: + name = global_myname; + sid = &global_sam_sid; + break; + default: + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + init_dom_query(&r_u->dom.id5, name, sid); + break; + case 0x06: + /* check if the user have enough rights */ + if (!(handle->access & 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->id6.server_role = 2; + break; + default: + /* + * any other role is a primary + * of the domain, it controls. + */ + info->id6.server_role = 3; + break; + } + break; + default: + DEBUG(0,("_lsa_query_info: 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->undoc_buffer = 0x22000000; /* bizarre */ + r_u->info_class = q_u->info_class; + } + + return r_u->status; +} + +/*************************************************************************** + _lsa_lookup_sids + ***************************************************************************/ + +NTSTATUS _lsa_lookup_sids(pipes_struct *p, LSA_Q_LOOKUP_SIDS *q_u, LSA_R_LOOKUP_SIDS *r_u) +{ + struct lsa_info *handle; + DOM_SID2 *sid = q_u->sids.sid; + int num_entries = q_u->sids.num_entries; + DOM_R_REF *ref = NULL; + LSA_TRANS_NAME_ENUM *names = NULL; + uint32 mapped_count = 0; + + ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF)); + names = (LSA_TRANS_NAME_ENUM *)talloc_zero(p->mem_ctx, sizeof(LSA_TRANS_NAME_ENUM)); + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) { + r_u->status = NT_STATUS_INVALID_HANDLE; + goto done; + } + + /* check if the user have enough rights */ + if (!(handle->access & POLICY_LOOKUP_NAMES)) { + r_u->status = NT_STATUS_ACCESS_DENIED; + goto done; + } + if (!ref || !names) + return NT_STATUS_NO_MEMORY; + +done: + + /* set up the LSA Lookup SIDs response */ + init_lsa_trans_names(p->mem_ctx, ref, names, num_entries, sid, &mapped_count); + init_reply_lookup_sids(r_u, ref, names, mapped_count); + + return r_u->status; +} + +/*************************************************************************** +lsa_reply_lookup_names + ***************************************************************************/ + +NTSTATUS _lsa_lookup_names(pipes_struct *p,LSA_Q_LOOKUP_NAMES *q_u, LSA_R_LOOKUP_NAMES *r_u) +{ + struct lsa_info *handle; + UNISTR2 *names = q_u->uni_name; + int num_entries = q_u->num_entries; + DOM_R_REF *ref; + DOM_RID2 *rids; + uint32 mapped_count = 0; + + if (num_entries > MAX_LOOKUP_SIDS) { + num_entries = MAX_LOOKUP_SIDS; + DEBUG(5,("_lsa_lookup_names: truncating name lookup list to %d\n", num_entries)); + } + + ref = (DOM_R_REF *)talloc_zero(p->mem_ctx, sizeof(DOM_R_REF)); + rids = (DOM_RID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_RID2)*num_entries); + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) { + r_u->status = NT_STATUS_INVALID_HANDLE; + goto done; + } + + /* check if the user have enough rights */ + if (!(handle->access & POLICY_LOOKUP_NAMES)) { + r_u->status = NT_STATUS_ACCESS_DENIED; + goto done; + } + + if (!ref || !rids) + return NT_STATUS_NO_MEMORY; + +done: + + /* set up the LSA Lookup RIDs response */ + init_lsa_rid2s(ref, rids, num_entries, names, &mapped_count, p->endian); + init_reply_lookup_names(r_u, ref, num_entries, rids, mapped_count); + + return r_u->status; +} + +/*************************************************************************** + _lsa_close. Also weird - needs to check if lsa handle is correct. JRA. + ***************************************************************************/ + +NTSTATUS _lsa_close(pipes_struct *p, LSA_Q_CLOSE *q_u, LSA_R_CLOSE *r_u) +{ + if (!find_policy_by_hnd(p, &q_u->pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + close_policy_hnd(p, &q_u->pol); + return NT_STATUS_OK; +} + +/*************************************************************************** + "No more secrets Marty...." :-). + ***************************************************************************/ + +NTSTATUS _lsa_open_secret(pipes_struct *p, LSA_Q_OPEN_SECRET *q_u, LSA_R_OPEN_SECRET *r_u) +{ + return NT_STATUS_OBJECT_NAME_NOT_FOUND; +} + +/*************************************************************************** +_lsa_enum_privs. + ***************************************************************************/ + +NTSTATUS _lsa_enum_privs(pipes_struct *p, LSA_Q_ENUM_PRIVS *q_u, LSA_R_ENUM_PRIVS *r_u) +{ + struct lsa_info *handle; + uint32 i; + + uint32 enum_context=q_u->enum_context; + LSA_PRIV_ENTRY *entry; + LSA_PRIV_ENTRY *entries=NULL; + + if (enum_context >= PRIV_ALL_INDEX) + return NT_STATUS_NO_MORE_ENTRIES; + + entries = (LSA_PRIV_ENTRY *)talloc_zero(p->mem_ctx, sizeof(LSA_PRIV_ENTRY) * (PRIV_ALL_INDEX)); + if (entries==NULL) + return NT_STATUS_NO_MEMORY; + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) + return NT_STATUS_INVALID_HANDLE; + + /* check if the user have enough rights */ + + /* + * I don't know if it's the right one. not documented. + */ + if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + entry = entries; + + DEBUG(10,("_lsa_enum_privs: enum_context:%d total entries:%d\n", enum_context, PRIV_ALL_INDEX)); + + for (i = 0; i < PRIV_ALL_INDEX; i++, entry++) { + if( i<enum_context) { + init_uni_hdr(&entry->hdr_name, 0); + init_unistr2(&entry->name, NULL, 0 ); + entry->luid_low = 0; + entry->luid_high = 0; + } else { + init_uni_hdr(&entry->hdr_name, strlen(privs[i+1].priv)); + init_unistr2(&entry->name, privs[i+1].priv, strlen(privs[i+1].priv) ); + entry->luid_low = privs[i+1].se_priv; + entry->luid_high = 0; + } + } + + enum_context = PRIV_ALL_INDEX; + init_lsa_r_enum_privs(r_u, enum_context, PRIV_ALL_INDEX, entries); + + return NT_STATUS_OK; +} + +/*************************************************************************** +_lsa_priv_get_dispname. + ***************************************************************************/ + +NTSTATUS _lsa_priv_get_dispname(pipes_struct *p, LSA_Q_PRIV_GET_DISPNAME *q_u, LSA_R_PRIV_GET_DISPNAME *r_u) +{ + struct lsa_info *handle; + fstring name_asc; + int i=1; + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) + return NT_STATUS_INVALID_HANDLE; + + /* check if the user have enough rights */ + + /* + * I don't know if it's the right one. not documented. + */ + if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + unistr2_to_ascii(name_asc, &q_u->name, sizeof(name_asc)); + + DEBUG(10,("_lsa_priv_get_dispname: %s", name_asc)); + + while (privs[i].se_priv!=SE_PRIV_ALL && strcmp(name_asc, privs[i].priv)) + i++; + + if (privs[i].se_priv!=SE_PRIV_ALL) { + DEBUG(10,(": %s\n", privs[i].description)); + init_uni_hdr(&r_u->hdr_desc, strlen(privs[i].description)); + init_unistr2(&r_u->desc, privs[i].description, strlen(privs[i].description) ); + + r_u->ptr_info=0xdeadbeef; + r_u->lang_id=q_u->lang_id; + return NT_STATUS_OK; + } else { + DEBUG(10,("_lsa_priv_get_dispname: doesn't exist\n")); + r_u->ptr_info=0; + return NT_STATUS_NO_SUCH_PRIVILEGE; + } +} + +/*************************************************************************** +_lsa_enum_accounts. + ***************************************************************************/ + +NTSTATUS _lsa_enum_accounts(pipes_struct *p, LSA_Q_ENUM_ACCOUNTS *q_u, LSA_R_ENUM_ACCOUNTS *r_u) +{ + struct lsa_info *handle; + GROUP_MAP *map=NULL; + int num_entries=0; + LSA_SID_ENUM *sids=&r_u->sids; + int i=0,j=0; + + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) + return NT_STATUS_INVALID_HANDLE; + + /* check if the user have enough rights */ + + /* + * I don't know if it's the right one. not documented. + */ + if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* get the list of mapped groups (domain, local, builtin) */ + if(!enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_OK; + + if (q_u->enum_context >= num_entries) + return NT_STATUS_NO_MORE_ENTRIES; + + sids->ptr_sid = (uint32 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(uint32)); + sids->sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, (num_entries-q_u->enum_context)*sizeof(DOM_SID2)); + + if (sids->ptr_sid==NULL || sids->sid==NULL) { + SAFE_FREE(map); + return NT_STATUS_NO_MEMORY; + } + + for (i=q_u->enum_context, j=0; i<num_entries; i++) { + init_dom_sid2( &(*sids).sid[j], &map[i].sid); + (*sids).ptr_sid[j]=1; + j++; + } + + SAFE_FREE(map); + + init_lsa_r_enum_accounts(r_u, j); + + return NT_STATUS_OK; +} + + +NTSTATUS _lsa_unk_get_connuser(pipes_struct *p, LSA_Q_UNK_GET_CONNUSER *q_u, LSA_R_UNK_GET_CONNUSER *r_u) +{ + fstring username, domname; + int ulen, dlen; + user_struct *vuser = get_valid_user_struct(p->vuid); + + if (vuser == NULL) + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + + fstrcpy(username, vuser->user.smb_name); + fstrcpy(domname, vuser->user.domain); + + ulen = strlen(username) + 1; + dlen = strlen(domname) + 1; + + init_uni_hdr(&r_u->hdr_user_name, ulen); + r_u->ptr_user_name = 1; + init_unistr2(&r_u->uni2_user_name, username, ulen); + + r_u->unk1 = 1; + + init_uni_hdr(&r_u->hdr_dom_name, dlen); + r_u->ptr_dom_name = 1; + init_unistr2(&r_u->uni2_dom_name, domname, dlen); + + r_u->status = NT_STATUS_OK; + + return r_u->status; +} + +/*************************************************************************** + + ***************************************************************************/ + +NTSTATUS _lsa_open_account(pipes_struct *p, LSA_Q_OPENACCOUNT *q_u, LSA_R_OPENACCOUNT *r_u) +{ + struct lsa_info *handle; + struct lsa_info *info; + + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) + return NT_STATUS_INVALID_HANDLE; + + /* check if the user have enough rights */ + + /* + * I don't know if it's the right one. not documented. + * but guessed with rpcclient. + */ + if (!(handle->access & POLICY_GET_PRIVATE_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + /* associate the user/group SID with the (unique) handle. */ + if ((info = (struct lsa_info *)malloc(sizeof(struct lsa_info))) == NULL) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(info); + info->sid = q_u->sid.sid; + info->access = q_u->access; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->pol, free_lsa_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return r_u->status; +} + +/*************************************************************************** + For a given SID, enumerate all the privilege this account has. + ***************************************************************************/ + +NTSTATUS _lsa_enum_privsaccount(pipes_struct *p, LSA_Q_ENUMPRIVSACCOUNT *q_u, LSA_R_ENUMPRIVSACCOUNT *r_u) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + int i=0; + + LUID_ATTR *set=NULL; + + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_group_map_from_sid(info->sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + DEBUG(10,("_lsa_enum_privsaccount: %d privileges\n", map.priv_set.count)); + if (map.priv_set.count!=0) { + + set=(LUID_ATTR *)talloc(p->mem_ctx, map.priv_set.count*sizeof(LUID_ATTR)); + if (set == NULL) { + free_privilege(&map.priv_set); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i<map.priv_set.count; i++) { + set[i].luid.low=map.priv_set.set[i].luid.low; + set[i].luid.high=map.priv_set.set[i].luid.high; + set[i].attr=map.priv_set.set[i].attr; + DEBUG(10,("_lsa_enum_privsaccount: priv %d: %d:%d:%d\n", i, + set[i].luid.high, set[i].luid.low, set[i].attr)); + } + } + + init_lsa_r_enum_privsaccount(r_u, set, map.priv_set.count, 0); + free_privilege(&map.priv_set); + + return r_u->status; +} + +/*************************************************************************** + + ***************************************************************************/ + +NTSTATUS _lsa_getsystemaccount(pipes_struct *p, LSA_Q_GETSYSTEMACCOUNT *q_u, LSA_R_GETSYSTEMACCOUNT *r_u) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_group_map_from_sid(info->sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + /* + 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_u->access=map.systemaccount; + + return r_u->status; +} + +/*************************************************************************** + update the systemaccount information + ***************************************************************************/ + +NTSTATUS _lsa_setsystemaccount(pipes_struct *p, LSA_Q_SETSYSTEMACCOUNT *q_u, LSA_R_SETSYSTEMACCOUNT *r_u) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_group_map_from_sid(info->sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + map.systemaccount=q_u->access; + + if(!add_mapping_entry(&map, TDB_REPLACE)) + return NT_STATUS_NO_SUCH_GROUP; + + free_privilege(&map.priv_set); + + return r_u->status; +} + +/*************************************************************************** + For a given SID, add some privileges. + ***************************************************************************/ + +NTSTATUS _lsa_addprivs(pipes_struct *p, LSA_Q_ADDPRIVS *q_u, LSA_R_ADDPRIVS *r_u) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + int i=0; + + LUID_ATTR *luid_attr=NULL; + PRIVILEGE_SET *set=NULL; + + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_group_map_from_sid(info->sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + set=&q_u->set; + + for (i=0; i<set->count; i++) { + luid_attr=&set->set[i]; + + /* check if the privilege is already there */ + if (check_priv_in_privilege(&map.priv_set, *luid_attr)){ + free_privilege(&map.priv_set); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + add_privilege(&map.priv_set, *luid_attr); + } + + if(!add_mapping_entry(&map, TDB_REPLACE)) + return NT_STATUS_NO_SUCH_GROUP; + + free_privilege(&map.priv_set); + + return r_u->status; +} + +/*************************************************************************** + For a given SID, remove some privileges. + ***************************************************************************/ + +NTSTATUS _lsa_removeprivs(pipes_struct *p, LSA_Q_REMOVEPRIVS *q_u, LSA_R_REMOVEPRIVS *r_u) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + int i=0; + + LUID_ATTR *luid_attr=NULL; + PRIVILEGE_SET *set=NULL; + + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_group_map_from_sid(info->sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + if (q_u->allrights!=0) { + /* log it and return, until I see one myself don't do anything */ + DEBUG(5,("_lsa_removeprivs: trying to remove all privileges ?\n")); + return NT_STATUS_OK; + } + + if (q_u->ptr==0) { + /* log it and return, until I see one myself don't do anything */ + DEBUG(5,("_lsa_removeprivs: no privileges to remove ?\n")); + return NT_STATUS_OK; + } + + set=&q_u->set; + + for (i=0; i<set->count; i++) { + luid_attr=&set->set[i]; + + /* if we don't have the privilege, we're trying to remove, give up */ + /* what else can we do ??? JFM. */ + if (!check_priv_in_privilege(&map.priv_set, *luid_attr)){ + free_privilege(&map.priv_set); + return NT_STATUS_NO_SUCH_PRIVILEGE; + } + + remove_privilege(&map.priv_set, *luid_attr); + } + + if(!add_mapping_entry(&map, TDB_REPLACE)) + return NT_STATUS_NO_SUCH_GROUP; + + free_privilege(&map.priv_set); + + return r_u->status; +} + +/*************************************************************************** + For a given SID, remove some privileges. + ***************************************************************************/ + +NTSTATUS _lsa_query_secobj(pipes_struct *p, LSA_Q_QUERY_SEC_OBJ *q_u, LSA_R_QUERY_SEC_OBJ *r_u) +{ + struct lsa_info *handle=NULL; + SEC_DESC *psd = NULL; + size_t sd_size; + NTSTATUS status; + + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&handle)) + return NT_STATUS_INVALID_HANDLE; + + /* check if the user have enough rights */ + if (!(handle->access & POLICY_VIEW_LOCAL_INFORMATION)) + return NT_STATUS_ACCESS_DENIED; + + + switch (q_u->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_u->buf = 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_u->buf = make_sec_desc_buf(p->mem_ctx, sd_size, psd)) == NULL) + return NT_STATUS_NO_MEMORY; + break; + default: + return NT_STATUS_INVALID_LEVEL; + } + + r_u->ptr=1; + + return r_u->status; +} + + diff --git a/source3/rpc_server/srv_netlog.c b/source3/rpc_server/srv_netlog.c new file mode 100644 index 0000000000..dfd270ff7d --- /dev/null +++ b/source3/rpc_server/srv_netlog.c @@ -0,0 +1,340 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the interface to the netlogon pipe. */ + +#include "includes.h" + +/************************************************************************* + api_net_req_chal: + *************************************************************************/ + +static BOOL api_net_req_chal(pipes_struct *p) +{ + NET_Q_REQ_CHAL q_u; + NET_R_REQ_CHAL r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the challenge... */ + if(!net_io_q_req_chal("", &q_u, data, 0)) { + DEBUG(0,("api_net_req_chal: Failed to unmarshall NET_Q_REQ_CHAL.\n")); + return False; + } + + r_u.status = _net_req_chal(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_req_chal("", &r_u, rdata, 0)) { + DEBUG(0,("api_net_req_chal: Failed to marshall NET_R_REQ_CHAL.\n")); + return False; + } + + return True; +} + +/************************************************************************* + api_net_auth: + *************************************************************************/ + +static BOOL api_net_auth(pipes_struct *p) +{ + NET_Q_AUTH q_u; + NET_R_AUTH r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the challenge... */ + if(!net_io_q_auth("", &q_u, data, 0)) { + DEBUG(0,("api_net_auth: Failed to unmarshall NET_Q_AUTH.\n")); + return False; + } + + r_u.status = _net_auth(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_auth("", &r_u, rdata, 0)) { + DEBUG(0,("api_net_auth: Failed to marshall NET_R_AUTH.\n")); + return False; + } + + return True; +} + +/************************************************************************* + api_net_auth_2: + *************************************************************************/ + +static BOOL api_net_auth_2(pipes_struct *p) +{ + NET_Q_AUTH_2 q_u; + NET_R_AUTH_2 r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the challenge... */ + if(!net_io_q_auth_2("", &q_u, data, 0)) { + DEBUG(0,("api_net_auth_2: Failed to unmarshall NET_Q_AUTH_2.\n")); + return False; + } + + r_u.status = _net_auth_2(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_auth_2("", &r_u, rdata, 0)) { + DEBUG(0,("api_net_auth_2: Failed to marshall NET_R_AUTH_2.\n")); + return False; + } + + return True; +} + +/************************************************************************* + api_net_srv_pwset: + *************************************************************************/ + +static BOOL api_net_srv_pwset(pipes_struct *p) +{ + NET_Q_SRV_PWSET q_u; + NET_R_SRV_PWSET r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the challenge and encrypted password ... */ + if(!net_io_q_srv_pwset("", &q_u, data, 0)) { + DEBUG(0,("api_net_srv_pwset: Failed to unmarshall NET_Q_SRV_PWSET.\n")); + return False; + } + + r_u.status = _net_srv_pwset(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_srv_pwset("", &r_u, rdata, 0)) { + DEBUG(0,("api_net_srv_pwset: Failed to marshall NET_R_SRV_PWSET.\n")); + return False; + } + + return True; +} + +/************************************************************************* + api_net_sam_logoff: + *************************************************************************/ + +static BOOL api_net_sam_logoff(pipes_struct *p) +{ + NET_Q_SAM_LOGOFF q_u; + NET_R_SAM_LOGOFF 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(!net_io_q_sam_logoff("", &q_u, data, 0)) { + DEBUG(0,("api_net_sam_logoff: Failed to unmarshall NET_Q_SAM_LOGOFF.\n")); + return False; + } + + r_u.status = _net_sam_logoff(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_sam_logoff("", &r_u, rdata, 0)) { + DEBUG(0,("api_net_sam_logoff: Failed to marshall NET_R_SAM_LOGOFF.\n")); + return False; + } + + return True; +} + +/************************************************************************* + api_net_sam_logon: + *************************************************************************/ + +static BOOL api_net_sam_logon(pipes_struct *p) +{ + NET_Q_SAM_LOGON q_u; + NET_R_SAM_LOGON 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(!net_io_q_sam_logon("", &q_u, data, 0)) { + DEBUG(0, ("api_net_sam_logon: Failed to unmarshall NET_Q_SAM_LOGON.\n")); + return False; + } + + r_u.status = _net_sam_logon(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_sam_logon("", &r_u, rdata, 0)) { + DEBUG(0,("api_net_sam_logon: Failed to marshall NET_R_SAM_LOGON.\n")); + return False; + } + + return True; +} + +/************************************************************************* + api_net_trust_dom_list: + *************************************************************************/ + +static BOOL api_net_trust_dom_list(pipes_struct *p) +{ + NET_Q_TRUST_DOM_LIST q_u; + NET_R_TRUST_DOM_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); + + DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__)); + + /* grab the lsa trusted domain list query... */ + if(!net_io_q_trust_dom("", &q_u, data, 0)) { + DEBUG(0,("api_net_trust_dom_list: Failed to unmarshall NET_Q_TRUST_DOM_LIST.\n")); + return False; + } + + /* construct reply. */ + r_u.status = _net_trust_dom_list(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!net_io_r_trust_dom("", &r_u, rdata, 0)) { + DEBUG(0,("net_reply_trust_dom_list: Failed to marshall NET_R_TRUST_DOM_LIST.\n")); + return False; + } + + DEBUG(6,("api_net_trust_dom_list: %d\n", __LINE__)); + + return True; +} + +/************************************************************************* + api_net_logon_ctrl2: + *************************************************************************/ + +static BOOL api_net_logon_ctrl2(pipes_struct *p) +{ + NET_Q_LOGON_CTRL2 q_u; + NET_R_LOGON_CTRL2 r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__)); + + /* grab the lsa netlogon ctrl2 query... */ + if(!net_io_q_logon_ctrl2("", &q_u, data, 0)) { + DEBUG(0,("api_net_logon_ctrl2: Failed to unmarshall NET_Q_LOGON_CTRL2.\n")); + return False; + } + + r_u.status = _net_logon_ctrl2(p, &q_u, &r_u); + + if(!net_io_r_logon_ctrl2("", &r_u, rdata, 0)) { + DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n")); + return False; + } + + DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__)); + + return True; +} + +/************************************************************************* + api_net_logon_ctrl: + *************************************************************************/ + +static BOOL api_net_logon_ctrl(pipes_struct *p) +{ + NET_Q_LOGON_CTRL q_u; + NET_R_LOGON_CTRL r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + DEBUG(6,("api_net_logon_ctrl: %d\n", __LINE__)); + + /* grab the lsa netlogon ctrl query... */ + if(!net_io_q_logon_ctrl("", &q_u, data, 0)) { + DEBUG(0,("api_net_logon_ctrl: Failed to unmarshall NET_Q_LOGON_CTRL.\n")); + return False; + } + + r_u.status = _net_logon_ctrl(p, &q_u, &r_u); + + if(!net_io_r_logon_ctrl("", &r_u, rdata, 0)) { + DEBUG(0,("net_reply_logon_ctrl2: Failed to marshall NET_R_LOGON_CTRL2.\n")); + return False; + } + + DEBUG(6,("api_net_logon_ctrl2: %d\n", __LINE__)); + + return True; +} + +/******************************************************************* + array of \PIPE\NETLOGON operations + ********************************************************************/ +static struct api_struct api_net_cmds [] = +{ + { "NET_REQCHAL" , NET_REQCHAL , api_net_req_chal }, + { "NET_AUTH" , NET_AUTH , api_net_auth }, + { "NET_AUTH2" , NET_AUTH2 , api_net_auth_2 }, + { "NET_SRVPWSET" , NET_SRVPWSET , api_net_srv_pwset }, + { "NET_SAMLOGON" , NET_SAMLOGON , api_net_sam_logon }, + { "NET_SAMLOGOFF" , NET_SAMLOGOFF , api_net_sam_logoff }, + { "NET_LOGON_CTRL2" , NET_LOGON_CTRL2 , api_net_logon_ctrl2 }, + { "NET_TRUST_DOM_LIST", NET_TRUST_DOM_LIST, api_net_trust_dom_list }, + { "NET_LOGON_CTRL" , NET_LOGON_CTRL , api_net_logon_ctrl }, + { NULL , 0 , NULL } +}; + +/******************************************************************* + receives a netlogon pipe and responds. + ********************************************************************/ + +BOOL api_netlog_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_netlog_rpc", api_net_cmds); +} diff --git a/source3/rpc_server/srv_netlog_nt.c b/source3/rpc_server/srv_netlog_nt.c new file mode 100644 index 0000000000..bdb064c81d --- /dev/null +++ b/source3/rpc_server/srv_netlog_nt.c @@ -0,0 +1,715 @@ +/* + * 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. + * Copyirht (C) Andrew Bartlett 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the implementation of the netlogon pipe. */ + +#include "includes.h" + +extern pstring global_myname; +extern DOM_SID global_sam_sid; + +/************************************************************************* + init_net_r_req_chal: + *************************************************************************/ + +static void init_net_r_req_chal(NET_R_REQ_CHAL *r_c, + DOM_CHAL *srv_chal, NTSTATUS status) +{ + DEBUG(6,("init_net_r_req_chal: %d\n", __LINE__)); + memcpy(r_c->srv_chal.data, srv_chal->data, sizeof(srv_chal->data)); + r_c->status = status; +} + +/************************************************************************* + error messages cropping up when using nltest.exe... + *************************************************************************/ + +#define ERROR_NO_SUCH_DOMAIN 0x54b +#define ERROR_NO_LOGON_SERVERS 0x51f + +/************************************************************************* + net_reply_logon_ctrl: + *************************************************************************/ + +/* Some flag values reverse engineered from NLTEST.EXE */ + +#define LOGON_CTRL_IN_SYNC 0x00 +#define LOGON_CTRL_REPL_NEEDED 0x01 +#define LOGON_CTRL_REPL_IN_PROGRESS 0x02 + +NTSTATUS _net_logon_ctrl(pipes_struct *p, NET_Q_LOGON_CTRL *q_u, + NET_R_LOGON_CTRL *r_u) +{ + uint32 flags = 0x0; + uint32 pdc_connection_status = 0x00; /* Maybe a win32 error code? */ + + /* Setup the Logon Control response */ + + init_net_r_logon_ctrl(r_u, q_u->query_level, flags, + pdc_connection_status); + + return r_u->status; +} + +/**************************************************************************** +Send a message to smbd to do a sam synchronisation +**************************************************************************/ +static void send_sync_message(void) +{ + TDB_CONTEXT *tdb; + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, + TDB_DEFAULT, O_RDONLY, 0); + + if (!tdb) { + DEBUG(3, ("send_sync_message(): failed to open connections " + "database\n")); + return; + } + + DEBUG(3, ("sending sam synchronisation message\n")); + + message_send_all(tdb, MSG_SMB_SAM_SYNC, NULL, 0, False, NULL); + + tdb_close(tdb); +} + +/************************************************************************* + net_reply_logon_ctrl2: + *************************************************************************/ + +NTSTATUS _net_logon_ctrl2(pipes_struct *p, NET_Q_LOGON_CTRL2 *q_u, NET_R_LOGON_CTRL2 *r_u) +{ + uint32 flags = 0x0; + uint32 pdc_connection_status = 0x0; + uint32 logon_attempts = 0x0; + uint32 tc_status = ERROR_NO_LOGON_SERVERS; + char *trusted_domain = "test_domain"; + + DEBUG(0, ("*** net long ctrl2 %d, %d, %d\n", + q_u->function_code, q_u->query_level, q_u->switch_value)); + + DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__)); + + + /* set up the Logon Control2 response */ + init_net_r_logon_ctrl2(r_u, q_u->query_level, + flags, pdc_connection_status, logon_attempts, + tc_status, trusted_domain); + + if (lp_server_role() == ROLE_DOMAIN_BDC) + send_sync_message(); + + DEBUG(6,("_net_logon_ctrl2: %d\n", __LINE__)); + + return r_u->status; +} + +/************************************************************************* + net_reply_trust_dom_list: + *************************************************************************/ + +NTSTATUS _net_trust_dom_list(pipes_struct *p, NET_Q_TRUST_DOM_LIST *q_u, NET_R_TRUST_DOM_LIST *r_u) +{ + char *trusted_domain = "test_domain"; + uint32 num_trust_domains = 1; + + DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__)); + + /* set up the Trusted Domain List response */ + init_r_trust_dom(r_u, num_trust_domains, trusted_domain); + + DEBUG(6,("_net_trust_dom_list: %d\n", __LINE__)); + + return r_u->status; +} + +/*********************************************************************************** + init_net_r_srv_pwset: + ***********************************************************************************/ + +static void init_net_r_srv_pwset(NET_R_SRV_PWSET *r_s, + DOM_CRED *srv_cred, NTSTATUS status) +{ + DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__)); + + memcpy(&r_s->srv_cred, srv_cred, sizeof(r_s->srv_cred)); + r_s->status = status; + + DEBUG(5,("init_net_r_srv_pwset: %d\n", __LINE__)); +} + +/****************************************************************** + gets a machine password entry. checks access rights of the host. + ******************************************************************/ + +static BOOL get_md4pw(char *md4pw, char *mach_acct) +{ + SAM_ACCOUNT *sampass = NULL; + const uint8 *pass; + BOOL ret; + uint32 acct_ctrl; + +#if 0 + /* + * 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(), client_addr())) + { + DEBUG(0,("get_md4pw: Workstation %s denied access to domain\n", mach_acct)); + return False; + } +#endif /* 0 */ + + if(!NT_STATUS_IS_OK(pdb_init_sam(&sampass))) + return False; + + /* 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==False) { + DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct)); + pdb_free_sam(&sampass); + return False; + } + + acct_ctrl = pdb_get_acct_ctrl(sampass); + if (!(acct_ctrl & ACB_DISABLED) && + ((acct_ctrl & ACB_DOMTRUST) || + (acct_ctrl & ACB_WSTRUST) || + (acct_ctrl & ACB_SVRTRUST)) && + ((pass=pdb_get_nt_passwd(sampass)) != NULL)) { + memcpy(md4pw, pass, 16); + dump_data(5, md4pw, 16); + pdb_free_sam(&sampass); + return True; + } + + DEBUG(0,("get_md4pw: Workstation %s: no account in domain\n", mach_acct)); + pdb_free_sam(&sampass); + return False; + +} + +/************************************************************************* + _net_req_chal + *************************************************************************/ + +NTSTATUS _net_req_chal(pipes_struct *p, NET_Q_REQ_CHAL *q_u, NET_R_REQ_CHAL *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + + rpcstr_pull(p->dc.remote_machine,q_u->uni_logon_clnt.buffer,sizeof(fstring),q_u->uni_logon_clnt.uni_str_len*2,0); + + /* create a server challenge for the client */ + /* Set these to random values. */ + generate_random_buffer(p->dc.srv_chal.data, 8, False); + + memcpy(p->dc.srv_cred.challenge.data, p->dc.srv_chal.data, 8); + + memcpy(p->dc.clnt_chal.data , q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data)); + memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data)); + + memset((char *)p->dc.sess_key, '\0', sizeof(p->dc.sess_key)); + + p->dc.challenge_sent = True; + /* set up the LSA REQUEST CHALLENGE response */ + init_net_r_req_chal(r_u, &p->dc.srv_chal, status); + + return status; +} + +/************************************************************************* + init_net_r_auth: + *************************************************************************/ + +static void init_net_r_auth(NET_R_AUTH *r_a, DOM_CHAL *resp_cred, NTSTATUS status) +{ + memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data)); + r_a->status = status; +} + +/************************************************************************* + _net_auth + *************************************************************************/ + +NTSTATUS _net_auth(pipes_struct *p, NET_Q_AUTH *q_u, NET_R_AUTH *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + DOM_CHAL srv_cred; + UTIME srv_time; + fstring mach_acct; + + srv_time.time = 0; + + rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0); + + if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) { + + /* from client / server challenges and md4 password, generate sess key */ + cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal, + (char *)p->dc.md4pw, p->dc.sess_key); + + /* check that the client credentials are valid */ + if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) { + + /* create server challenge for inclusion in the reply */ + cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred); + + /* copy the received client credentials for use next time */ + memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data)); + memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data)); + + /* Save the machine account name. */ + fstrcpy(p->dc.mach_acct, mach_acct); + + p->dc.authenticated = True; + + } else { + status = NT_STATUS_ACCESS_DENIED; + } + } else { + status = NT_STATUS_ACCESS_DENIED; + } + + /* set up the LSA AUTH response */ + init_net_r_auth(r_u, &srv_cred, status); + + return r_u->status; +} + +/************************************************************************* + init_net_r_auth_2: + *************************************************************************/ + +static void init_net_r_auth_2(NET_R_AUTH_2 *r_a, + DOM_CHAL *resp_cred, NEG_FLAGS *flgs, NTSTATUS status) +{ + memcpy(r_a->srv_chal.data, resp_cred->data, sizeof(resp_cred->data)); + memcpy(&r_a->srv_flgs, flgs, sizeof(r_a->srv_flgs)); + r_a->status = status; +} + +/************************************************************************* + _net_auth_2 + *************************************************************************/ + +NTSTATUS _net_auth_2(pipes_struct *p, NET_Q_AUTH_2 *q_u, NET_R_AUTH_2 *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + DOM_CHAL srv_cred; + UTIME srv_time; + NEG_FLAGS srv_flgs; + fstring mach_acct; + + srv_time.time = 0; + + rpcstr_pull(mach_acct, q_u->clnt_id.uni_acct_name.buffer,sizeof(fstring),q_u->clnt_id.uni_acct_name.uni_str_len*2,0); + + if (p->dc.challenge_sent && get_md4pw((char *)p->dc.md4pw, mach_acct)) { + + /* from client / server challenges and md4 password, generate sess key */ + cred_session_key(&p->dc.clnt_chal, &p->dc.srv_chal, + (char *)p->dc.md4pw, p->dc.sess_key); + + /* check that the client credentials are valid */ + if (cred_assert(&q_u->clnt_chal, p->dc.sess_key, &p->dc.clnt_cred.challenge, srv_time)) { + + /* create server challenge for inclusion in the reply */ + cred_create(p->dc.sess_key, &p->dc.srv_cred.challenge, srv_time, &srv_cred); + + /* copy the received client credentials for use next time */ + memcpy(p->dc.clnt_cred.challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data)); + memcpy(p->dc.srv_cred .challenge.data, q_u->clnt_chal.data, sizeof(q_u->clnt_chal.data)); + + /* Save the machine account name. */ + fstrcpy(p->dc.mach_acct, mach_acct); + + p->dc.authenticated = True; + + } else { + status = NT_STATUS_ACCESS_DENIED; + } + } else { + status = NT_STATUS_ACCESS_DENIED; + } + + srv_flgs.neg_flags = 0x000001ff; + + /* set up the LSA AUTH 2 response */ + init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status); + + return r_u->status; +} + +/************************************************************************* + _net_srv_pwset + *************************************************************************/ + +NTSTATUS _net_srv_pwset(pipes_struct *p, NET_Q_SRV_PWSET *q_u, NET_R_SRV_PWSET *r_u) +{ + NTSTATUS status = NT_STATUS_WRONG_PASSWORD; + DOM_CRED srv_cred; + pstring workstation; + SAM_ACCOUNT *sampass=NULL; + BOOL ret = False; + unsigned char pwd[16]; + int i; + uint32 acct_ctrl; + + /* checks and updates credentials. creates reply credentials */ + if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->clnt_id.cred, &srv_cred))) + return NT_STATUS_INVALID_HANDLE; + + memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred)); + + DEBUG(5,("_net_srv_pwset: %d\n", __LINE__)); + + rpcstr_pull(workstation,q_u->clnt_id.login.uni_acct_name.buffer, + sizeof(workstation),q_u->clnt_id.login.uni_acct_name.uni_str_len*2,0); + + DEBUG(3,("Server Password Set by Wksta:[%s] on account [%s]\n", workstation, p->dc.mach_acct)); + + pdb_init_sam(&sampass); + + become_root(); + ret=pdb_getsampwnam(sampass, p->dc.mach_acct); + unbecome_root(); + + /* Ensure the account exists and is a machine account. */ + + acct_ctrl = pdb_get_acct_ctrl(sampass); + + if (!(ret + && (acct_ctrl & ACB_WSTRUST || + acct_ctrl & ACB_SVRTRUST || + acct_ctrl & ACB_DOMTRUST))) { + pdb_free_sam(&sampass); + return NT_STATUS_NO_SUCH_USER; + } + + if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED) { + pdb_free_sam(&sampass); + return NT_STATUS_ACCOUNT_DISABLED; + } + + DEBUG(100,("Server password set : new given value was :\n")); + for(i = 0; i < 16; i++) + DEBUG(100,("%02X ", q_u->pwd[i])); + DEBUG(100,("\n")); + + cred_hash3( pwd, q_u->pwd, p->dc.sess_key, 0); + + /* lies! nt and lm passwords are _not_ the same: don't care */ + if (!pdb_set_lanman_passwd (sampass, pwd)) { + pdb_free_sam(&sampass); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_nt_passwd (sampass, pwd)) { + pdb_free_sam(&sampass); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_changed_now (sampass)) { + pdb_free_sam(&sampass); + /* Not quite sure what this one qualifies as, but this will do */ + return NT_STATUS_UNSUCCESSFUL; + } + + become_root(); + ret = pdb_update_sam_account (sampass); + unbecome_root(); + + if (ret) + status = NT_STATUS_OK; + + /* set up the LSA Server Password Set response */ + init_net_r_srv_pwset(r_u, &srv_cred, status); + + pdb_free_sam(&sampass); + return r_u->status; +} + + +/************************************************************************* + _net_sam_logoff: + *************************************************************************/ + +NTSTATUS _net_sam_logoff(pipes_struct *p, NET_Q_SAM_LOGOFF *q_u, NET_R_SAM_LOGOFF *r_u) +{ + DOM_CRED srv_cred; + + if (!get_valid_user_struct(p->vuid)) + return NT_STATUS_NO_SUCH_USER; + + /* checks and updates credentials. creates reply credentials */ + if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, + &q_u->sam_id.client.cred, &srv_cred))) + return NT_STATUS_INVALID_HANDLE; + + memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred)); + + /* XXXX maybe we want to say 'no', reject the client's credentials */ + r_u->buffer_creds = 1; /* yes, we have valid server credentials */ + memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds)); + + r_u->status = NT_STATUS_OK; + + return r_u->status; +} + + +/************************************************************************* + _net_sam_logon + *************************************************************************/ + +NTSTATUS _net_sam_logon(pipes_struct *p, NET_Q_SAM_LOGON *q_u, NET_R_SAM_LOGON *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + NET_USER_INFO_3 *usr_info = NULL; + NET_ID_INFO_CTR *ctr = q_u->sam_id.ctr; + DOM_CRED srv_cred; + UNISTR2 *uni_samlogon_user = NULL; + UNISTR2 *uni_samlogon_domain = NULL; + UNISTR2 *uni_samlogon_workstation = NULL; + fstring nt_username, nt_domain, nt_workstation; + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + extern userdom_struct current_user_info; + SAM_ACCOUNT *sampw; + + usr_info = (NET_USER_INFO_3 *)talloc(p->mem_ctx, sizeof(NET_USER_INFO_3)); + if (!usr_info) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(usr_info); + + /* store the user information, if there is any. */ + r_u->user = usr_info; + r_u->switch_value = 0; /* indicates no info */ + r_u->auth_resp = 1; /* authoritative response */ + r_u->switch_value = 3; /* indicates type of validation user info */ + + if (!get_valid_user_struct(p->vuid)) + return NT_STATUS_NO_SUCH_USER; + + /* checks and updates credentials. creates reply credentials */ + if (!(p->dc.authenticated && deal_with_creds(p->dc.sess_key, &p->dc.clnt_cred, &q_u->sam_id.client.cred, &srv_cred))) + return NT_STATUS_INVALID_HANDLE; + + memcpy(&p->dc.srv_cred, &p->dc.clnt_cred, sizeof(p->dc.clnt_cred)); + + r_u->buffer_creds = 1; /* yes, we have valid server credentials */ + memcpy(&r_u->srv_creds, &srv_cred, sizeof(r_u->srv_creds)); + + /* find the username */ + + switch (q_u->sam_id.logon_level) { + case INTERACTIVE_LOGON_TYPE: + uni_samlogon_user = &ctr->auth.id1.uni_user_name; + uni_samlogon_domain = &ctr->auth.id1.uni_domain_name; + + uni_samlogon_workstation = &ctr->auth.id1.uni_wksta_name; + + DEBUG(3,("SAM Logon (Interactive). Domain:[%s]. ", lp_workgroup())); + break; + case NET_LOGON_TYPE: + uni_samlogon_user = &ctr->auth.id2.uni_user_name; + uni_samlogon_domain = &ctr->auth.id2.uni_domain_name; + uni_samlogon_workstation = &ctr->auth.id2.uni_wksta_name; + + 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 */ + + /* check username exists */ + + rpcstr_pull(nt_username,uni_samlogon_user->buffer,sizeof(nt_username),uni_samlogon_user->uni_str_len*2,0); + rpcstr_pull(nt_domain,uni_samlogon_domain->buffer,sizeof(nt_domain),uni_samlogon_domain->uni_str_len*2,0); + rpcstr_pull(nt_workstation,uni_samlogon_workstation->buffer,sizeof(nt_workstation),uni_samlogon_workstation->uni_str_len*2,0); + + DEBUG(3,("User:[%s@%s] Requested Domain:[%s]\n", nt_username, + nt_workstation, nt_domain)); + + pstrcpy(current_user_info.smb_name, nt_username); + + /* + * Convert to a UNIX username. + */ + + DEBUG(5,("Attempting validation level %d for unmapped username %s.\n", q_u->sam_id.ctr->switch_value, nt_username)); + + switch (ctr->switch_value) { + case NET_LOGON_TYPE: + { + struct auth_context *auth_context = NULL; + if (!NT_STATUS_IS_OK(status = make_auth_context_fixed(&auth_context, ctr->auth.id2.lm_chal))) { + return status; + } + + /* Standard challenge/response authenticaion */ + if (!make_user_info_netlogon_network(&user_info, + nt_username, nt_domain, + nt_workstation, + ctr->auth.id2.lm_chal_resp.buffer, + ctr->auth.id2.lm_chal_resp.str_str_len, + ctr->auth.id2.nt_chal_resp.buffer, + ctr->auth.id2.nt_chal_resp.str_str_len)) { + status = NT_STATUS_NO_MEMORY; + } else { + status = auth_context->check_ntlm_password(auth_context, user_info, &server_info); + } + (auth_context->free)(&auth_context); + + break; + } + case INTERACTIVE_LOGON_TYPE: + /* 'Interactive' autheticaion, supplies the password in its + MD4 form, encrypted with the session key. We will + convert this to chellange/responce for the auth + subsystem to chew on */ + { + struct auth_context *auth_context = NULL; + 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, chal, + ctr->auth.id1.lm_owf.data, + ctr->auth.id1.nt_owf.data, + p->dc.sess_key)) { + status = NT_STATUS_NO_MEMORY; + } else { + status = auth_context->check_ntlm_password(auth_context, user_info, &server_info); + } + + (auth_context->free)(&auth_context); + + break; + } + default: + DEBUG(2,("SAM Logon: unsupported switch value\n")); + return NT_STATUS_INVALID_INFO_CLASS; + } /* end switch */ + + free_user_info(&user_info); + + DEBUG(5, ("_net_sam_logon: check_password returned status %s\n", + nt_errstr(status))); + + /* Check account and password */ + + if (!NT_STATUS_IS_OK(status)) { + free_server_info(&server_info); + return status; + } + + if (server_info->guest) { + /* We don't like guest domain logons... */ + DEBUG(5,("_net_sam_logon: Attempted domain logon as GUEST denied.\n")); + free_server_info(&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. */ + + { + DOM_GID *gids = NULL; + int num_gids = 0; + pstring my_name; + pstring my_workgroup; + + /* set up pointer indicating user/password failed to be found */ + usr_info->ptr_user_info = 0; + + pstrcpy(my_workgroup, lp_workgroup()); + pstrcpy(my_name, global_myname); + strupper(my_name); + + /* + * This is the point at which we get the group + * database - we should be getting the gid_t list + * from /etc/group and then turning the uids into + * rids and then into machine sids for this user. + * JRA. + */ + + gids = NULL; + get_domain_user_groups(p->mem_ctx, &num_gids, &gids, server_info->sam_account); + + sampw = server_info->sam_account; + + init_net_user_info3(p->mem_ctx, usr_info, + pdb_get_user_rid(sampw), + pdb_get_group_rid(sampw), + + pdb_get_username(sampw), + pdb_get_fullname(sampw), + pdb_get_homedir(sampw), + pdb_get_dirdrive(sampw), + pdb_get_logon_script(sampw), + pdb_get_profile_path(sampw), + pdb_get_logon_time(sampw), + pdb_get_logoff_time(sampw), + pdb_get_kickoff_time(sampw), + pdb_get_pass_last_set_time(sampw), + pdb_get_pass_can_change_time(sampw), + pdb_get_pass_must_change_time(sampw), + + 0, /* logon_count */ + 0, /* bad_pw_count */ + num_gids, /* uint32 num_groups */ + gids , /* DOM_GID *gids */ + 0x20 , /* uint32 user_flgs (?) */ + NULL, /* uchar sess_key[16] */ + my_name , /* char *logon_srv */ + my_workgroup, /* char *logon_dom */ + &global_sam_sid, /* DOM_SID *dom_sid */ + NULL); /* char *other_sids */ + } + free_server_info(&server_info); + return status; +} + + diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c new file mode 100644 index 0000000000..70574b4cdd --- /dev/null +++ b/source3/rpc_server/srv_pipe.c @@ -0,0 +1,1225 @@ +/* + * 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) Jeremy Allison 1999. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* 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" + +static void NTLMSSPcalc_p( pipes_struct *p, unsigned char *data, int len) +{ + unsigned char *hash = p->ntlmssp_hash; + unsigned char index_i = hash[256]; + unsigned char index_j = hash[257]; + int ind; + + for( ind = 0; ind < len; ind++) { + unsigned char tc; + unsigned char t; + + index_i++; + index_j += hash[index_i]; + + tc = hash[index_i]; + hash[index_i] = hash[index_j]; + hash[index_j] = tc; + + t = hash[index_i] + hash[index_j]; + data[ind] = data[ind] ^ hash[t]; + } + + hash[256] = index_i; + hash[257] = index_j; +} + +/******************************************************************* + Generate the next PDU to be returned from the data in p->rdata. + We cheat here as this function doesn't handle the special auth + footers of the authenticated bind response reply. + ********************************************************************/ + +BOOL create_next_pdu(pipes_struct *p) +{ + RPC_HDR_RESP hdr_resp; + BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0); + BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0); + uint32 data_len; + uint32 data_space_available; + uint32 data_len_left; + prs_struct outgoing_pdu; + char *data; + char *data_from; + 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(0x1c010002)); + 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_space_available = sizeof(p->out_data.current_pdu) - RPC_HEADER_LEN - RPC_HDR_RESP_LEN; + if(p->ntlmssp_auth_validated) + data_space_available -= (RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN); + + /* + * The amount we send is the minimum of the available + * space and the amount left to send. + */ + + 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: no data left to send !\n")); + return False; + } + + 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; + + /* + * Set up the header lengths. + */ + + if (p->ntlmssp_auth_validated) { + p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len + + RPC_HDR_AUTH_LEN + RPC_AUTH_NTLMSSP_CHK_LEN; + p->hdr.auth_len = RPC_AUTH_NTLMSSP_CHK_LEN; + } else { + p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len; + p->hdr.auth_len = 0; + } + + /* + * 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; + + /* + * Init the parse struct to point at the outgoing + * data. + */ + + prs_init( &outgoing_pdu, 0, 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: 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: 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. */ + data_from = prs_data_p(&p->out_data.rdata) + p->out_data.data_sent_length; + + if(!prs_append_data(&outgoing_pdu, data_from, data_len)) { + DEBUG(0,("create_next_pdu: failed to copy %u bytes of data.\n", (unsigned int)data_len)); + prs_mem_free(&outgoing_pdu); + return False; + } + + /* + * Set data to point to where we copied the data into. + */ + + data = prs_data_p(&outgoing_pdu) + data_pos; + + if (p->hdr.auth_len > 0) { + uint32 crc32 = 0; + + DEBUG(5,("create_next_pdu: sign: %s seal: %s data %d auth %d\n", + BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, p->hdr.auth_len)); + + if (auth_seal) { + crc32 = crc32_calc_buffer(data, data_len); + NTLMSSPcalc_p(p, (uchar*)data, data_len); + } + + if (auth_seal || auth_verify) { + RPC_HDR_AUTH auth_info; + + init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, + (auth_verify ? RPC_HDR_AUTH_LEN : 0), (auth_verify ? 1 : 0)); + if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &outgoing_pdu, 0)) { + DEBUG(0,("create_next_pdu: failed to marshall RPC_HDR_AUTH.\n")); + prs_mem_free(&outgoing_pdu); + return False; + } + } + + if (auth_verify) { + RPC_AUTH_NTLMSSP_CHK ntlmssp_chk; + char *auth_data = prs_data_p(&outgoing_pdu); + + p->ntlmssp_seq_num++; + init_rpc_auth_ntlmssp_chk(&ntlmssp_chk, NTLMSSP_SIGN_VERSION, + crc32, p->ntlmssp_seq_num++); + auth_data = prs_data_p(&outgoing_pdu) + prs_offset(&outgoing_pdu) + 4; + if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, &outgoing_pdu, 0)) { + DEBUG(0,("create_next_pdu: failed to marshall RPC_AUTH_NTLMSSP_CHK.\n")); + prs_mem_free(&outgoing_pdu); + return False; + } + NTLMSSPcalc_p(p, (uchar*)auth_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4); + } + } + + /* + * 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; +} + +/******************************************************************* + 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. + The initial challenge is stored in p->challenge. + *******************************************************************/ + +static BOOL api_pipe_ntlmssp_verify(pipes_struct *p, RPC_AUTH_NTLMSSP_RESP *ntlmssp_resp) +{ + uchar lm_owf[24]; + uchar nt_owf[128]; + int nt_pw_len; + int lm_pw_len; + fstring user_name; + fstring domain; + fstring wks; + + NTSTATUS nt_status; + + struct auth_context *auth_context = NULL; + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + + uid_t uid; + uid_t gid; + + DEBUG(5,("api_pipe_ntlmssp_verify: checking user details\n")); + + memset(p->user_name, '\0', sizeof(p->user_name)); + memset(p->pipe_user_name, '\0', sizeof(p->pipe_user_name)); + memset(p->domain, '\0', sizeof(p->domain)); + memset(p->wks, '\0', sizeof(p->wks)); + + /* Set up for non-authenticated user. */ + delete_nt_token(&p->pipe_user.nt_user_token); + p->pipe_user.ngroups = 0; + SAFE_FREE( p->pipe_user.groups); + + /* + * Setup an empty password for a guest user. + */ + + /* + * We always negotiate UNICODE. + */ + + if (p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_UNICODE) { + rpcstr_pull(user_name, ntlmssp_resp->user, sizeof(fstring), ntlmssp_resp->hdr_usr.str_str_len*2, 0 ); + rpcstr_pull(domain, ntlmssp_resp->domain, sizeof(fstring), ntlmssp_resp->hdr_domain.str_str_len*2, 0); + rpcstr_pull(wks, ntlmssp_resp->wks, sizeof(fstring), ntlmssp_resp->hdr_wks.str_str_len*2, 0); + } else { + pull_ascii_fstring(user_name, ntlmssp_resp->user); + pull_ascii_fstring(domain, ntlmssp_resp->domain); + pull_ascii_fstring(wks, ntlmssp_resp->wks); + } + + DEBUG(5,("user: %s domain: %s wks: %s\n", user_name, domain, wks)); + + nt_pw_len = MIN(sizeof(nt_owf), ntlmssp_resp->hdr_nt_resp.str_str_len); + lm_pw_len = MIN(sizeof(lm_owf), ntlmssp_resp->hdr_lm_resp.str_str_len); + + memcpy(lm_owf, ntlmssp_resp->lm_resp, sizeof(lm_owf)); + memcpy(nt_owf, ntlmssp_resp->nt_resp, nt_pw_len); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("lm, nt owfs, chal\n")); + dump_data(100, (char *)lm_owf, sizeof(lm_owf)); + dump_data(100, (char *)nt_owf, nt_pw_len); + dump_data(100, (char *)p->challenge, 8); +#endif + + /* + * Allow guest access. Patch from Shirish Kalele <kalele@veritas.com>. + */ + + if (*user_name) { + + /* + * Do the length checking only if user is not NULL. + */ + + if (ntlmssp_resp->hdr_lm_resp.str_str_len == 0) + return False; + if (ntlmssp_resp->hdr_nt_resp.str_str_len == 0) + return False; + if (ntlmssp_resp->hdr_usr.str_str_len == 0) + return False; + if (ntlmssp_resp->hdr_domain.str_str_len == 0) + return False; + if (ntlmssp_resp->hdr_wks.str_str_len == 0) + return False; + + } + + make_auth_context_fixed(&auth_context, (uchar*)p->challenge); + + if (!make_user_info_netlogon_network(&user_info, + user_name, domain, wks, + lm_owf, lm_pw_len, + nt_owf, nt_pw_len)) { + DEBUG(0,("make_user_info_netlogon_network failed! Failing authenticaion.\n")); + return False; + } + + nt_status = auth_context->check_ntlm_password(auth_context, user_info, &server_info); + + (auth_context->free)(&auth_context); + free_user_info(&user_info); + + p->ntlmssp_auth_validated = NT_STATUS_IS_OK(nt_status); + + if (!p->ntlmssp_auth_validated) { + DEBUG(1,("api_pipe_ntlmssp_verify: User [%s]\\[%s] from machine %s \ +failed authentication on named pipe %s.\n", domain, user_name, wks, p->name )); + free_server_info(&server_info); + return False; + } + + /* + * Set up the sign/seal data. + */ + + { + uchar p24[24]; + NTLMSSPOWFencrypt(server_info->first_8_lm_hash, lm_owf, p24); + { + unsigned char j = 0; + int ind; + + unsigned char k2[8]; + + memcpy(k2, p24, 5); + k2[5] = 0xe5; + k2[6] = 0x38; + k2[7] = 0xb0; + + for (ind = 0; ind < 256; ind++) + p->ntlmssp_hash[ind] = (unsigned char)ind; + + for( ind = 0; ind < 256; ind++) { + unsigned char tc; + + j += (p->ntlmssp_hash[ind] + k2[ind%8]); + + tc = p->ntlmssp_hash[ind]; + p->ntlmssp_hash[ind] = p->ntlmssp_hash[j]; + p->ntlmssp_hash[j] = tc; + } + + p->ntlmssp_hash[256] = 0; + p->ntlmssp_hash[257] = 0; + } +/* NTLMSSPhash(p->ntlmssp_hash, p24); */ + p->ntlmssp_seq_num = 0; + + } + + fstrcpy(p->user_name, user_name); + fstrcpy(p->pipe_user_name, pdb_get_username(server_info->sam_account)); + fstrcpy(p->domain, domain); + fstrcpy(p->wks, wks); + + /* + * Store the UNIX credential data (uid/gid pair) in the pipe structure. + */ + + if (!IS_SAM_UNIX_USER(server_info->sam_account)) { + DEBUG(0,("Attempted authenticated pipe with invalid user. No uid/gid in SAM_ACCOUNT\n")); + free_server_info(&server_info); + return False; + } + + memcpy(p->session_key, server_info->session_key, sizeof(p->session_key)); + + uid = pdb_get_uid(server_info->sam_account); + gid = pdb_get_gid(server_info->sam_account); + + p->pipe_user.uid = uid; + p->pipe_user.gid = gid; + + /* Set up pipe user group membership. */ + initialise_groups(p->pipe_user_name, p->pipe_user.uid, p->pipe_user.gid); + get_current_groups( &p->pipe_user.ngroups, &p->pipe_user.groups); + + if (server_info->ptok) + add_supplementary_nt_login_groups(&p->pipe_user.ngroups, &p->pipe_user.groups, &server_info->ptok); + + /* Create an NT_USER_TOKEN struct for this user. */ + p->pipe_user.nt_user_token = create_nt_token(p->pipe_user.uid,p->pipe_user.gid, + p->pipe_user.ngroups, p->pipe_user.groups, + server_info->guest, server_info->ptok); + + p->ntlmssp_auth_validated = True; + + pdb_free_sam(&server_info->sam_account); + return True; +} + +/******************************************************************* + The switch table for the pipe names and the functions to handle them. + *******************************************************************/ + +struct api_cmd +{ + char * pipe_clnt_name; + char * pipe_srv_name; + BOOL (*fn) (pipes_struct *); +}; + +static struct api_cmd api_fd_commands[] = +{ + { "lsarpc", "lsass", api_ntlsa_rpc }, + { "samr", "lsass", api_samr_rpc }, + { "srvsvc", "ntsvcs", api_srvsvc_rpc }, + { "wkssvc", "ntsvcs", api_wkssvc_rpc }, + { "NETLOGON", "lsass", api_netlog_rpc }, + { "winreg", "winreg", api_reg_rpc }, + { "spoolss", "spoolss", api_spoolss_rpc }, + { "netdfs", "netdfs" , api_netdfs_rpc }, + { NULL, NULL, NULL } +}; + +/******************************************************************* + This is the client reply to our challenge for an authenticated + bind request. The challenge we sent is in p->challenge. +*******************************************************************/ + +BOOL api_pipe_bind_auth_resp(pipes_struct *p, prs_struct *rpc_in_p) +{ + RPC_HDR_AUTHA autha_info; + RPC_AUTH_VERIFIER auth_verifier; + RPC_AUTH_NTLMSSP_RESP ntlmssp_resp; + + DEBUG(5,("api_pipe_bind_auth_resp: decode request. %d\n", __LINE__)); + + if (p->hdr.auth_len == 0) { + DEBUG(0,("api_pipe_bind_auth_resp: No auth field sent !\n")); + return False; + } + + /* + * Decode the authentication verifier response. + */ + + if(!smb_io_rpc_hdr_autha("", &autha_info, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_HDR_AUTHA failed.\n")); + return False; + } + + if (autha_info.auth_type != NTLMSSP_AUTH_TYPE || autha_info.auth_level != NTLMSSP_AUTH_LEVEL) { + DEBUG(0,("api_pipe_bind_auth_resp: incorrect auth type (%d) or level (%d).\n", + (int)autha_info.auth_type, (int)autha_info.auth_level )); + return False; + } + + if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_bind_auth_resp: unmarshall of RPC_AUTH_VERIFIER failed.\n")); + return False; + } + + /* + * Ensure this is a NTLMSSP_AUTH packet type. + */ + + if (!rpc_auth_verifier_chk(&auth_verifier, "NTLMSSP", NTLMSSP_AUTH)) { + DEBUG(0,("api_pipe_bind_auth_resp: rpc_auth_verifier_chk failed.\n")); + return False; + } + + if(!smb_io_rpc_auth_ntlmssp_resp("", &ntlmssp_resp, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_bind_auth_resp: Failed to unmarshall RPC_AUTH_NTLMSSP_RESP.\n")); + return False; + } + + /* + * The following call actually checks the challenge/response data. + * for correctness against the given DOMAIN\user name. + */ + + if (!api_pipe_ntlmssp_verify(p, &ntlmssp_resp)) + return False; + + p->pipe_bound = True +; + return True; +} + +/******************************************************************* + 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( &outgoing_rpc, 0, 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; + + 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( &outgoing_pdu, 0, 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; +} + +/******************************************************************* + Ensure a bind request has the correct abstract & transfer interface. + Used to reject unknown binds from Win2k. +*******************************************************************/ + +BOOL check_bind_req(char* pipe_name, RPC_IFACE* abstract, + RPC_IFACE* transfer) +{ + extern struct pipe_id_info pipe_names[]; + int i=0; + fstring pname; + fstrcpy(pname,"\\PIPE\\"); + fstrcat(pname,pipe_name); + + for(i=0;pipe_names[i].client_pipe; i++) { + if(strequal(pipe_names[i].client_pipe, pname)) + break; + } + + if(pipe_names[i].client_pipe == NULL) + return False; + + /* check the abstract interface */ + if((abstract->version != pipe_names[i].abstr_syntax.version) || + (memcmp(&abstract->uuid, &pipe_names[i].abstr_syntax.uuid, + sizeof(RPC_UUID)) != 0)) + return False; + + /* check the transfer interface */ + if((transfer->version != pipe_names[i].trans_syntax.version) || + (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, + sizeof(RPC_UUID)) != 0)) + return False; + + return True; +} + +/******************************************************************* + 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; + enum RPC_PKT_TYPE reply_pkt_type; + + p->ntlmssp_auth_requested = False; + + DEBUG(5,("api_pipe_bind_req: decode request. %d\n", __LINE__)); + + /* + * Try and find the correct pipe name to ensure + * that this is a pipe name we support. + */ + + for (i = 0; api_fd_commands[i].pipe_clnt_name; i++) { + if (strequal(api_fd_commands[i].pipe_clnt_name, p->name) && + api_fd_commands[i].fn != NULL) { + DEBUG(3,("api_pipe_bind_req: \\PIPE\\%s -> \\PIPE\\%s\n", + api_fd_commands[i].pipe_clnt_name, + api_fd_commands[i].pipe_srv_name)); + fstrcpy(p->pipe_srv_name, api_fd_commands[i].pipe_srv_name); + break; + } + } + + if (api_fd_commands[i].fn == NULL) { + DEBUG(3,("api_pipe_bind_req: Unknown pipe name %s in bind request.\n", + p->name )); + if(!setup_bind_nak(p)) + return False; + return True; + } + + /* 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")); + return False; + } + + /* + * Check if this is an authenticated request. + */ + + if (p->hdr.auth_len != 0) { + RPC_AUTH_VERIFIER auth_verifier; + RPC_AUTH_NTLMSSP_NEG ntlmssp_neg; + + /* + * 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")); + return False; + } + + /* + * We only support NTLMSSP_AUTH_TYPE requests. + */ + + if(auth_info.auth_type != NTLMSSP_AUTH_TYPE) { + DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n", + auth_info.auth_type )); + return False; + } + + if(!smb_io_rpc_auth_verifier("", &auth_verifier, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n")); + return False; + } + + if(!strequal(auth_verifier.signature, "NTLMSSP")) { + DEBUG(0,("api_pipe_bind_req: auth_verifier.signature != NTLMSSP\n")); + return False; + } + + if(auth_verifier.msg_type != NTLMSSP_NEGOTIATE) { + DEBUG(0,("api_pipe_bind_req: auth_verifier.msg_type (%d) != NTLMSSP_NEGOTIATE\n", + auth_verifier.msg_type)); + return False; + } + + if(!smb_io_rpc_auth_ntlmssp_neg("", &ntlmssp_neg, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_bind_req: Failed to unmarshall RPC_AUTH_NTLMSSP_NEG.\n")); + return False; + } + + p->ntlmssp_chal_flags = SMBD_NTLMSSP_NEG_FLAGS; + p->ntlmssp_auth_requested = True; + } + + switch(p->hdr.pkt_type) { + case RPC_BIND: + /* name has to be \PIPE\xxxxx */ + fstrcpy(ack_pipe_name, "\\PIPE\\"); + fstrcat(ack_pipe_name, p->pipe_srv_name); + reply_pkt_type = RPC_BINDACK; + break; + case RPC_ALTCONT: + /* secondary address CAN be NULL + * as the specs say it's ignored. + * It MUST NULL to have the spoolss working. + */ + fstrcpy(ack_pipe_name,""); + reply_pkt_type = RPC_ALTCONTRESP; + break; + default: + return False; + } + + DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__)); + + /* + * 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( &outgoing_rpc, 0, p->mem_ctx, MARSHALL); + 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,("pi_pipe_bind_req: malloc out_auth failed.\n")); + prs_mem_free(&outgoing_rpc); + prs_mem_free(&out_hdr_ba); + return False; + } + + if (p->ntlmssp_auth_requested) + assoc_gid = 0x7a77; + else + 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->name, &hdr_rb.abstract, &hdr_rb.transfer)) { + init_rpc_hdr_ba(&hdr_ba, + MAX_PDU_FRAG_LEN, + MAX_PDU_FRAG_LEN, + assoc_gid, + ack_pipe_name, + 0x1, 0x0, 0x0, + &hdr_rb.transfer); + } else { + RPC_IFACE null_interface; + ZERO_STRUCT(null_interface); + /* Rejection reason: abstract syntax not supported */ + init_rpc_hdr_ba(&hdr_ba, MAX_PDU_FRAG_LEN, + MAX_PDU_FRAG_LEN, assoc_gid, + ack_pipe_name, 0x1, 0x2, 0x1, + &null_interface); + } + + /* + * 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; + } + + /* + * Now the authentication. + */ + + if (p->ntlmssp_auth_requested) { + RPC_AUTH_VERIFIER auth_verifier; + RPC_AUTH_NTLMSSP_CHAL ntlmssp_chal; + + generate_random_buffer(p->challenge, 8, False); + + /*** Authentication info ***/ + + init_rpc_hdr_auth(&auth_info, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, RPC_HDR_AUTH_LEN, 1); + if(!smb_io_rpc_hdr_auth("", &auth_info, &out_auth, 0)) { + DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_AUTH failed.\n")); + goto err_exit; + } + + /*** NTLMSSP verifier ***/ + + init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_CHALLENGE); + if(!smb_io_rpc_auth_verifier("", &auth_verifier, &out_auth, 0)) { + DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_VERIFIER failed.\n")); + goto err_exit; + } + + /* NTLMSSP challenge ***/ + + init_rpc_auth_ntlmssp_chal(&ntlmssp_chal, p->ntlmssp_chal_flags, p->challenge); + if(!smb_io_rpc_auth_ntlmssp_chal("", &ntlmssp_chal, &out_auth, 0)) { + DEBUG(0,("api_pipe_bind_req: marshalling of RPC_AUTH_NTLMSSP_CHAL failed.\n")); + goto err_exit; + } + + /* Auth len in the rpc header doesn't include auth_header. */ + auth_len = prs_offset(&out_auth) - RPC_HDR_AUTH_LEN; + } + + /* + * Create the header, now we know the length. + */ + + init_rpc_hdr(&p->hdr, reply_pkt_type, 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,("pi_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(p->ntlmssp_auth_requested && !prs_append_prs_data( &outgoing_rpc, &out_auth)) { + DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n")); + goto err_exit; + } + + if(!p->ntlmssp_auth_requested) + p->pipe_bound = True; + + /* + * 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 False; +} + +/**************************************************************************** + Deal with sign & seal processing on an RPC request. +****************************************************************************/ + +BOOL api_pipe_auth_process(pipes_struct *p, prs_struct *rpc_in) +{ + /* + * We always negotiate the following two bits.... + */ + BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0); + BOOL auth_seal = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL) != 0); + int data_len; + int auth_len; + uint32 old_offset; + uint32 crc32 = 0; + + auth_len = p->hdr.auth_len; + + if ((auth_len != RPC_AUTH_NTLMSSP_CHK_LEN) && auth_verify) { + DEBUG(0,("api_pipe_auth_process: Incorrect auth_len %d.\n", 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. + */ + + data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - + (auth_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len; + + DEBUG(5,("api_pipe_auth_process: sign: %s seal: %s data %d auth %d\n", + BOOLSTR(auth_verify), BOOLSTR(auth_seal), data_len, auth_len)); + + if (auth_seal) { + /* + * The data in rpc_in doesn't contain the RPC_HEADER as this + * has already been consumed. + */ + char *data = prs_data_p(rpc_in) + RPC_HDR_REQ_LEN; + NTLMSSPcalc_p(p, (uchar*)data, data_len); + crc32 = crc32_calc_buffer(data, data_len); + } + + old_offset = prs_offset(rpc_in); + + if (auth_seal || auth_verify) { + RPC_HDR_AUTH auth_info; + + if(!prs_set_offset(rpc_in, old_offset + data_len)) { + DEBUG(0,("api_pipe_auth_process: cannot move offset to %u.\n", + (unsigned int)old_offset + data_len )); + return False; + } + + if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) { + DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_HDR_AUTH.\n")); + return False; + } + } + + if (auth_verify) { + RPC_AUTH_NTLMSSP_CHK ntlmssp_chk; + char *req_data = prs_data_p(rpc_in) + prs_offset(rpc_in) + 4; + + DEBUG(5,("api_pipe_auth_process: auth %d\n", prs_offset(rpc_in) + 4)); + + /* + * Ensure we have RPC_AUTH_NTLMSSP_CHK_LEN - 4 more bytes in the + * incoming buffer. + */ + if(prs_mem_get(rpc_in, RPC_AUTH_NTLMSSP_CHK_LEN - 4) == NULL) { + DEBUG(0,("api_pipe_auth_process: missing %d bytes in buffer.\n", + RPC_AUTH_NTLMSSP_CHK_LEN - 4 )); + return False; + } + + NTLMSSPcalc_p(p, (uchar*)req_data, RPC_AUTH_NTLMSSP_CHK_LEN - 4); + if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &ntlmssp_chk, rpc_in, 0)) { + DEBUG(0,("api_pipe_auth_process: failed to unmarshall RPC_AUTH_NTLMSSP_CHK.\n")); + return False; + } + + if (!rpc_auth_ntlmssp_chk(&ntlmssp_chk, crc32, p->ntlmssp_seq_num)) { + DEBUG(0,("api_pipe_auth_process: NTLMSSP check failed.\n")); + return False; + } + } + + /* + * Return the current pointer to the data offset. + */ + + if(!prs_set_offset(rpc_in, old_offset)) { + DEBUG(0,("api_pipe_auth_process: failed to set offset back to %u\n", + (unsigned int)old_offset )); + return False; + } + + 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->ntlmssp_auth_validated) { + memcpy(user, &p->pipe_user, sizeof(struct current_user)); + } else { + extern struct current_user current_user; + memcpy(user, ¤t_user, sizeof(struct current_user)); + } + + return user; +} + +/**************************************************************************** + 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) +{ + int i = 0; + BOOL ret = False; + + if (p->ntlmssp_auth_validated) { + + if(!become_authenticated_pipe_user(p)) { + prs_mem_free(&p->out_data.rdata); + return False; + } + } + + for (i = 0; api_fd_commands[i].pipe_clnt_name; i++) { + if (strequal(api_fd_commands[i].pipe_clnt_name, p->name) && + api_fd_commands[i].fn != NULL) { + DEBUG(3,("Doing \\PIPE\\%s\n", api_fd_commands[i].pipe_clnt_name)); + set_current_rpc_talloc(p->mem_ctx); + ret = api_fd_commands[i].fn(p); + set_current_rpc_talloc(NULL); + } + } + + if(p->ntlmssp_auth_validated) + unbecome_authenticated_pipe_user(); + + return ret; +} + +/******************************************************************* + Calls the underlying RPC function for a named pipe. + ********************************************************************/ + +BOOL api_rpcTNP(pipes_struct *p, char *rpc_name, + struct api_struct *api_rpc_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; api_rpc_cmds[fn_num].name; 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 (api_rpc_cmds[fn_num].name == NULL) { + /* + * 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(0x1c010002)); + return True; + } + + offset1 = prs_offset(&p->out_data.rdata); + + /* 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(0x1C00001A)); + 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) && + (p->in_data.data.data_offset != p->in_data.data.buffer_size)) { + int data_len = p->in_data.data.buffer_size - + p->in_data.data.data_offset; + char *data; + + data = 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, + 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..44dd5fac65 --- /dev/null +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -0,0 +1,1131 @@ +/* + * 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) Jeremy Allison 1999. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +#define PIPE "\\PIPE\\" +#define PIPELEN strlen(PIPE) + +static smb_np_struct *chain_p; +static int pipes_open; + +#ifndef MAX_OPEN_PIPES +#define MAX_OPEN_PIPES 2048 +#endif + +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 ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n, + BOOL *is_data_outstanding); +static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n); +static BOOL close_internal_rpc_pipe_hnd(void *np_conn); +static void *make_internal_rpc_pipe_p(char *pipe_name, + connection_struct *conn, uint16 vuid); + +/**************************************************************************** + Pipe iterator functions. +****************************************************************************/ + +smb_np_struct *get_first_pipe(void) +{ + return Pipes; +} + +smb_np_struct *get_next_pipe(smb_np_struct *p) +{ + return p->next; +} + +/**************************************************************************** + 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, 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(char *pipe_name, + connection_struct *conn, uint16 vuid) +{ + int i; + smb_np_struct *p, *p_it; + static int next_pipe; + + DEBUG(4,("Open pipe requested %s (pipes_open=%d)\n", + pipe_name, pipes_open)); + + + /* 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 = (smb_np_struct *)malloc(sizeof(*p)); + + if (!p) + { + DEBUG(0,("ERROR! no memory for pipes_struct!\n")); + return NULL; + } + + ZERO_STRUCTP(p); + + /* 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->namedpipe_close = close_internal_rpc_pipe_hnd; + + p->np_state = p->namedpipe_create(pipe_name, conn, vuid); + + if (p->np_state == NULL) { + + DEBUG(0,("open_rpc_pipe_p: make_internal_rpc_pipe_p failed.\n")); + SAFE_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; + + fstrcpy(p->name, pipe_name); + + 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 +****************************************************************************/ + +static void *make_internal_rpc_pipe_p(char *pipe_name, + connection_struct *conn, uint16 vuid) +{ + pipes_struct *p; + user_struct *vuser = get_valid_user_struct(vuid); + + DEBUG(4,("Create pipe requested %s\n", pipe_name)); + + if (!vuser && vuid != UID_FIELD_INVALID) { + DEBUG(0,("ERROR! vuid %d did not map to a valid vuser struct!\n", vuid)); + return NULL; + } + + p = (pipes_struct *)malloc(sizeof(*p)); + + if (!p) + { + DEBUG(0,("ERROR! no memory for pipes_struct!\n")); + return NULL; + } + + ZERO_STRUCTP(p); + + if ((p->mem_ctx = talloc_init()) == NULL) { + DEBUG(0,("open_rpc_pipe_p: talloc_init failed.\n")); + SAFE_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); + SAFE_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, MAX_PDU_FRAG_LEN, p->mem_ctx, MARSHALL)) { + DEBUG(0,("open_rpc_pipe_p: malloc fail for in_data struct.\n")); + return NULL; + } + + DLIST_ADD(InternalPipes, p); + + p->conn = conn; + p->vuid = vuid; + + p->ntlmssp_chal_flags = 0; + p->ntlmssp_auth_validated = False; + p->ntlmssp_auth_requested = False; + + p->pipe_bound = False; + p->fault_state = False; + p->endian = RPC_LITTLE_ENDIAN; + + ZERO_STRUCT(p->pipe_user); + + p->pipe_user.uid = (uid_t)-1; + p->pipe_user.gid = (gid_t)-1; + + /* Store the session key */ + if (vuser) { + memcpy(p->session_key, vuser->session_key, sizeof(p->session_key)); + } + + /* + * Initialize the incoming RPC struct. + */ + + p->in_data.pdu_needed_len = 0; + p->in_data.pdu_received_len = 0; + + /* + * Initialize the outgoing RPC struct. + */ + + p->out_data.current_pdu_len = 0; + p->out_data.current_pdu_sent = 0; + p->out_data.data_sent_length = 0; + + /* + * Initialize the outgoing RPC data buffer with no memory. + */ + prs_init(&p->out_data.rdata, 0, p->mem_ctx, MARSHALL); + + fstrcpy(p->name, pipe_name); + + DEBUG(4,("Created internal pipe %s (pipes_open=%d)\n", + pipe_name, pipes_open)); + + return (void*)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 : vuid = 0x%x\n", + p->name, p->vuid )); +} + +/**************************************************************************** + 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( &rpc_in, 0, 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 > 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 )); + + /* + * Adjust for the header we just ate. + */ + p->in_data.pdu_received_len = 0; + p->in_data.pdu_needed_len = (uint32)p->hdr.frag_len - RPC_HEADER_LEN; + + /* + * Null the data we just ate. + */ + + memset((char *)&p->in_data.current_in_pdu[0], '\0', 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. +****************************************************************************/ + +void free_pipe_context(pipes_struct *p) +{ + if (p->mem_ctx) { + DEBUG(3,("free_pipe_context: destroying talloc pool of size %u\n", talloc_pool_size(p->mem_ctx) )); + talloc_destroy_pool(p->mem_ctx); + } else { + p->mem_ctx = talloc_init(); + 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) +{ + BOOL auth_verify = ((p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) != 0); + size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - + (auth_verify ? 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; + } + + if(p->ntlmssp_auth_validated && !api_pipe_auth_process(p, rpc_in_p)) { + DEBUG(0,("process_request_pdu: failed to do auth processing.\n")); + set_incoming_fault(p); + return False; + } + + if (p->ntlmssp_auth_requested && !p->ntlmssp_auth_validated) { + + /* + * Authentication _was_ requested and it already failed. + */ + + DEBUG(0,("process_request_pdu: RPC request received on pipe %s where \ +authentication failed. Denying the request.\n", p->name)); + set_incoming_fault(p); + return False; + } + + /* + * 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. + */ + + { + char *data_from = prs_data_p(rpc_in_p) + prs_offset(rpc_in_p); + + if(!prs_append_data(&p->in_data.data, data_from, 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 ssize_t process_complete_pdu(pipes_struct *p) +{ + prs_struct rpc_in; + size_t data_len = p->in_data.pdu_received_len; + char *data_p = (char *)&p->in_data.current_in_pdu[0]; + 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(0x1c010002)); + return (ssize_t)data_len; + } + + prs_init( &rpc_in, 0, 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_BIND: + case RPC_ALTCONT: + /* + * 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_BINDRESP: + /* + * We assume that a pipe bind_resp is only in one pdu. + */ + if(pipe_init_outgoing_data(p)) + reply = api_pipe_bind_auth_resp(p, &rpc_in); + break; + case RPC_REQUEST: + reply = process_request_pdu(p, &rpc_in); + 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(0x1c010002)); + 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); + return (ssize_t)data_len; +} + +/**************************************************************************** + 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, 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) + return unmarshall_rpc_header(p); + + /* + * 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. + */ + + 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; + + /* + * Do we have a complete PDU ? + */ + + if(p->in_data.pdu_received_len == p->in_data.pdu_needed_len) + return process_complete_pdu(p); + + 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, data, n); + + return p->namedpipe_write(p->np_state, data, n); +} + +/**************************************************************************** + Accepts incoming data on an internal rpc pipe. +****************************************************************************/ + +static ssize_t write_to_internal_pipe(void *np_conn, char *data, size_t n) +{ + pipes_struct *p = (pipes_struct*)np_conn; + 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. +****************************************************************************/ + +static ssize_t read_from_internal_pipe(void *np_conn, char *data, size_t n, + BOOL *is_data_outstanding) +{ + pipes_struct *p = (pipes_struct*)np_conn; + 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 > 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, 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; + } + + p->namedpipe_close(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); + + ZERO_STRUCTP(p); + + SAFE_FREE(p); + + return True; +} + +/**************************************************************************** + Close an rpc pipe. +****************************************************************************/ + +static BOOL close_internal_rpc_pipe_hnd(void *np_conn) +{ + pipes_struct *p = (pipes_struct *)np_conn; + 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->mem_ctx) + talloc_destroy(p->mem_ctx); + + /* Free the handles database. */ + close_policy_by_pipe(p); + + delete_nt_token(&p->pipe_user.nt_user_token); + SAFE_FREE(p->pipe_user.groups); + + DLIST_REMOVE(InternalPipes, p); + + ZERO_STRUCTP(p); + + SAFE_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(char *buf, int where) +{ + int pnum = SVAL(buf,where); + + 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_reg.c b/source3/rpc_server/srv_reg.c new file mode 100644 index 0000000000..569f3fb8b1 --- /dev/null +++ b/source3/rpc_server/srv_reg.c @@ -0,0 +1,208 @@ +/* + * 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 2000. + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the interface for the registry functions. */ + +#include "includes.h" + +/******************************************************************* + api_reg_close + ********************************************************************/ + +static BOOL api_reg_close(pipes_struct *p) +{ + REG_Q_CLOSE q_u; + REG_R_CLOSE r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the reg unknown 1 */ + if(!reg_io_q_close("", &q_u, data, 0)) + return False; + + r_u.status = _reg_close(p, &q_u, &r_u); + + if(!reg_io_r_close("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_reg_open + ********************************************************************/ + +static BOOL api_reg_open(pipes_struct *p) +{ + REG_Q_OPEN_HKLM q_u; + REG_R_OPEN_HKLM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the reg open */ + if(!reg_io_q_open_hklm("", &q_u, data, 0)) + return False; + + r_u.status = _reg_open(p, &q_u, &r_u); + + if(!reg_io_r_open_hklm("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_reg_open_entry + ********************************************************************/ + +static BOOL api_reg_open_entry(pipes_struct *p) +{ + REG_Q_OPEN_ENTRY q_u; + REG_R_OPEN_ENTRY r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the reg open entry */ + if(!reg_io_q_open_entry("", &q_u, data, 0)) + return False; + + /* construct reply. */ + r_u.status = _reg_open_entry(p, &q_u, &r_u); + + if(!reg_io_r_open_entry("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_reg_info + ********************************************************************/ + +static BOOL api_reg_info(pipes_struct *p) +{ + REG_Q_INFO q_u; + REG_R_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the reg unknown 0x11*/ + if(!reg_io_q_info("", &q_u, data, 0)) + return False; + + r_u.status = _reg_info(p, &q_u, &r_u); + + if(!reg_io_r_info("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_reg_shutdown + ********************************************************************/ + +static BOOL api_reg_shutdown(pipes_struct *p) +{ + REG_Q_SHUTDOWN q_u; + REG_R_SHUTDOWN r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the reg shutdown */ + if(!reg_io_q_shutdown("", &q_u, data, 0)) + return False; + + r_u.status = _reg_shutdown(p, &q_u, &r_u); + + if(!reg_io_r_shutdown("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_reg_abort_shutdown + ********************************************************************/ + +static BOOL api_reg_abort_shutdown(pipes_struct *p) +{ + REG_Q_ABORT_SHUTDOWN q_u; + REG_R_ABORT_SHUTDOWN r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the reg shutdown */ + if(!reg_io_q_abort_shutdown("", &q_u, data, 0)) + return False; + + r_u.status = _reg_abort_shutdown(p, &q_u, &r_u); + + if(!reg_io_r_abort_shutdown("", &r_u, rdata, 0)) + return False; + + return True; +} + + +/******************************************************************* + array of \PIPE\reg operations + ********************************************************************/ +static struct api_struct api_reg_cmds[] = +{ + { "REG_CLOSE" , REG_CLOSE , api_reg_close }, + { "REG_OPEN_ENTRY" , REG_OPEN_ENTRY , api_reg_open_entry }, + { "REG_OPEN" , REG_OPEN_HKLM , api_reg_open }, + { "REG_INFO" , REG_INFO , api_reg_info }, + { "REG_SHUTDOWN" , REG_SHUTDOWN , api_reg_shutdown }, + { "REG_ABORT_SHUTDOWN", REG_ABORT_SHUTDOWN, api_reg_abort_shutdown }, + { NULL, 0 , NULL } +}; + +/******************************************************************* + receives a reg pipe and responds. + ********************************************************************/ + +BOOL api_reg_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_reg_rpc", api_reg_cmds); +} diff --git a/source3/rpc_server/srv_reg_nt.c b/source3/rpc_server/srv_reg_nt.c new file mode 100644 index 0000000000..adedd4a8fa --- /dev/null +++ b/source3/rpc_server/srv_reg_nt.c @@ -0,0 +1,236 @@ +/* + * 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) Hewlett-Packard Company 1999. + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Implementation of registry functions. */ + +#include "includes.h" + +struct reg_info { + /* for use by \PIPE\winreg */ + fstring name; /* name of registry key */ +}; + +static void free_reg_info(void *ptr) +{ + struct reg_info *info = (struct reg_info *)ptr; + + SAFE_FREE(info); +} + +/******************************************************************* + reg_reply_unknown_1 + ********************************************************************/ + +NTSTATUS _reg_close(pipes_struct *p, REG_Q_CLOSE *q_u, REG_R_CLOSE *r_u) +{ + /* set up the REG unknown_1 response */ + ZERO_STRUCT(r_u->pol); + + /* close the policy handle */ + if (!close_policy_hnd(p, &q_u->pol)) + return NT_STATUS_OBJECT_NAME_INVALID; + + return NT_STATUS_OK; +} + +/******************************************************************* + reg_reply_open + ********************************************************************/ + +NTSTATUS _reg_open(pipes_struct *p, REG_Q_OPEN_HKLM *q_u, REG_R_OPEN_HKLM *r_u) +{ + if (!create_policy_hnd(p, &r_u->pol, free_reg_info, NULL)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return NT_STATUS_OK; +} + +/******************************************************************* + reg_reply_open_entry + ********************************************************************/ + +NTSTATUS _reg_open_entry(pipes_struct *p, REG_Q_OPEN_ENTRY *q_u, REG_R_OPEN_ENTRY *r_u) +{ + POLICY_HND pol; + fstring name; + struct reg_info *info = NULL; + + DEBUG(5,("reg_open_entry: %d\n", __LINE__)); + + if (!find_policy_by_hnd(p, &q_u->pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + rpcstr_pull(name,q_u->uni_name.buffer,sizeof(name),q_u->uni_name.uni_str_len*2,0); + + DEBUG(5,("reg_open_entry: %s\n", name)); + + /* lkcl XXXX do a check on the name, here */ + if (!strequal(name, "SYSTEM\\CurrentControlSet\\Control\\ProductOptions") && + !strequal(name, "System\\CurrentControlSet\\services\\Netlogon\\parameters\\")) + return NT_STATUS_ACCESS_DENIED; + + if ((info = (struct reg_info *)malloc(sizeof(struct reg_info))) == NULL) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(info); + fstrcpy(info->name, name); + + if (!create_policy_hnd(p, &pol, free_reg_info, (void *)info)) + return NT_STATUS_TOO_MANY_SECRETS; /* ha ha very droll */ + + init_reg_r_open_entry(r_u, &pol, NT_STATUS_OK); + + DEBUG(5,("reg_open_entry: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + reg_reply_info + ********************************************************************/ + +NTSTATUS _reg_info(pipes_struct *p, REG_Q_INFO *q_u, REG_R_INFO *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + char *key = NULL; + uint32 type=0x1; /* key type: REG_SZ */ + + UNISTR2 *uni_key = NULL; + BUFFER2 *buf = NULL; + fstring name; + + DEBUG(5,("_reg_info: %d\n", __LINE__)); + + if (!find_policy_by_hnd(p, &q_u->pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + rpcstr_pull(name, q_u->uni_type.buffer, sizeof(name), q_u->uni_type.uni_str_len*2, 0); + + DEBUG(5,("reg_info: checking key: %s\n", name)); + + uni_key = (UNISTR2 *)talloc_zero(p->mem_ctx, sizeof(UNISTR2)); + buf = (BUFFER2 *)talloc_zero(p->mem_ctx, sizeof(BUFFER2)); + + if (!uni_key || !buf) + return NT_STATUS_NO_MEMORY; + + if ( strequal(name, "RefusePasswordChange") ) { + type=0xF770; + status = NT_STATUS_NO_SUCH_FILE; + init_unistr2(uni_key, "", 0); + init_buffer2(buf, (uint8*) uni_key->buffer, uni_key->uni_str_len*2); + + buf->buf_max_len=4; + + goto out; + } + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + key = "LanmanNT"; + break; + case ROLE_STANDALONE: + key = "ServerNT"; + break; + case ROLE_DOMAIN_MEMBER: + key = "WinNT"; + break; + } + + /* This makes the server look like a member server to clients */ + /* which tells clients that we have our own local user and */ + /* group databases and helps with ACL support. */ + + init_unistr2(uni_key, key, strlen(key)+1); + init_buffer2(buf, (uint8*)uni_key->buffer, uni_key->uni_str_len*2); + + out: + init_reg_r_info(q_u->ptr_buf, r_u, buf, type, status); + + DEBUG(5,("reg_open_entry: %d\n", __LINE__)); + + return status; +} + +/******************************************************************* + reg_shutdwon + ********************************************************************/ + +#define SHUTDOWN_R_STRING "-r" +#define SHUTDOWN_F_STRING "-f" + + +NTSTATUS _reg_shutdown(pipes_struct *p, REG_Q_SHUTDOWN *q_u, REG_R_SHUTDOWN *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + pstring shutdown_script; + UNISTR2 unimsg = q_u->uni_msg; + pstring message; + pstring chkmsg; + fstring timeout; + fstring r; + fstring f; + + /* message */ + rpcstr_pull (message, unimsg.buffer, sizeof(message), unimsg.uni_str_len*2,0); + /* security check */ + alpha_strcpy (chkmsg, message, NULL, sizeof(message)); + /* timeout */ + snprintf(timeout, sizeof(timeout), "%d", q_u->timeout); + /* reboot */ + snprintf(r, sizeof(r), (q_u->flags & REG_REBOOT_ON_SHUTDOWN)?SHUTDOWN_R_STRING:""); + /* force */ + snprintf(f, sizeof(f), (q_u->flags & REG_FORCE_SHUTDOWN)?SHUTDOWN_F_STRING:""); + + pstrcpy(shutdown_script, lp_shutdown_script()); + + if(*shutdown_script) { + int shutdown_ret; + all_string_sub(shutdown_script, "%m", chkmsg, sizeof(shutdown_script)); + all_string_sub(shutdown_script, "%t", timeout, sizeof(shutdown_script)); + all_string_sub(shutdown_script, "%r", r, sizeof(shutdown_script)); + all_string_sub(shutdown_script, "%f", f, sizeof(shutdown_script)); + shutdown_ret = smbrun(shutdown_script,NULL); + DEBUG(3,("_reg_shutdown: Running the command `%s' gave %d\n",shutdown_script,shutdown_ret)); + } + + return status; +} + +NTSTATUS _reg_abort_shutdown(pipes_struct *p, REG_Q_ABORT_SHUTDOWN *q_u, REG_R_ABORT_SHUTDOWN *r_u) +{ + NTSTATUS status = NT_STATUS_OK; + pstring abort_shutdown_script; + + pstrcpy(abort_shutdown_script, lp_abort_shutdown_script()); + + if(*abort_shutdown_script) { + int abort_shutdown_ret; + abort_shutdown_ret = smbrun(abort_shutdown_script,NULL); + DEBUG(3,("_reg_abort_shutdown: Running the command `%s' gave %d\n",abort_shutdown_script,abort_shutdown_ret)); + } + + return status; +} diff --git a/source3/rpc_server/srv_samr.c b/source3/rpc_server/srv_samr.c new file mode 100644 index 0000000000..c555305bce --- /dev/null +++ b/source3/rpc_server/srv_samr.c @@ -0,0 +1,1442 @@ +/* + * 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) Jean François Micouleau 1998-2001. + * + * Split into interface and implementation modules by, + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This is the interface to the SAMR code. + */ + +#include "includes.h" + +/******************************************************************* + api_samr_close_hnd + ********************************************************************/ + +static BOOL api_samr_close_hnd(pipes_struct *p) +{ + SAMR_Q_CLOSE_HND q_u; + SAMR_R_CLOSE_HND 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(!samr_io_q_close_hnd("", &q_u, data, 0)) { + DEBUG(0,("api_samr_close_hnd: unable to unmarshall SAMR_Q_CLOSE_HND.\n")); + return False; + } + + r_u.status = _samr_close_hnd(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_close_hnd("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_close_hnd: unable to marshall SAMR_R_CLOSE_HND.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_open_domain + ********************************************************************/ + +static BOOL api_samr_open_domain(pipes_struct *p) +{ + SAMR_Q_OPEN_DOMAIN q_u; + SAMR_R_OPEN_DOMAIN 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(!samr_io_q_open_domain("", &q_u, data, 0)) { + DEBUG(0,("api_samr_open_domain: unable to unmarshall SAMR_Q_OPEN_DOMAIN.\n")); + return False; + } + + r_u.status = _samr_open_domain(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_open_domain("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_open_domain: unable to marshall SAMR_R_OPEN_DOMAIN.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_get_usrdom_pwinfo + ********************************************************************/ + +static BOOL api_samr_get_usrdom_pwinfo(pipes_struct *p) +{ + SAMR_Q_GET_USRDOM_PWINFO q_u; + SAMR_R_GET_USRDOM_PWINFO 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(!samr_io_q_get_usrdom_pwinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to unmarshall SAMR_Q_GET_USRDOM_PWINFO.\n")); + return False; + } + + r_u.status = _samr_get_usrdom_pwinfo(p, &q_u, &r_u); + + if(!samr_io_r_get_usrdom_pwinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_get_usrdom_pwinfo: unable to marshall SAMR_R_GET_USRDOM_PWINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_sec_obj + ********************************************************************/ + +static BOOL api_samr_query_sec_obj(pipes_struct *p) +{ + SAMR_Q_QUERY_SEC_OBJ q_u; + SAMR_R_QUERY_SEC_OBJ 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(!samr_io_q_query_sec_obj("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_sec_obj: unable to unmarshall SAMR_Q_QUERY_SEC_OBJ.\n")); + return False; + } + + r_u.status = _samr_query_sec_obj(p, &q_u, &r_u); + + if(!samr_io_r_query_sec_obj("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_sec_obj: unable to marshall SAMR_R_QUERY_SEC_OBJ.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_enum_dom_users + ********************************************************************/ + +static BOOL api_samr_enum_dom_users(pipes_struct *p) +{ + SAMR_Q_ENUM_DOM_USERS q_u; + SAMR_R_ENUM_DOM_USERS r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open */ + if(!samr_io_q_enum_dom_users("", &q_u, data, 0)) { + DEBUG(0,("api_samr_enum_dom_users: unable to unmarshall SAMR_Q_ENUM_DOM_USERS.\n")); + return False; + } + + r_u.status = _samr_enum_dom_users(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_enum_dom_users("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_enum_dom_users: unable to marshall SAMR_R_ENUM_DOM_USERS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_enum_dom_groups + ********************************************************************/ + +static BOOL api_samr_enum_dom_groups(pipes_struct *p) +{ + SAMR_Q_ENUM_DOM_GROUPS q_u; + SAMR_R_ENUM_DOM_GROUPS r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open */ + if(!samr_io_q_enum_dom_groups("", &q_u, data, 0)) { + DEBUG(0,("api_samr_enum_dom_groups: unable to unmarshall SAMR_Q_ENUM_DOM_GROUPS.\n")); + return False; + } + + r_u.status = _samr_enum_dom_groups(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_enum_dom_groups("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_enum_dom_groups: unable to marshall SAMR_R_ENUM_DOM_GROUPS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_enum_dom_aliases + ********************************************************************/ + +static BOOL api_samr_enum_dom_aliases(pipes_struct *p) +{ + SAMR_Q_ENUM_DOM_ALIASES q_u; + SAMR_R_ENUM_DOM_ALIASES r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open */ + if(!samr_io_q_enum_dom_aliases("", &q_u, data, 0)) { + DEBUG(0,("api_samr_enum_dom_aliases: unable to unmarshall SAMR_Q_ENUM_DOM_ALIASES.\n")); + return False; + } + + r_u.status = _samr_enum_dom_aliases(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_enum_dom_aliases("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_enum_dom_aliases: unable to marshall SAMR_R_ENUM_DOM_ALIASES.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_dispinfo + ********************************************************************/ + +static BOOL api_samr_query_dispinfo(pipes_struct *p) +{ + SAMR_Q_QUERY_DISPINFO q_u; + SAMR_R_QUERY_DISPINFO 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(!samr_io_q_query_dispinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_dispinfo: unable to unmarshall SAMR_Q_QUERY_DISPINFO.\n")); + return False; + } + + r_u.status = _samr_query_dispinfo(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_query_dispinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_dispinfo: unable to marshall SAMR_R_QUERY_DISPINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_aliasinfo + ********************************************************************/ + +static BOOL api_samr_query_aliasinfo(pipes_struct *p) +{ + SAMR_Q_QUERY_ALIASINFO q_u; + SAMR_R_QUERY_ALIASINFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open */ + if(!samr_io_q_query_aliasinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_aliasinfo: unable to unmarshall SAMR_Q_QUERY_ALIASINFO.\n")); + return False; + } + + r_u.status = _samr_query_aliasinfo(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_query_aliasinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_aliasinfo: unable to marshall SAMR_R_QUERY_ALIASINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_lookup_names + ********************************************************************/ + +static BOOL api_samr_lookup_names(pipes_struct *p) +{ + SAMR_Q_LOOKUP_NAMES q_u; + SAMR_R_LOOKUP_NAMES r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr lookup names */ + if(!samr_io_q_lookup_names("", &q_u, data, 0)) { + DEBUG(0,("api_samr_lookup_names: unable to unmarshall SAMR_Q_LOOKUP_NAMES.\n")); + return False; + } + + r_u.status = _samr_lookup_names(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_lookup_names("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_lookup_names: unable to marshall SAMR_R_LOOKUP_NAMES.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_chgpasswd_user + ********************************************************************/ + +static BOOL api_samr_chgpasswd_user(pipes_struct *p) +{ + SAMR_Q_CHGPASSWD_USER q_u; + SAMR_R_CHGPASSWD_USER r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* unknown 38 command */ + if (!samr_io_q_chgpasswd_user("", &q_u, data, 0)) { + DEBUG(0,("api_samr_chgpasswd_user: Failed to unmarshall SAMR_Q_CHGPASSWD_USER.\n")); + return False; + } + + r_u.status = _samr_chgpasswd_user(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_chgpasswd_user("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_chgpasswd_user: Failed to marshall SAMR_R_CHGPASSWD_USER.\n" )); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_lookup_rids + ********************************************************************/ + +static BOOL api_samr_lookup_rids(pipes_struct *p) +{ + SAMR_Q_LOOKUP_RIDS q_u; + SAMR_R_LOOKUP_RIDS r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr lookup names */ + if(!samr_io_q_lookup_rids("", &q_u, data, 0)) { + DEBUG(0,("api_samr_lookup_rids: unable to unmarshall SAMR_Q_LOOKUP_RIDS.\n")); + return False; + } + + r_u.status = _samr_lookup_rids(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_lookup_rids("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_lookup_rids: unable to marshall SAMR_R_LOOKUP_RIDS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_open_user + ********************************************************************/ + +static BOOL api_samr_open_user(pipes_struct *p) +{ + SAMR_Q_OPEN_USER q_u; + SAMR_R_OPEN_USER r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr unknown 22 */ + if(!samr_io_q_open_user("", &q_u, data, 0)) { + DEBUG(0,("api_samr_open_user: unable to unmarshall SAMR_Q_OPEN_USER.\n")); + return False; + } + + r_u.status = _api_samr_open_user(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_open_user("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_open_user: unable to marshall SAMR_R_OPEN_USER.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_userinfo + ********************************************************************/ + +static BOOL api_samr_query_userinfo(pipes_struct *p) +{ + SAMR_Q_QUERY_USERINFO q_u; + SAMR_R_QUERY_USERINFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr unknown 24 */ + if(!samr_io_q_query_userinfo("", &q_u, data, 0)){ + DEBUG(0,("api_samr_query_userinfo: unable to unmarshall SAMR_Q_QUERY_USERINFO.\n")); + return False; + } + + r_u.status = _samr_query_userinfo(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_query_userinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_userinfo: unable to marshall SAMR_R_QUERY_USERINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_usergroups + ********************************************************************/ + +static BOOL api_samr_query_usergroups(pipes_struct *p) +{ + SAMR_Q_QUERY_USERGROUPS q_u; + SAMR_R_QUERY_USERGROUPS r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr unknown 32 */ + if(!samr_io_q_query_usergroups("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_usergroups: unable to unmarshall SAMR_Q_QUERY_USERGROUPS.\n")); + return False; + } + + r_u.status = _samr_query_usergroups(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_query_usergroups("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_usergroups: unable to marshall SAMR_R_QUERY_USERGROUPS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_dom_info + ********************************************************************/ + +static BOOL api_samr_query_dom_info(pipes_struct *p) +{ + SAMR_Q_QUERY_DOMAIN_INFO q_u; + SAMR_R_QUERY_DOMAIN_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr unknown 8 command */ + if(!samr_io_q_query_dom_info("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_dom_info: unable to unmarshall SAMR_Q_QUERY_DOMAIN_INFO.\n")); + return False; + } + + r_u.status = _samr_query_dom_info(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_query_dom_info("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_dom_info: unable to marshall SAMR_R_QUERY_DOMAIN_INFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_create_user + ********************************************************************/ + +static BOOL api_samr_create_user(pipes_struct *p) +{ + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + SAMR_Q_CREATE_USER q_u; + SAMR_R_CREATE_USER r_u; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr create user */ + if (!samr_io_q_create_user("", &q_u, data, 0)) { + DEBUG(0,("api_samr_create_user: Unable to unmarshall SAMR_Q_CREATE_USER.\n")); + return False; + } + + r_u.status=_api_samr_create_user(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_create_user("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_create_user: Unable to marshall SAMR_R_CREATE_USER.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_connect_anon + ********************************************************************/ + +static BOOL api_samr_connect_anon(pipes_struct *p) +{ + SAMR_Q_CONNECT_ANON q_u; + SAMR_R_CONNECT_ANON r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open policy */ + if(!samr_io_q_connect_anon("", &q_u, data, 0)) { + DEBUG(0,("api_samr_connect_anon: unable to unmarshall SAMR_Q_CONNECT_ANON.\n")); + return False; + } + + r_u.status = _samr_connect_anon(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_connect_anon("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_connect_anon: unable to marshall SAMR_R_CONNECT_ANON.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_connect + ********************************************************************/ + +static BOOL api_samr_connect(pipes_struct *p) +{ + SAMR_Q_CONNECT q_u; + SAMR_R_CONNECT r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open policy */ + if(!samr_io_q_connect("", &q_u, data, 0)) { + DEBUG(0,("api_samr_connect: unable to unmarshall SAMR_Q_CONNECT.\n")); + return False; + } + + r_u.status = _samr_connect(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_connect("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_connect: unable to marshall SAMR_R_CONNECT.\n")); + return False; + } + + return True; +} + +/********************************************************************** + api_samr_lookup_domain + **********************************************************************/ + +static BOOL api_samr_lookup_domain(pipes_struct *p) +{ + SAMR_Q_LOOKUP_DOMAIN q_u; + SAMR_R_LOOKUP_DOMAIN 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(!samr_io_q_lookup_domain("", &q_u, data, 0)) { + DEBUG(0,("api_samr_lookup_domain: Unable to unmarshall SAMR_Q_LOOKUP_DOMAIN.\n")); + return False; + } + + r_u.status = _samr_lookup_domain(p, &q_u, &r_u); + + if(!samr_io_r_lookup_domain("", &r_u, rdata, 0)){ + DEBUG(0,("api_samr_lookup_domain: Unable to marshall SAMR_R_LOOKUP_DOMAIN.\n")); + return False; + } + + return True; +} + +/********************************************************************** + api_samr_enum_domains + **********************************************************************/ + +static BOOL api_samr_enum_domains(pipes_struct *p) +{ + SAMR_Q_ENUM_DOMAINS q_u; + SAMR_R_ENUM_DOMAINS 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(!samr_io_q_enum_domains("", &q_u, data, 0)) { + DEBUG(0,("api_samr_enum_domains: Unable to unmarshall SAMR_Q_ENUM_DOMAINS.\n")); + return False; + } + + r_u.status = _samr_enum_domains(p, &q_u, &r_u); + + if(!samr_io_r_enum_domains("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_enum_domains: Unable to marshall SAMR_R_ENUM_DOMAINS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_open_alias + ********************************************************************/ + +static BOOL api_samr_open_alias(pipes_struct *p) +{ + SAMR_Q_OPEN_ALIAS q_u; + SAMR_R_OPEN_ALIAS r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr open policy */ + if(!samr_io_q_open_alias("", &q_u, data, 0)) { + DEBUG(0,("api_samr_open_alias: Unable to unmarshall SAMR_Q_OPEN_ALIAS.\n")); + return False; + } + + r_u.status=_api_samr_open_alias(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_open_alias("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_open_alias: Unable to marshall SAMR_R_OPEN_ALIAS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_set_userinfo + ********************************************************************/ + +static BOOL api_samr_set_userinfo(pipes_struct *p) +{ + SAMR_Q_SET_USERINFO q_u; + SAMR_R_SET_USERINFO 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 (!samr_io_q_set_userinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_set_userinfo: Unable to unmarshall SAMR_Q_SET_USERINFO.\n")); + /* Fix for W2K SP2 */ + if (q_u.switch_value == 0x1a) { + setup_fault_pdu(p, NT_STATUS(0x1c000006)); + return True; + } + return False; + } + + r_u.status = _samr_set_userinfo(p, &q_u, &r_u); + + if(!samr_io_r_set_userinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_set_userinfo: Unable to marshall SAMR_R_SET_USERINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_set_userinfo2 + ********************************************************************/ + +static BOOL api_samr_set_userinfo2(pipes_struct *p) +{ + SAMR_Q_SET_USERINFO2 q_u; + SAMR_R_SET_USERINFO2 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 (!samr_io_q_set_userinfo2("", &q_u, data, 0)) { + DEBUG(0,("api_samr_set_userinfo2: Unable to unmarshall SAMR_Q_SET_USERINFO2.\n")); + return False; + } + + r_u.status = _samr_set_userinfo2(p, &q_u, &r_u); + + if(!samr_io_r_set_userinfo2("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_set_userinfo2: Unable to marshall SAMR_R_SET_USERINFO2.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_useraliases + ********************************************************************/ + +static BOOL api_samr_query_useraliases(pipes_struct *p) +{ + SAMR_Q_QUERY_USERALIASES q_u; + SAMR_R_QUERY_USERALIASES 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 (!samr_io_q_query_useraliases("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_useraliases: Unable to unmarshall SAMR_Q_QUERY_USERALIASES.\n")); + return False; + } + + r_u.status = _samr_query_useraliases(p, &q_u, &r_u); + + if (! samr_io_r_query_useraliases("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_useraliases: Unable to nmarshall SAMR_R_QUERY_USERALIASES.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_aliasmem + ********************************************************************/ + +static BOOL api_samr_query_aliasmem(pipes_struct *p) +{ + SAMR_Q_QUERY_ALIASMEM q_u; + SAMR_R_QUERY_ALIASMEM 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 (!samr_io_q_query_aliasmem("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_aliasmem: unable to unmarshall SAMR_Q_QUERY_ALIASMEM.\n")); + return False; + } + + r_u.status = _samr_query_aliasmem(p, &q_u, &r_u); + + if (!samr_io_r_query_aliasmem("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_aliasmem: unable to marshall SAMR_R_QUERY_ALIASMEM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_groupmem + ********************************************************************/ + +static BOOL api_samr_query_groupmem(pipes_struct *p) +{ + SAMR_Q_QUERY_GROUPMEM q_u; + SAMR_R_QUERY_GROUPMEM 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 (!samr_io_q_query_groupmem("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_groupmem: unable to unmarshall SAMR_Q_QUERY_GROUPMEM.\n")); + return False; + } + + r_u.status = _samr_query_groupmem(p, &q_u, &r_u); + + if (!samr_io_r_query_groupmem("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_groupmem: unable to marshall SAMR_R_QUERY_GROUPMEM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_add_aliasmem + ********************************************************************/ + +static BOOL api_samr_add_aliasmem(pipes_struct *p) +{ + SAMR_Q_ADD_ALIASMEM q_u; + SAMR_R_ADD_ALIASMEM 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 (!samr_io_q_add_aliasmem("", &q_u, data, 0)) { + DEBUG(0,("api_samr_add_aliasmem: unable to unmarshall SAMR_Q_ADD_ALIASMEM.\n")); + return False; + } + + r_u.status = _samr_add_aliasmem(p, &q_u, &r_u); + + if (!samr_io_r_add_aliasmem("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_add_aliasmem: unable to marshall SAMR_R_ADD_ALIASMEM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_del_aliasmem + ********************************************************************/ + +static BOOL api_samr_del_aliasmem(pipes_struct *p) +{ + SAMR_Q_DEL_ALIASMEM q_u; + SAMR_R_DEL_ALIASMEM 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 (!samr_io_q_del_aliasmem("", &q_u, data, 0)) { + DEBUG(0,("api_samr_del_aliasmem: unable to unmarshall SAMR_Q_DEL_ALIASMEM.\n")); + return False; + } + + r_u.status = _samr_del_aliasmem(p, &q_u, &r_u); + + if (!samr_io_r_del_aliasmem("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_del_aliasmem: unable to marshall SAMR_R_DEL_ALIASMEM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_add_groupmem + ********************************************************************/ + +static BOOL api_samr_add_groupmem(pipes_struct *p) +{ + SAMR_Q_ADD_GROUPMEM q_u; + SAMR_R_ADD_GROUPMEM 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 (!samr_io_q_add_groupmem("", &q_u, data, 0)) { + DEBUG(0,("api_samr_add_groupmem: unable to unmarshall SAMR_Q_ADD_GROUPMEM.\n")); + return False; + } + + r_u.status = _samr_add_groupmem(p, &q_u, &r_u); + + if (!samr_io_r_add_groupmem("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_add_groupmem: unable to marshall SAMR_R_ADD_GROUPMEM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_del_groupmem + ********************************************************************/ + +static BOOL api_samr_del_groupmem(pipes_struct *p) +{ + SAMR_Q_DEL_GROUPMEM q_u; + SAMR_R_DEL_GROUPMEM 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 (!samr_io_q_del_groupmem("", &q_u, data, 0)) { + DEBUG(0,("api_samr_del_groupmem: unable to unmarshall SAMR_Q_DEL_GROUPMEM.\n")); + return False; + } + + r_u.status = _samr_del_groupmem(p, &q_u, &r_u); + + if (!samr_io_r_del_groupmem("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_del_groupmem: unable to marshall SAMR_R_DEL_GROUPMEM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_delete_dom_user + ********************************************************************/ + +static BOOL api_samr_delete_dom_user(pipes_struct *p) +{ + SAMR_Q_DELETE_DOM_USER q_u; + SAMR_R_DELETE_DOM_USER 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 (!samr_io_q_delete_dom_user("", &q_u, data, 0)) { + DEBUG(0,("api_samr_delete_dom_user: unable to unmarshall SAMR_Q_DELETE_DOM_USER.\n")); + return False; + } + + r_u.status = _samr_delete_dom_user(p, &q_u, &r_u); + + if (!samr_io_r_delete_dom_user("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_delete_dom_user: unable to marshall SAMR_R_DELETE_DOM_USER.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_delete_dom_group + ********************************************************************/ + +static BOOL api_samr_delete_dom_group(pipes_struct *p) +{ + SAMR_Q_DELETE_DOM_GROUP q_u; + SAMR_R_DELETE_DOM_GROUP 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 (!samr_io_q_delete_dom_group("", &q_u, data, 0)) { + DEBUG(0,("api_samr_delete_dom_group: unable to unmarshall SAMR_Q_DELETE_DOM_GROUP.\n")); + return False; + } + + r_u.status = _samr_delete_dom_group(p, &q_u, &r_u); + + if (!samr_io_r_delete_dom_group("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_delete_dom_group: unable to marshall SAMR_R_DELETE_DOM_GROUP.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_delete_dom_alias + ********************************************************************/ + +static BOOL api_samr_delete_dom_alias(pipes_struct *p) +{ + SAMR_Q_DELETE_DOM_ALIAS q_u; + SAMR_R_DELETE_DOM_ALIAS 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 (!samr_io_q_delete_dom_alias("", &q_u, data, 0)) { + DEBUG(0,("api_samr_delete_dom_alias: unable to unmarshall SAMR_Q_DELETE_DOM_ALIAS.\n")); + return False; + } + + r_u.status = _samr_delete_dom_alias(p, &q_u, &r_u); + + if (!samr_io_r_delete_dom_alias("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_delete_dom_alias: unable to marshall SAMR_R_DELETE_DOM_ALIAS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_create_dom_group + ********************************************************************/ + +static BOOL api_samr_create_dom_group(pipes_struct *p) +{ + SAMR_Q_CREATE_DOM_GROUP q_u; + SAMR_R_CREATE_DOM_GROUP 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 (!samr_io_q_create_dom_group("", &q_u, data, 0)) { + DEBUG(0,("api_samr_create_dom_group: unable to unmarshall SAMR_Q_CREATE_DOM_GROUP.\n")); + return False; + } + + r_u.status = _samr_create_dom_group(p, &q_u, &r_u); + + if (!samr_io_r_create_dom_group("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_create_dom_group: unable to marshall SAMR_R_CREATE_DOM_GROUP.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_create_dom_alias + ********************************************************************/ + +static BOOL api_samr_create_dom_alias(pipes_struct *p) +{ + SAMR_Q_CREATE_DOM_ALIAS q_u; + SAMR_R_CREATE_DOM_ALIAS 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 (!samr_io_q_create_dom_alias("", &q_u, data, 0)) { + DEBUG(0,("api_samr_create_dom_alias: unable to unmarshall SAMR_Q_CREATE_DOM_ALIAS.\n")); + return False; + } + + r_u.status = _samr_create_dom_alias(p, &q_u, &r_u); + + if (!samr_io_r_create_dom_alias("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_create_dom_alias: unable to marshall SAMR_R_CREATE_DOM_ALIAS.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_groupinfo + ********************************************************************/ + +static BOOL api_samr_query_groupinfo(pipes_struct *p) +{ + SAMR_Q_QUERY_GROUPINFO q_u; + SAMR_R_QUERY_GROUPINFO 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 (!samr_io_q_query_groupinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_query_groupinfo: unable to unmarshall SAMR_Q_QUERY_GROUPINFO.\n")); + return False; + } + + r_u.status = _samr_query_groupinfo(p, &q_u, &r_u); + + if (!samr_io_r_query_groupinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_query_groupinfo: unable to marshall SAMR_R_QUERY_GROUPINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_set_groupinfo + ********************************************************************/ + +static BOOL api_samr_set_groupinfo(pipes_struct *p) +{ + SAMR_Q_SET_GROUPINFO q_u; + SAMR_R_SET_GROUPINFO 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 (!samr_io_q_set_groupinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_set_groupinfo: unable to unmarshall SAMR_Q_SET_GROUPINFO.\n")); + return False; + } + + r_u.status = _samr_set_groupinfo(p, &q_u, &r_u); + + if (!samr_io_r_set_groupinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_set_groupinfo: unable to marshall SAMR_R_SET_GROUPINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_set_aliasinfo + ********************************************************************/ + +static BOOL api_samr_set_aliasinfo(pipes_struct *p) +{ + SAMR_Q_SET_ALIASINFO q_u; + SAMR_R_SET_ALIASINFO 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 (!samr_io_q_set_aliasinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_set_aliasinfo: unable to unmarshall SAMR_Q_SET_ALIASINFO.\n")); + return False; + } + + r_u.status = _samr_set_aliasinfo(p, &q_u, &r_u); + + if (!samr_io_r_set_aliasinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_set_aliasinfo: unable to marshall SAMR_R_SET_ALIASINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_get_dom_pwinfo + ********************************************************************/ + +static BOOL api_samr_get_dom_pwinfo(pipes_struct *p) +{ + SAMR_Q_GET_DOM_PWINFO q_u; + SAMR_R_GET_DOM_PWINFO 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 (!samr_io_q_get_dom_pwinfo("", &q_u, data, 0)) { + DEBUG(0,("api_samr_get_dom_pwinfo: unable to unmarshall SAMR_Q_GET_DOM_PWINFO.\n")); + return False; + } + + r_u.status = _samr_get_dom_pwinfo(p, &q_u, &r_u); + + if (!samr_io_r_get_dom_pwinfo("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_get_dom_pwinfo: unable to marshall SAMR_R_GET_DOM_PWINFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_open_group + ********************************************************************/ + +static BOOL api_samr_open_group(pipes_struct *p) +{ + SAMR_Q_OPEN_GROUP q_u; + SAMR_R_OPEN_GROUP 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 (!samr_io_q_open_group("", &q_u, data, 0)) { + DEBUG(0,("api_samr_open_group: unable to unmarshall SAMR_Q_OPEN_GROUP.\n")); + return False; + } + + r_u.status = _samr_open_group(p, &q_u, &r_u); + + if (!samr_io_r_open_group("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_open_group: unable to marshall SAMR_R_OPEN_GROUP.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_unknown_2d + ********************************************************************/ + +static BOOL api_samr_unknown_2d(pipes_struct *p) +{ + SAMR_Q_UNKNOWN_2D q_u; + SAMR_R_UNKNOWN_2D 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 (!samr_io_q_unknown_2d("", &q_u, data, 0)) { + DEBUG(0,("api_samr_unknown_2d: unable to unmarshall SAMR_Q_UNKNOWN_2D.\n")); + return False; + } + + r_u.status = _samr_unknown_2d(p, &q_u, &r_u); + + if (!samr_io_r_unknown_2d("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_unknown_2d: unable to marshall SAMR_R_UNKNOWN_2D.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_query_dom_info + ********************************************************************/ + +static BOOL api_samr_unknown_2e(pipes_struct *p) +{ + SAMR_Q_UNKNOWN_2E q_u; + SAMR_R_UNKNOWN_2E r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr unknown 8 command */ + if(!samr_io_q_unknown_2e("", &q_u, data, 0)) { + DEBUG(0,("api_samr_unknown_2e: unable to unmarshall SAMR_Q_UNKNOWN_2E.\n")); + return False; + } + + r_u.status = _samr_unknown_2e(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_samr_unknown_2e("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_unknown_2e: unable to marshall SAMR_R_UNKNOWN_2E.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_samr_set_dom_info + ********************************************************************/ + +static BOOL api_samr_set_dom_info(pipes_struct *p) +{ + SAMR_Q_SET_DOMAIN_INFO q_u; + SAMR_R_SET_DOMAIN_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the samr unknown 8 command */ + if(!samr_io_q_set_domain_info("", &q_u, data, 0)) { + DEBUG(0,("api_samr_set_dom_info: unable to unmarshall SAMR_Q_SET_DOMAIN_INFO.\n")); + return False; + } + + r_u.status = _samr_set_dom_info(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!samr_io_r_set_domain_info("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_set_dom_info: unable to marshall SAMR_R_SET_DOMAIN_INFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + array of \PIPE\samr operations + ********************************************************************/ + +static struct api_struct api_samr_cmds [] = +{ + {"SAMR_CLOSE_HND" , SAMR_CLOSE_HND , api_samr_close_hnd }, + {"SAMR_CONNECT" , SAMR_CONNECT , api_samr_connect }, + {"SAMR_CONNECT_ANON" , SAMR_CONNECT_ANON , api_samr_connect_anon }, + {"SAMR_ENUM_DOMAINS" , SAMR_ENUM_DOMAINS , api_samr_enum_domains }, + {"SAMR_ENUM_DOM_USERS" , SAMR_ENUM_DOM_USERS , api_samr_enum_dom_users }, + + {"SAMR_ENUM_DOM_GROUPS" , SAMR_ENUM_DOM_GROUPS , api_samr_enum_dom_groups }, + {"SAMR_ENUM_DOM_ALIASES" , SAMR_ENUM_DOM_ALIASES , api_samr_enum_dom_aliases }, + {"SAMR_QUERY_USERALIASES" , SAMR_QUERY_USERALIASES, api_samr_query_useraliases}, + {"SAMR_QUERY_ALIASMEM" , SAMR_QUERY_ALIASMEM , api_samr_query_aliasmem }, + {"SAMR_QUERY_GROUPMEM" , SAMR_QUERY_GROUPMEM , api_samr_query_groupmem }, + {"SAMR_ADD_ALIASMEM" , SAMR_ADD_ALIASMEM , api_samr_add_aliasmem }, + {"SAMR_DEL_ALIASMEM" , SAMR_DEL_ALIASMEM , api_samr_del_aliasmem }, + {"SAMR_ADD_GROUPMEM" , SAMR_ADD_GROUPMEM , api_samr_add_groupmem }, + {"SAMR_DEL_GROUPMEM" , SAMR_DEL_GROUPMEM , api_samr_del_groupmem }, + + {"SAMR_DELETE_DOM_USER" , SAMR_DELETE_DOM_USER , api_samr_delete_dom_user }, + {"SAMR_DELETE_DOM_GROUP" , SAMR_DELETE_DOM_GROUP , api_samr_delete_dom_group }, + {"SAMR_DELETE_DOM_ALIAS" , SAMR_DELETE_DOM_ALIAS , api_samr_delete_dom_alias }, + {"SAMR_CREATE_DOM_GROUP" , SAMR_CREATE_DOM_GROUP , api_samr_create_dom_group }, + {"SAMR_CREATE_DOM_ALIAS" , SAMR_CREATE_DOM_ALIAS , api_samr_create_dom_alias }, + {"SAMR_LOOKUP_NAMES" , SAMR_LOOKUP_NAMES , api_samr_lookup_names }, + {"SAMR_OPEN_USER" , SAMR_OPEN_USER , api_samr_open_user }, + {"SAMR_QUERY_USERINFO" , SAMR_QUERY_USERINFO , api_samr_query_userinfo }, + {"SAMR_SET_USERINFO" , SAMR_SET_USERINFO , api_samr_set_userinfo }, + {"SAMR_SET_USERINFO2" , SAMR_SET_USERINFO2 , api_samr_set_userinfo2 }, + + {"SAMR_QUERY_DOMAIN_INFO" , SAMR_QUERY_DOMAIN_INFO, api_samr_query_dom_info }, + {"SAMR_QUERY_USERGROUPS" , SAMR_QUERY_USERGROUPS , api_samr_query_usergroups }, + {"SAMR_QUERY_DISPINFO" , SAMR_QUERY_DISPINFO , api_samr_query_dispinfo }, + {"SAMR_QUERY_DISPINFO3" , SAMR_QUERY_DISPINFO3 , api_samr_query_dispinfo }, + {"SAMR_QUERY_DISPINFO4" , SAMR_QUERY_DISPINFO4 , api_samr_query_dispinfo }, + + {"SAMR_QUERY_ALIASINFO" , SAMR_QUERY_ALIASINFO , api_samr_query_aliasinfo }, + {"SAMR_QUERY_GROUPINFO" , SAMR_QUERY_GROUPINFO , api_samr_query_groupinfo }, + {"SAMR_SET_GROUPINFO" , SAMR_SET_GROUPINFO , api_samr_set_groupinfo }, + {"SAMR_SET_ALIASINFO" , SAMR_SET_ALIASINFO , api_samr_set_aliasinfo }, + {"SAMR_CREATE_USER" , SAMR_CREATE_USER , api_samr_create_user }, + {"SAMR_LOOKUP_RIDS" , SAMR_LOOKUP_RIDS , api_samr_lookup_rids }, + {"SAMR_GET_DOM_PWINFO" , SAMR_GET_DOM_PWINFO , api_samr_get_dom_pwinfo }, + {"SAMR_CHGPASSWD_USER" , SAMR_CHGPASSWD_USER , api_samr_chgpasswd_user }, + {"SAMR_OPEN_ALIAS" , SAMR_OPEN_ALIAS , api_samr_open_alias }, + {"SAMR_OPEN_GROUP" , SAMR_OPEN_GROUP , api_samr_open_group }, + {"SAMR_OPEN_DOMAIN" , SAMR_OPEN_DOMAIN , api_samr_open_domain }, + {"SAMR_UNKNOWN_2D" , SAMR_UNKNOWN_2D , api_samr_unknown_2d }, + {"SAMR_LOOKUP_DOMAIN" , SAMR_LOOKUP_DOMAIN , api_samr_lookup_domain }, + + {"SAMR_QUERY_SEC_OBJECT" , SAMR_QUERY_SEC_OBJECT , api_samr_query_sec_obj }, + {"SAMR_GET_USRDOM_PWINFO" , SAMR_GET_USRDOM_PWINFO, api_samr_get_usrdom_pwinfo}, + {"SAMR_UNKNOWN_2E" , SAMR_UNKNOWN_2E , api_samr_unknown_2e }, + {"SAMR_SET_DOMAIN_INFO" , SAMR_SET_DOMAIN_INFO , api_samr_set_dom_info }, + {NULL , 0 , NULL } +}; + +/******************************************************************* + receives a samr pipe and responds. + ********************************************************************/ +BOOL api_samr_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_samr_rpc", api_samr_cmds); +} diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c new file mode 100644 index 0000000000..c83f6b3d8d --- /dev/null +++ b/source3/rpc_server/srv_samr_nt.c @@ -0,0 +1,3826 @@ +/* + * 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-2002. + * Copyright (C) Jean François Micouleau 1998-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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This is the implementation of the SAMR code. + */ + +#include "includes.h" + +extern fstring global_myworkgroup; +extern pstring global_myname; +extern DOM_SID global_sam_sid; +extern DOM_SID global_sid_Builtin; + +extern rid_name domain_group_rids[]; +extern rid_name domain_alias_rids[]; +extern rid_name builtin_alias_rids[]; + + +typedef struct _disp_info { + BOOL user_dbloaded; + uint32 num_user_account; + DISP_USER_INFO *disp_user_info; + BOOL group_dbloaded; + uint32 num_group_account; + DISP_GROUP_INFO *disp_group_info; +} DISP_INFO; + +struct samr_info { + /* for use by the \PIPE\samr policy */ + DOM_SID sid; + uint32 status; /* some sort of flag. best to record it. comes from opnum 0x39 */ + DISP_INFO disp_info; +}; + +/******************************************************************* + Create a samr_info struct. +********************************************************************/ + +static struct samr_info *get_samr_info_by_sid(DOM_SID *psid) +{ + struct samr_info *info; + fstring sid_str; + + if ((info = (struct samr_info *)malloc(sizeof(struct samr_info))) == NULL) + return NULL; + + ZERO_STRUCTP(info); + if (psid) { + DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_to_string(sid_str, psid) )); + sid_copy( &info->sid, psid); + } else { + DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n")); + } + return info; +} + +/******************************************************************* + Function to free the per handle data. + ********************************************************************/ +static void free_samr_db(struct samr_info *info) +{ + int i; + + if (info->disp_info.group_dbloaded) { + for (i=0; i<info->disp_info.num_group_account; i++) + SAFE_FREE(info->disp_info.disp_group_info[i].grp); + + SAFE_FREE(info->disp_info.disp_group_info); + } + + if (info->disp_info.user_dbloaded){ + for (i=0; i<info->disp_info.num_user_account; i++) + pdb_free_sam(&info->disp_info.disp_user_info[i].sam); + + SAFE_FREE(info->disp_info.disp_user_info); + } + + info->disp_info.user_dbloaded=False; + info->disp_info.group_dbloaded=False; + info->disp_info.num_group_account=0; + info->disp_info.num_user_account=0; +} + + +static void free_samr_info(void *ptr) +{ + struct samr_info *info=(struct samr_info *) ptr; + + free_samr_db(info); + SAFE_FREE(info); +} + +/******************************************************************* + Ensure password info is never given out. Paranioa... JRA. + ********************************************************************/ + +static void samr_clear_passwd_fields( SAM_USER_INFO_21 *pass, int num_entries) +{ + int i; + + if (!pass) + return; + + for (i = 0; i < num_entries; i++) { + memset(&pass[i].lm_pwd, '\0', sizeof(pass[i].lm_pwd)); + memset(&pass[i].nt_pwd, '\0', sizeof(pass[i].nt_pwd)); + } +} + +static void samr_clear_sam_passwd(SAM_ACCOUNT *sam_pass) +{ + + if (!sam_pass) + return; + + /* These now zero out the old password */ + + pdb_set_lanman_passwd(sam_pass, NULL); + pdb_set_nt_passwd(sam_pass, NULL); +} + + +static NTSTATUS load_sampwd_entries(struct samr_info *info, uint16 acb_mask) +{ + SAM_ACCOUNT *pwd = NULL; + DISP_USER_INFO *pwd_array = NULL; + + DEBUG(10,("load_sampwd_entries\n")); + + /* if the snapshoot is already loaded, return */ + if (info->disp_info.user_dbloaded==True) { + DEBUG(10,("load_sampwd_entries: already in memory\n")); + return NT_STATUS_OK; + } + + if (!pdb_setsampwent(False)) { + DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n")); + return NT_STATUS_ACCESS_DENIED; + } + + for (pdb_init_sam(&pwd); pdb_getsampwent(pwd) == True; pwd=NULL, pdb_init_sam(&pwd) ) { + + if (acb_mask != 0 && !(pdb_get_acct_ctrl(pwd) & acb_mask)) { + pdb_free_sam(&pwd); + DEBUG(5,(" acb_mask %x reject\n", acb_mask)); + continue; + } + + /* Realloc some memory for the array of ptr to the SAM_ACCOUNT structs */ + if (info->disp_info.num_user_account % MAX_SAM_ENTRIES == 0) { + + DEBUG(10,("load_sampwd_entries: allocating more memory\n")); + pwd_array=(DISP_USER_INFO *)Realloc(info->disp_info.disp_user_info, + (info->disp_info.num_user_account+MAX_SAM_ENTRIES)*sizeof(DISP_USER_INFO)); + + if (pwd_array==NULL) + return NT_STATUS_NO_MEMORY; + + info->disp_info.disp_user_info=pwd_array; + } + + /* link the SAM_ACCOUNT to the array */ + info->disp_info.disp_user_info[info->disp_info.num_user_account].sam=pwd; + + DEBUG(10,("load_sampwd_entries: entry: %d\n", info->disp_info.num_user_account)); + + info->disp_info.num_user_account++; + } + + pdb_endsampwent(); + + /* the snapshoot is in memory, we're ready to enumerate fast */ + + info->disp_info.user_dbloaded=True; + + DEBUG(12,("load_sampwd_entries: done\n")); + + return NT_STATUS_OK; +} + +static NTSTATUS load_group_domain_entries(struct samr_info *info, DOM_SID *sid) +{ + GROUP_MAP *map=NULL; + DISP_GROUP_INFO *grp_array = NULL; + uint32 group_entries = 0; + uint32 i; + + DEBUG(10,("load_group_domain_entries\n")); + + /* if the snapshoot is already loaded, return */ + if (info->disp_info.group_dbloaded==True) { + DEBUG(10,("load_group_domain_entries: already in memory\n")); + return NT_STATUS_OK; + } + + enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV); + + info->disp_info.num_group_account=group_entries; + + grp_array=(DISP_GROUP_INFO *)malloc(info->disp_info.num_group_account*sizeof(DISP_GROUP_INFO)); + + if (group_entries!=0 && grp_array==NULL) { + SAFE_FREE(map); + return NT_STATUS_NO_MEMORY; + } + + info->disp_info.disp_group_info=grp_array; + + for (i=0; i<group_entries; i++) { + + grp_array[i].grp=(DOMAIN_GRP *)malloc(sizeof(DOMAIN_GRP)); + + fstrcpy(grp_array[i].grp->name, map[i].nt_name); + fstrcpy(grp_array[i].grp->comment, map[i].comment); + sid_split_rid(&map[i].sid, &grp_array[i].grp->rid); + grp_array[i].grp->attr=SID_NAME_DOM_GRP; + } + + SAFE_FREE(map); + + /* the snapshoot is in memory, we're ready to enumerate fast */ + + info->disp_info.group_dbloaded=True; + + DEBUG(12,("load_group_domain_entries: done\n")); + + return NT_STATUS_OK; +} + + +/******************************************************************* + This next function should be replaced with something that + dynamically returns the correct user info..... JRA. + ********************************************************************/ + +static NTSTATUS get_sampwd_entries(SAM_USER_INFO_21 *pw_buf, int start_idx, + int *total_entries, int *num_entries, + int max_num_entries, uint16 acb_mask) +{ + SAM_ACCOUNT *pwd = NULL; + BOOL not_finished = True; + + (*num_entries) = 0; + (*total_entries) = 0; + + if (pw_buf == NULL) + return NT_STATUS_NO_MEMORY; + + pdb_init_sam(&pwd); + + if (!pdb_setsampwent(False)) { + DEBUG(0, ("get_sampwd_entries: Unable to open passdb.\n")); + pdb_free_sam(&pwd); + return NT_STATUS_ACCESS_DENIED; + } + + while (((not_finished = pdb_getsampwent(pwd)) != False) + && (*num_entries) < max_num_entries) + { + int user_name_len; + + if (start_idx > 0) { + + pdb_reset_sam(pwd); + + /* skip the requested number of entries. + not very efficient, but hey... */ + start_idx--; + continue; + } + + user_name_len = strlen(pdb_get_username(pwd))+1; + init_unistr2(&pw_buf[(*num_entries)].uni_user_name, pdb_get_username(pwd), user_name_len); + init_uni_hdr(&pw_buf[(*num_entries)].hdr_user_name, user_name_len); + pw_buf[(*num_entries)].user_rid = pdb_get_user_rid(pwd); + memset((char *)pw_buf[(*num_entries)].nt_pwd, '\0', 16); + + /* Now check if the NT compatible password is available. */ + if (pdb_get_nt_passwd(pwd)) + memcpy( pw_buf[(*num_entries)].nt_pwd , pdb_get_nt_passwd(pwd), 16); + + pw_buf[(*num_entries)].acb_info = pdb_get_acct_ctrl(pwd); + + DEBUG(5, ("entry idx: %d user %s, rid 0x%x, acb %x", + (*num_entries), pdb_get_username(pwd), pdb_get_user_rid(pwd), pdb_get_acct_ctrl(pwd) )); + + if (acb_mask == 0 || (pdb_get_acct_ctrl(pwd) & acb_mask)) { + DEBUG(5,(" acb_mask %x accepts\n", acb_mask)); + (*num_entries)++; + } else { + DEBUG(5,(" acb_mask %x rejects\n", acb_mask)); + } + + (*total_entries)++; + + pdb_reset_sam(pwd); + + } + + pdb_endsampwent(); + pdb_free_sam(&pwd); + + if (not_finished) + return STATUS_MORE_ENTRIES; + else + return NT_STATUS_OK; +} + +/******************************************************************* + _samr_close_hnd + ********************************************************************/ + +NTSTATUS _samr_close_hnd(pipes_struct *p, SAMR_Q_CLOSE_HND *q_u, SAMR_R_CLOSE_HND *r_u) +{ + r_u->status = NT_STATUS_OK; + + /* close the policy handle */ + if (!close_policy_hnd(p, &q_u->pol)) + return NT_STATUS_OBJECT_NAME_INVALID; + + DEBUG(5,("samr_reply_close_hnd: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + samr_reply_open_domain + ********************************************************************/ + +NTSTATUS _samr_open_domain(pipes_struct *p, SAMR_Q_OPEN_DOMAIN *q_u, SAMR_R_OPEN_DOMAIN *r_u) +{ + struct samr_info *info; + + r_u->status = NT_STATUS_OK; + + /* find the connection policy handle. */ + if (!find_policy_by_hnd(p, &q_u->pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + /* associate the domain SID with the (unique) handle. */ + if ((info = get_samr_info_by_sid(&q_u->dom_sid.sid))==NULL) + return NT_STATUS_NO_MEMORY; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->domain_pol, free_samr_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + DEBUG(5,("samr_open_domain: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + _samr_get_usrdom_pwinfo + ********************************************************************/ + +NTSTATUS _samr_get_usrdom_pwinfo(pipes_struct *p, SAMR_Q_GET_USRDOM_PWINFO *q_u, SAMR_R_GET_USRDOM_PWINFO *r_u) +{ + struct samr_info *info = NULL; + + r_u->status = NT_STATUS_OK; + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->user_pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_check_is_in_our_domain(&info->sid)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + init_samr_r_get_usrdom_pwinfo(r_u, NT_STATUS_OK); + + DEBUG(5,("_samr_get_usrdom_pwinfo: %d\n", __LINE__)); + + /* + * NT sometimes return NT_STATUS_ACCESS_DENIED + * I don't know yet why. + */ + + return r_u->status; +} + +/******************************************************************* + samr_make_usr_obj_sd + ********************************************************************/ + +static NTSTATUS samr_make_usr_obj_sd(TALLOC_CTX *ctx, SEC_DESC_BUF **buf, DOM_SID *usr_sid) +{ + extern DOM_SID global_sid_World; + DOM_SID adm_sid; + DOM_SID act_sid; + + SEC_ACE ace[4]; + SEC_ACCESS mask; + + SEC_ACL *psa = NULL; + SEC_DESC *psd = NULL; + size_t sd_size; + + sid_copy(&adm_sid, &global_sid_Builtin); + sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS); + + sid_copy(&act_sid, &global_sid_Builtin); + sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS); + + init_sec_access(&mask, 0x2035b); + init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + init_sec_access(&mask, 0xf07ff); + init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + init_sec_access(&mask,0x20044); + init_sec_ace(&ace[3], usr_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + if((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 4, ace)) == NULL) + return NT_STATUS_NO_MEMORY; + + if((psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, &sd_size)) == NULL) + return NT_STATUS_NO_MEMORY; + + if((*buf = make_sec_desc_buf(ctx, sd_size, psd)) == NULL) + return NT_STATUS_NO_MEMORY; + + return NT_STATUS_OK; +} + +static BOOL get_lsa_policy_samr_sid(pipes_struct *p, POLICY_HND *pol, DOM_SID *sid) +{ + struct samr_info *info = NULL; + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, pol, (void **)&info)) + return False; + + if (!info) + return False; + + *sid = info->sid; + return True; +} + +/******************************************************************* + _samr_query_sec_obj + ********************************************************************/ + +NTSTATUS _samr_query_sec_obj(pipes_struct *p, SAMR_Q_QUERY_SEC_OBJ *q_u, SAMR_R_QUERY_SEC_OBJ *r_u) +{ + DOM_SID pol_sid; + fstring str_sid; + + r_u->status = NT_STATUS_OK; + + /* Get the SID. */ + + if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &pol_sid)) + return NT_STATUS_INVALID_HANDLE; + + DEBUG(10,("_samr_query_sec_obj: querying security on SID: %s\n", sid_to_string(str_sid, &pol_sid))); + + r_u->status = samr_make_usr_obj_sd(p->mem_ctx, &r_u->buf, &pol_sid); + + if (NT_STATUS_IS_OK(r_u->status)) + r_u->ptr = 1; + + return r_u->status; +} + +/******************************************************************* +makes a SAM_ENTRY / UNISTR2* structure from a user list. +********************************************************************/ + +static void make_user_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp, + uint32 num_sam_entries, SAM_USER_INFO_21 *pass) +{ + uint32 i; + SAM_ENTRY *sam; + UNISTR2 *uni_name; + + *sam_pp = NULL; + *uni_name_pp = NULL; + + if (num_sam_entries == 0) + return; + + sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries); + + uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries); + + if (sam == NULL || uni_name == NULL) { + DEBUG(0, ("NULL pointers in SAMR_R_QUERY_DISPINFO\n")); + return; + } + + ZERO_STRUCTP(sam); + ZERO_STRUCTP(uni_name); + + for (i = 0; i < num_sam_entries; i++) { + int len = pass[i].uni_user_name.uni_str_len; + + init_sam_entry(&sam[i], len, pass[i].user_rid); + copy_unistr2(&uni_name[i], &pass[i].uni_user_name); + } + + *sam_pp = sam; + *uni_name_pp = uni_name; +} + +/******************************************************************* + samr_reply_enum_dom_users + ********************************************************************/ + +NTSTATUS _samr_enum_dom_users(pipes_struct *p, SAMR_Q_ENUM_DOM_USERS *q_u, SAMR_R_ENUM_DOM_USERS *r_u) +{ + SAM_USER_INFO_21 pass[MAX_SAM_ENTRIES]; + int num_entries = 0; + int total_entries = 0; + + r_u->status = NT_STATUS_OK; + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__)); + + become_root(); + r_u->status = get_sampwd_entries(pass, q_u->start_idx, &total_entries, &num_entries, + MAX_SAM_ENTRIES, q_u->acb_mask); + unbecome_root(); + + if (NT_STATUS_IS_ERR(r_u->status)) + return r_u->status; + + samr_clear_passwd_fields(pass, num_entries); + + /* + * Note from JRA. total_entries is not being used here. Currently if there is a + * large user base then it looks like NT will enumerate until get_sampwd_entries + * returns False due to num_entries being zero. This will cause an access denied + * return. I don't think this is right and needs further investigation. Note that + * this is also the same in the TNG code (I don't think that has been tested with + * a very large user list as MAX_SAM_ENTRIES is set to 600). + * + * I also think that one of the 'num_entries' return parameters is probably + * the "max entries" parameter - but in the TNG code they're all currently set to the same + * value (again I think this is wrong). + */ + + make_user_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_acct_name, num_entries, pass); + + init_samr_r_enum_dom_users(r_u, q_u->start_idx + num_entries, num_entries); + + DEBUG(5,("_samr_enum_dom_users: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* +makes a SAM_ENTRY / UNISTR2* structure from a group list. +********************************************************************/ + +static void make_group_sam_entry_list(TALLOC_CTX *ctx, SAM_ENTRY **sam_pp, UNISTR2 **uni_name_pp, + uint32 num_sam_entries, DOMAIN_GRP *grp) +{ + uint32 i; + SAM_ENTRY *sam; + UNISTR2 *uni_name; + + *sam_pp = NULL; + *uni_name_pp = NULL; + + if (num_sam_entries == 0) + return; + + sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries); + + uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries); + + if (sam == NULL || uni_name == NULL) { + DEBUG(0, ("NULL pointers in SAMR_R_QUERY_DISPINFO\n")); + return; + } + + for (i = 0; i < num_sam_entries; i++) { + /* + * JRA. I think this should include the null. TNG does not. + */ + int len = strlen(grp[i].name)+1; + + init_sam_entry(&sam[i], len, grp[i].rid); + init_unistr2(&uni_name[i], grp[i].name, len); + } + + *sam_pp = sam; + *uni_name_pp = uni_name; +} + +/******************************************************************* + Get the group entries - similar to get_sampwd_entries(). + ********************************************************************/ + +static NTSTATUS get_group_alias_entries(TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx, + uint32 *p_num_entries, uint32 max_entries) +{ + fstring sid_str; + uint32 num_entries = 0; + int i; + GROUP_MAP smap; + GROUP_MAP *map; + + sid_to_string(sid_str, sid); + DEBUG(5, ("get_group_alias_entries: enumerating aliases on SID: %s\n", sid_str)); + + *p_num_entries = 0; + + /* well-known aliases */ + if (sid_equal(sid, &global_sid_Builtin) && !lp_hide_local_users()) { + + enum_group_mapping(SID_NAME_ALIAS, &map, (int *)&num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV); + + if (num_entries != 0) { + *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP)); + if (*d_grp==NULL) + return NT_STATUS_NO_MEMORY; + + for(i=0; i<num_entries && i<max_entries; i++) { + fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name); + sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid); + + } + } + SAFE_FREE(map); + + } else if (sid_equal(sid, &global_sam_sid) && !lp_hide_local_users()) { + struct sys_grent *glist; + struct sys_grent *grp; + struct passwd *pw; + gid_t winbind_gid_low, winbind_gid_high; + + lp_winbind_gid(&winbind_gid_low, &winbind_gid_high); + + /* local aliases */ + /* we return the UNIX groups here. This seems to be the right */ + /* thing to do, since NT member servers return their local */ + /* groups in the same situation. */ + + /* use getgrent_list() to retrieve the list of groups to avoid + * problems with getgrent possible infinite loop by internal + * libc grent structures overwrites by called functions */ + grp = glist = getgrent_list(); + if (grp == NULL) + return NT_STATUS_NO_MEMORY; + + for (; (num_entries < max_entries) && (grp != NULL); grp = grp->next) { + uint32 trid; + + if(!get_group_from_gid(grp->gr_gid, &smap, MAPPING_WITHOUT_PRIV)) + continue; + + if (smap.sid_name_use!=SID_NAME_ALIAS) { + continue; + } + + sid_split_rid(&smap.sid, &trid); + + if (!sid_equal(sid, &smap.sid)) + continue; + + /* Don't return winbind groups as they are not local! */ + if ((grp->gr_gid >= winbind_gid_low)&&(grp->gr_gid <= winbind_gid_high)) { + DEBUG(10,("get_group_alias_entries: not returing %s, not local.\n", smap.nt_name )); + continue; + } + + /* Don't return user private groups... */ + + /* + * We used to do a Get_Pwnam() here, but this has been + * trimmed back to the common case for private groups + * to save lookups and to use the _alloc interface. + * + * This also matches the group mapping code + */ + + if ((pw = getpwnam_alloc(smap.nt_name)) != 0) { + DEBUG(10,("get_group_alias_entries: not returing %s, clashes with user.\n", smap.nt_name )); + passwd_free(&pw); + continue; + } + + for( i = 0; i < num_entries; i++) + if ( (*d_grp)[i].rid == trid ) + break; + + if ( i < num_entries ) { + continue; /* rid was there, dup! */ + } + + /* JRA - added this for large group db enumeration... */ + + if (start_idx > 0) { + /* skip the requested number of entries. + not very efficient, but hey... + */ + start_idx--; + continue; + } + + *d_grp=talloc_realloc(ctx,*d_grp, (num_entries+1)*sizeof(DOMAIN_GRP)); + if (*d_grp==NULL) { + grent_free(glist); + return NT_STATUS_NO_MEMORY; + } + + fstrcpy((*d_grp)[num_entries].name, smap.nt_name); + (*d_grp)[num_entries].rid = trid; + num_entries++; + DEBUG(10,("get_group_alias_entries: added entry %d, rid:%d\n", num_entries, trid)); + } + + grent_free(glist); + } + + *p_num_entries = num_entries; + + DEBUG(10,("get_group_alias_entries: returning %d entries\n", *p_num_entries)); + + if (num_entries >= max_entries) + return STATUS_MORE_ENTRIES; + return NT_STATUS_OK; +} + +/******************************************************************* + Get the group entries - similar to get_sampwd_entries(). + ********************************************************************/ + +static NTSTATUS get_group_domain_entries(TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx, + uint32 *p_num_entries, uint32 max_entries) +{ + GROUP_MAP *map=NULL; + int i; + uint32 group_entries = 0; + uint32 num_entries = 0; + + *p_num_entries = 0; + + enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV); + + num_entries=group_entries-start_idx; + + /* limit the number of entries */ + if (num_entries>max_entries) { + DEBUG(5,("Limiting to %d entries\n", max_entries)); + num_entries=max_entries; + } + + *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP)); + if (num_entries!=0 && *d_grp==NULL){ + SAFE_FREE(map); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i<num_entries; i++) { + fstrcpy((*d_grp)[i].name, map[i+start_idx].nt_name); + fstrcpy((*d_grp)[i].comment, map[i+start_idx].comment); + sid_split_rid(&map[i+start_idx].sid, &(*d_grp)[i].rid); + (*d_grp)[i].attr=SID_NAME_DOM_GRP; + } + + SAFE_FREE(map); + + *p_num_entries = num_entries; + + return NT_STATUS_OK; +} + +/******************************************************************* + samr_reply_enum_dom_groups + Only reply with one group - domain admins. This must be fixed for + a real PDC. JRA. + ********************************************************************/ + +NTSTATUS _samr_enum_dom_groups(pipes_struct *p, SAMR_Q_ENUM_DOM_GROUPS *q_u, SAMR_R_ENUM_DOM_GROUPS *r_u) +{ + DOMAIN_GRP *grp=NULL; + uint32 num_entries; + DOM_SID sid; + + r_u->status = NT_STATUS_OK; + + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + DEBUG(5,("samr_reply_enum_dom_groups: %d\n", __LINE__)); + + /* the domain group array is being allocated in the function below */ + get_group_domain_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, &num_entries, MAX_SAM_ENTRIES); + + make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp); + + init_samr_r_enum_dom_groups(r_u, q_u->start_idx, num_entries); + + DEBUG(5,("samr_enum_dom_groups: %d\n", __LINE__)); + + return r_u->status; +} + + +/******************************************************************* + samr_reply_enum_dom_aliases + ********************************************************************/ + +NTSTATUS _samr_enum_dom_aliases(pipes_struct *p, SAMR_Q_ENUM_DOM_ALIASES *q_u, SAMR_R_ENUM_DOM_ALIASES *r_u) +{ + DOMAIN_GRP *grp=NULL; + uint32 num_entries = 0; + fstring sid_str; + DOM_SID sid; + NTSTATUS status; + + r_u->status = NT_STATUS_OK; + + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_to_string(sid_str, &sid); + DEBUG(5,("samr_reply_enum_dom_aliases: sid %s\n", sid_str)); + + status = get_group_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, + &num_entries, MAX_SAM_ENTRIES); + if (NT_STATUS_IS_ERR(status)) return status; + + make_group_sam_entry_list(p->mem_ctx, &r_u->sam, &r_u->uni_grp_name, num_entries, grp); + + /*safe_free(grp);*/ + + init_samr_r_enum_dom_aliases(r_u, q_u->start_idx + num_entries, num_entries); + + DEBUG(5,("samr_enum_dom_aliases: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + samr_reply_query_dispinfo + ********************************************************************/ +NTSTATUS _samr_query_dispinfo(pipes_struct *p, SAMR_Q_QUERY_DISPINFO *q_u, SAMR_R_QUERY_DISPINFO *r_u) +{ + struct samr_info *info = NULL; + uint32 struct_size=0x20; /* W2K always reply that, client doesn't care */ + uint16 acb_mask; + + uint32 max_entries=q_u->max_entries; + uint32 enum_context=q_u->start_idx; + uint32 max_size=q_u->max_size; + + SAM_DISPINFO_CTR *ctr; + uint32 temp_size=0, total_data_size=0; + NTSTATUS disp_ret; + uint32 num_account = 0; + enum remote_arch_types ra_type = get_remote_arch(); + int max_sam_entries; + + max_sam_entries = (ra_type == RA_WIN95) ? MAX_SAM_ENTRIES_W95 : MAX_SAM_ENTRIES_W2K; + + DEBUG(5, ("samr_reply_query_dispinfo: %d\n", __LINE__)); + r_u->status = NT_STATUS_OK; + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->domain_pol, (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 + */ + + /* Get what we need from the password database */ + + if (q_u->switch_level==2) + acb_mask = ACB_WSTRUST; + else + acb_mask = ACB_NORMAL; + + /* Get what we need from the password database */ + switch (q_u->switch_level) { + case 0x1: + case 0x2: + case 0x4: + become_root(); + r_u->status=load_sampwd_entries(info, acb_mask); + unbecome_root(); + if (NT_STATUS_IS_ERR(r_u->status)) { + DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n")); + return r_u->status; + } + num_account = info->disp_info.num_user_account; + break; + case 0x3: + case 0x5: + r_u->status = load_group_domain_entries(info, &info->sid); + if (NT_STATUS_IS_ERR(r_u->status)) + return r_u->status; + num_account = info->disp_info.num_group_account; + break; + default: + DEBUG(0,("_samr_query_dispinfo: Unknown info level (%u)\n", (unsigned int)q_u->switch_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_reply_query_dispinfo: client requested %d entries, limiting to %d\n", max_entries, max_sam_entries)); + max_entries = max_sam_entries; + } + + if (enum_context > num_account) { + DEBUG(5, ("samr_reply_query_dispinfo: enumeration handle over total entries\n")); + return NT_STATUS_OK; + } + + /* verify we won't overflow */ + if (max_entries > num_account-enum_context) { + max_entries = num_account-enum_context; + DEBUG(5, ("samr_reply_query_dispinfo: only %d entries to return\n", max_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_reply_query_dispinfo: buffer size limits to only %d entries\n", max_entries)); + } + + if (!(ctr = (SAM_DISPINFO_CTR *)talloc_zero(p->mem_ctx,sizeof(SAM_DISPINFO_CTR)))) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(ctr); + + /* Now create reply structure */ + switch (q_u->switch_level) { + case 0x1: + if (max_entries) { + if (!(ctr->sam.info1 = (SAM_DISPINFO_1 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_1)))) + return NT_STATUS_NO_MEMORY; + } + disp_ret = init_sam_dispinfo_1(p->mem_ctx, ctr->sam.info1, max_entries, enum_context, info->disp_info.disp_user_info); + if (NT_STATUS_IS_ERR(disp_ret)) + return disp_ret; + break; + case 0x2: + if (max_entries) { + if (!(ctr->sam.info2 = (SAM_DISPINFO_2 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_2)))) + return NT_STATUS_NO_MEMORY; + } + disp_ret = init_sam_dispinfo_2(p->mem_ctx, ctr->sam.info2, max_entries, enum_context, info->disp_info.disp_user_info); + if (NT_STATUS_IS_ERR(disp_ret)) + return disp_ret; + break; + case 0x3: + if (max_entries) { + if (!(ctr->sam.info3 = (SAM_DISPINFO_3 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_3)))) + return NT_STATUS_NO_MEMORY; + } + disp_ret = init_sam_dispinfo_3(p->mem_ctx, ctr->sam.info3, max_entries, enum_context, info->disp_info.disp_group_info); + if (NT_STATUS_IS_ERR(disp_ret)) + return disp_ret; + break; + case 0x4: + if (max_entries) { + if (!(ctr->sam.info4 = (SAM_DISPINFO_4 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_4)))) + return NT_STATUS_NO_MEMORY; + } + disp_ret = init_sam_dispinfo_4(p->mem_ctx, ctr->sam.info4, max_entries, enum_context, info->disp_info.disp_user_info); + if (NT_STATUS_IS_ERR(disp_ret)) + return disp_ret; + break; + case 0x5: + if (max_entries) { + if (!(ctr->sam.info5 = (SAM_DISPINFO_5 *)talloc_zero(p->mem_ctx,max_entries*sizeof(SAM_DISPINFO_5)))) + return NT_STATUS_NO_MEMORY; + } + disp_ret = init_sam_dispinfo_5(p->mem_ctx, ctr->sam.info5, max_entries, enum_context, info->disp_info.disp_group_info); + if (NT_STATUS_IS_ERR(disp_ret)) + return disp_ret; + break; + + default: + ctr->sam.info = NULL; + return NT_STATUS_INVALID_INFO_CLASS; + } + + /* calculate the total size */ + total_data_size=num_account*struct_size; + + if (enum_context+max_entries < num_account) + r_u->status = STATUS_MORE_ENTRIES; + + DEBUG(5, ("_samr_query_dispinfo: %d\n", __LINE__)); + + init_samr_r_query_dispinfo(r_u, max_entries, total_data_size, temp_size, q_u->switch_level, ctr, r_u->status); + + return r_u->status; + +} + +/******************************************************************* + samr_reply_query_aliasinfo + ********************************************************************/ + +NTSTATUS _samr_query_aliasinfo(pipes_struct *p, SAMR_Q_QUERY_ALIASINFO *q_u, SAMR_R_QUERY_ALIASINFO *r_u) +{ + struct samr_info *info = NULL; + GROUP_MAP map; + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_check_is_in_our_domain(&info->sid) && + !sid_check_is_in_builtin(&info->sid)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + if(!get_local_group_from_sid(info->sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + switch (q_u->switch_level) { + case 1: + r_u->ptr = 1; + r_u->ctr.switch_value1 = 1; + init_samr_alias_info1(&r_u->ctr.alias.info1, map.nt_name, 1, map.comment); + break; + case 3: + r_u->ptr = 1; + r_u->ctr.switch_value1 = 3; + init_samr_alias_info3(&r_u->ctr.alias.info3, map.comment); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + DEBUG(5,("_samr_query_aliasinfo: %d\n", __LINE__)); + + return r_u->status; +} + +#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_lookup_names + ********************************************************************/ + +NTSTATUS _samr_lookup_names(pipes_struct *p, SAMR_Q_LOOKUP_NAMES *q_u, SAMR_R_LOOKUP_NAMES *r_u) +{ + uint32 rid[MAX_SAM_ENTRIES]; + uint32 local_rid; + enum SID_NAME_USE type[MAX_SAM_ENTRIES]; + enum SID_NAME_USE local_type; + int i; + int num_rids = q_u->num_names2; + DOM_SID pol_sid; + fstring sid_str; + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_lookup_names: %d\n", __LINE__)); + + ZERO_ARRAY(rid); + ZERO_ARRAY(type); + + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid)) { + init_samr_r_lookup_names(p->mem_ctx, r_u, 0, NULL, NULL, NT_STATUS_OBJECT_TYPE_MISMATCH); + return r_u->status; + } + + if (num_rids > MAX_SAM_ENTRIES) { + num_rids = MAX_SAM_ENTRIES; + DEBUG(5,("_samr_lookup_names: truncating entries to %d\n", num_rids)); + } + + DEBUG(5,("_samr_lookup_names: looking name on SID %s\n", sid_to_string(sid_str, &pol_sid))); + + become_root(); /* local_lookup_name can require root privs */ + + for (i = 0; i < num_rids; i++) { + fstring name; + DOM_SID sid; + + r_u->status = NT_STATUS_NONE_MAPPED; + + rid [i] = 0xffffffff; + type[i] = SID_NAME_UNKNOWN; + + rpcstr_pull(name, q_u->uni_name[i].buffer, sizeof(name), q_u->uni_name[i].uni_str_len*2, 0); + + /* + * we are only looking for a name + * the SID we get back can be outside + * the scope of the pol_sid + * + * in clear: it prevents to reply to domain\group: yes + * when only builtin\group exists. + * + * a cleaner code is to add the sid of the domain we're looking in + * to the local_lookup_name function. + */ + if(local_lookup_name(name, &sid, &local_type)) { + sid_split_rid(&sid, &local_rid); + + if (sid_equal(&sid, &pol_sid)) { + rid[i]=local_rid; + type[i]=local_type; + r_u->status = NT_STATUS_OK; + } + } + } + + unbecome_root(); + + init_samr_r_lookup_names(p->mem_ctx, r_u, num_rids, rid, (uint32 *)type, r_u->status); + + DEBUG(5,("_samr_lookup_names: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + _samr_chgpasswd_user + ********************************************************************/ + +NTSTATUS _samr_chgpasswd_user(pipes_struct *p, SAMR_Q_CHGPASSWD_USER *q_u, SAMR_R_CHGPASSWD_USER *r_u) +{ + fstring user_name; + fstring wks; + + DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__)); + + r_u->status = NT_STATUS_OK; + + rpcstr_pull(user_name, q_u->uni_user_name.buffer, sizeof(user_name), q_u->uni_user_name.uni_str_len*2, 0); + rpcstr_pull(wks, q_u->uni_dest_host.buffer, sizeof(wks), q_u->uni_dest_host.uni_str_len*2,0); + + DEBUG(5,("samr_chgpasswd_user: 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. + */ + + if (!pass_oem_change(user_name, q_u->lm_newpass.pass, q_u->lm_oldhash.hash, + q_u->nt_newpass.pass, q_u->nt_oldhash.hash)) + r_u->status = NT_STATUS_WRONG_PASSWORD; + + init_samr_r_chgpasswd_user(r_u, r_u->status); + + DEBUG(5,("_samr_chgpasswd_user: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* +makes a SAMR_R_LOOKUP_RIDS structure. +********************************************************************/ + +static BOOL make_samr_lookup_rids(TALLOC_CTX *ctx, uint32 num_names, fstring names[], + UNIHDR **pp_hdr_name, UNISTR2 **pp_uni_name) +{ + uint32 i; + UNIHDR *hdr_name=NULL; + UNISTR2 *uni_name=NULL; + + *pp_uni_name = NULL; + *pp_hdr_name = NULL; + + if (num_names != 0) { + hdr_name = (UNIHDR *)talloc_zero(ctx, sizeof(UNIHDR)*num_names); + if (hdr_name == NULL) + return False; + + uni_name = (UNISTR2 *)talloc_zero(ctx,sizeof(UNISTR2)*num_names); + if (uni_name == NULL) + return False; + } + + for (i = 0; i < num_names; i++) { + int len = names[i] != NULL ? strlen(names[i]) : 0; + DEBUG(10, ("names[%d]:%s\n", i, names[i])); + init_uni_hdr(&hdr_name[i], len); + init_unistr2(&uni_name[i], names[i], len); + } + + *pp_uni_name = uni_name; + *pp_hdr_name = hdr_name; + + return True; +} + +/******************************************************************* + _samr_lookup_rids + ********************************************************************/ + +NTSTATUS _samr_lookup_rids(pipes_struct *p, SAMR_Q_LOOKUP_RIDS *q_u, SAMR_R_LOOKUP_RIDS *r_u) +{ + fstring group_names[MAX_SAM_ENTRIES]; + uint32 *group_attrs = NULL; + UNIHDR *hdr_name = NULL; + UNISTR2 *uni_name = NULL; + DOM_SID pol_sid; + int num_rids = q_u->num_rids1; + int i; + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &pol_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (num_rids > MAX_SAM_ENTRIES) { + num_rids = MAX_SAM_ENTRIES; + DEBUG(5,("_samr_lookup_rids: truncating entries to %d\n", num_rids)); + } + + if (num_rids) { + if ((group_attrs = (uint32 *)talloc_zero(p->mem_ctx, num_rids * sizeof(uint32))) == NULL) + return NT_STATUS_NO_MEMORY; + } + + r_u->status = NT_STATUS_NONE_MAPPED; + + become_root(); /* lookup_sid can require root privs */ + + for (i = 0; i < num_rids; i++) { + fstring tmpname; + fstring domname; + DOM_SID sid; + enum SID_NAME_USE type; + + group_attrs[i] = SID_NAME_UNKNOWN; + *group_names[i] = '\0'; + + if (sid_equal(&pol_sid, &global_sam_sid)) { + sid_copy(&sid, &pol_sid); + sid_append_rid(&sid, q_u->rid[i]); + + if (lookup_sid(&sid, domname, tmpname, &type)) { + r_u->status = NT_STATUS_OK; + group_attrs[i] = (uint32)type; + fstrcpy(group_names[i],tmpname); + DEBUG(5,("_samr_lookup_rids: %s:%d\n", group_names[i], group_attrs[i])); + } + } + } + + unbecome_root(); + + if(!make_samr_lookup_rids(p->mem_ctx, num_rids, group_names, &hdr_name, &uni_name)) + return NT_STATUS_NO_MEMORY; + + init_samr_r_lookup_rids(r_u, num_rids, hdr_name, uni_name, group_attrs); + + DEBUG(5,("_samr_lookup_rids: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + _api_samr_open_user. Safe - gives out no passwd info. + ********************************************************************/ + +NTSTATUS _api_samr_open_user(pipes_struct *p, SAMR_Q_OPEN_USER *q_u, SAMR_R_OPEN_USER *r_u) +{ + SAM_ACCOUNT *sampass=NULL; + DOM_SID sid; + POLICY_HND domain_pol = q_u->domain_pol; + uint32 user_rid = q_u->user_rid; + POLICY_HND *user_pol = &r_u->user_pol; + struct samr_info *info = NULL; + BOOL ret; + + r_u->status = NT_STATUS_OK; + + /* find the domain policy handle. */ + if (!find_policy_by_hnd(p, &domain_pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + pdb_init_sam(&sampass); + + become_root(); + ret=pdb_getsampwrid(sampass, user_rid); + unbecome_root(); + + /* check that the RID exists in our domain. */ + if (ret == False) { + pdb_free_sam(&sampass); + return NT_STATUS_NO_SUCH_USER; + } + + pdb_free_sam(&sampass); + + /* Get the domain SID stored in the domain policy */ + if(!get_lsa_policy_samr_sid(p, &domain_pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + /* append the user's RID to it */ + if(!sid_append_rid(&sid, user_rid)) + return NT_STATUS_NO_SUCH_USER; + + /* associate the user's SID with the new handle. */ + if ((info = get_samr_info_by_sid(&sid)) == NULL) + return NT_STATUS_NO_MEMORY; + + /* 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 r_u->status; +} + +/************************************************************************* + get_user_info_10. Safe. Only gives out acb bits. + *************************************************************************/ + +static BOOL get_user_info_10(SAM_USER_INFO_10 *id10, uint32 user_rid) +{ + SAM_ACCOUNT *smbpass=NULL; + BOOL ret; + + if (!pdb_rid_is_user(user_rid)) { + DEBUG(4,("RID 0x%x is not a user RID\n", user_rid)); + return False; + } + + pdb_init_sam(&smbpass); + + become_root(); + ret = pdb_getsampwrid(smbpass, user_rid); + unbecome_root(); + + if (ret==False) { + DEBUG(4,("User 0x%x not found\n", user_rid)); + pdb_free_sam(&smbpass); + return False; + } + + DEBUG(3,("User:[%s]\n", pdb_get_username(smbpass) )); + + ZERO_STRUCTP(id10); + init_sam_user_info10(id10, pdb_get_acct_ctrl(smbpass) ); + + pdb_free_sam(&smbpass); + + return True; +} + +/************************************************************************* + get_user_info_12. 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_12(pipes_struct *p, SAM_USER_INFO_12 * id12, uint32 user_rid) +{ + SAM_ACCOUNT *smbpass=NULL; + BOOL ret; + + if (!p->ntlmssp_auth_validated) + return NT_STATUS_ACCESS_DENIED; + + if (!(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SIGN) || !(p->ntlmssp_chal_flags & NTLMSSP_NEGOTIATE_SEAL)) + return NT_STATUS_ACCESS_DENIED; + + /* + * Do *NOT* do become_root()/unbecome_root() here ! JRA. + */ + pdb_init_sam(&smbpass); + + ret = pdb_getsampwrid(smbpass, user_rid); + + if (ret == False) { + DEBUG(4, ("User 0x%x not found\n", user_rid)); + pdb_free_sam(&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) { + pdb_free_sam(&smbpass); + return NT_STATUS_ACCOUNT_DISABLED; + } + + ZERO_STRUCTP(id12); + init_sam_user_info12(id12, pdb_get_lanman_passwd(smbpass), pdb_get_nt_passwd(smbpass)); + + pdb_free_sam(&smbpass); + + return NT_STATUS_OK; +} + +/************************************************************************* + get_user_info_20 + *************************************************************************/ + +static BOOL get_user_info_20(SAM_USER_INFO_20 *id20, uint32 user_rid) +{ + SAM_ACCOUNT *sampass=NULL; + BOOL ret; + + if (!pdb_rid_is_user(user_rid)) { + DEBUG(4,("RID 0x%x is not a user RID\n", user_rid)); + return False; + } + + pdb_init_sam(&sampass); + + become_root(); + ret = pdb_getsampwrid(sampass, user_rid); + unbecome_root(); + + if (ret == False) { + DEBUG(4,("User 0x%x not found\n", user_rid)); + pdb_free_sam(&sampass); + return False; + } + + samr_clear_sam_passwd(sampass); + + DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) )); + + ZERO_STRUCTP(id20); + init_sam_user_info20A(id20, sampass); + + pdb_free_sam(&sampass); + + return True; +} + +/************************************************************************* + get_user_info_21 + *************************************************************************/ + +static BOOL get_user_info_21(SAM_USER_INFO_21 *id21, uint32 user_rid) +{ + SAM_ACCOUNT *sampass=NULL; + BOOL ret; + + if (!pdb_rid_is_user(user_rid)) { + DEBUG(4,("RID 0x%x is not a user RID\n", user_rid)); + return False; + } + + pdb_init_sam(&sampass); + + become_root(); + ret = pdb_getsampwrid(sampass, user_rid); + unbecome_root(); + + if (ret == False) { + DEBUG(4,("User 0x%x not found\n", user_rid)); + pdb_free_sam(&sampass); + return False; + } + + samr_clear_sam_passwd(sampass); + + DEBUG(3,("User:[%s]\n", pdb_get_username(sampass) )); + + ZERO_STRUCTP(id21); + init_sam_user_info21A(id21, sampass); + + pdb_free_sam(&sampass); + + return True; +} + +/******************************************************************* + _samr_query_userinfo + ********************************************************************/ + +NTSTATUS _samr_query_userinfo(pipes_struct *p, SAMR_Q_QUERY_USERINFO *q_u, SAMR_R_QUERY_USERINFO *r_u) +{ + SAM_USERINFO_CTR *ctr; + uint32 rid = 0; + struct samr_info *info = NULL; + + r_u->status=NT_STATUS_OK; + + /* search for the handle */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_check_is_in_our_domain(&info->sid)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + sid_peek_rid(&info->sid, &rid); + + DEBUG(5,("_samr_query_userinfo: rid:0x%x\n", rid)); + + ctr = (SAM_USERINFO_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_USERINFO_CTR)); + if (!ctr) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(ctr); + + /* ok! user info levels (lots: see MSDEV help), off we go... */ + ctr->switch_value = q_u->switch_value; + + switch (q_u->switch_value) { + case 0x10: + ctr->info.id10 = (SAM_USER_INFO_10 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_10)); + if (ctr->info.id10 == NULL) + return NT_STATUS_NO_MEMORY; + + if (!get_user_info_10(ctr->info.id10, rid)) + return NT_STATUS_NO_SUCH_USER; + break; + +#if 0 +/* whoops - got this wrong. i think. or don't understand what's happening. */ + case 0x11: + { + NTTIME expire; + info = (void *)&id11; + + expire.low = 0xffffffff; + expire.high = 0x7fffffff; + + ctr->info.id = (SAM_USER_INFO_11 *)talloc_zero(p->mem_ctx, + sizeof + (*ctr-> + info. + id11)); + ZERO_STRUCTP(ctr->info.id11); + init_sam_user_info11(ctr->info.id11, &expire, + "BROOKFIELDS$", /* name */ + 0x03ef, /* user rid */ + 0x201, /* group rid */ + 0x0080); /* acb info */ + + break; + } +#endif + + case 0x12: + ctr->info.id12 = (SAM_USER_INFO_12 *)talloc_zero(p->mem_ctx, sizeof(SAM_USER_INFO_12)); + if (ctr->info.id12 == NULL) + return NT_STATUS_NO_MEMORY; + + if (NT_STATUS_IS_ERR(r_u->status = get_user_info_12(p, ctr->info.id12, rid))) + return r_u->status; + break; + + case 20: + ctr->info.id20 = (SAM_USER_INFO_20 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_20)); + if (ctr->info.id20 == NULL) + return NT_STATUS_NO_MEMORY; + if (!get_user_info_20(ctr->info.id20, rid)) + return NT_STATUS_NO_SUCH_USER; + break; + + case 21: + ctr->info.id21 = (SAM_USER_INFO_21 *)talloc_zero(p->mem_ctx,sizeof(SAM_USER_INFO_21)); + if (ctr->info.id21 == NULL) + return NT_STATUS_NO_MEMORY; + if (!get_user_info_21(ctr->info.id21, rid)) + return NT_STATUS_NO_SUCH_USER; + break; + + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + init_samr_r_query_userinfo(r_u, ctr, r_u->status); + + DEBUG(5,("_samr_query_userinfo: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + samr_reply_query_usergroups + ********************************************************************/ + +NTSTATUS _samr_query_usergroups(pipes_struct *p, SAMR_Q_QUERY_USERGROUPS *q_u, SAMR_R_QUERY_USERGROUPS *r_u) +{ + SAM_ACCOUNT *sam_pass=NULL; + DOM_GID *gids = NULL; + int num_groups = 0; + uint32 rid; + struct samr_info *info = NULL; + BOOL ret; + + /* + * 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 + */ + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_check_is_in_our_domain(&info->sid)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + sid_peek_rid(&info->sid, &rid); + + pdb_init_sam(&sam_pass); + + become_root(); + ret = pdb_getsampwrid(sam_pass, rid); + unbecome_root(); + + if (ret == False) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + if(!get_domain_user_groups(p->mem_ctx, &num_groups, &gids, sam_pass)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_GROUP; + } + + /* construct the response. lkclXXXX: gids are not copied! */ + init_samr_r_query_usergroups(r_u, num_groups, gids, r_u->status); + + DEBUG(5,("_samr_query_usergroups: %d\n", __LINE__)); + + pdb_free_sam(&sam_pass); + + return r_u->status; +} + +/******************************************************************* + _samr_query_dom_info + ********************************************************************/ + +NTSTATUS _samr_query_dom_info(pipes_struct *p, SAMR_Q_QUERY_DOMAIN_INFO *q_u, SAMR_R_QUERY_DOMAIN_INFO *r_u) +{ + struct samr_info *info = NULL; + SAM_UNK_CTR *ctr; + uint32 min_pass_len,pass_hist,flag; + 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 num_users=0, num_groups=0, num_aliases=0; + + if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(ctr); + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + switch (q_u->switch_value) { + case 0x01: + account_policy_get(AP_MIN_PASSWORD_LEN, &min_pass_len); + account_policy_get(AP_PASSWORD_HISTORY, &pass_hist); + account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &flag); + account_policy_get(AP_MAX_PASSWORD_AGE, (int *)&u_expire); + account_policy_get(AP_MIN_PASSWORD_AGE, (int *)&u_min_age); + + unix_to_nt_time_abs(&nt_expire, u_expire); + unix_to_nt_time_abs(&nt_min_age, u_min_age); + + init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist, + flag, nt_expire, nt_min_age); + break; + case 0x02: + become_root(); + r_u->status=load_sampwd_entries(info, ACB_NORMAL); + unbecome_root(); + if (NT_STATUS_IS_ERR(r_u->status)) { + DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n")); + return r_u->status; + } + num_users=info->disp_info.num_user_account; + free_samr_db(info); + + r_u->status=load_group_domain_entries(info, &global_sam_sid); + if (NT_STATUS_IS_ERR(r_u->status)) { + DEBUG(5, ("_samr_query_dispinfo: load_group_domain_entries failed\n")); + return r_u->status; + } + num_groups=info->disp_info.num_group_account; + free_samr_db(info); + + /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */ + init_unk_info2(&ctr->info.inf2, global_myworkgroup, global_myname, (uint32) time(NULL), + num_users, num_groups, num_aliases); + break; + case 0x03: + account_policy_get(AP_TIME_TO_LOGOUT, (int *)&u_logout); + unix_to_nt_time_abs(&nt_logout, u_logout); + + init_unk_info3(&ctr->info.inf3, nt_logout); + break; + case 0x05: + init_unk_info5(&ctr->info.inf5, global_myname); + break; + case 0x06: + init_unk_info6(&ctr->info.inf6); + break; + case 0x07: + init_unk_info7(&ctr->info.inf7); + break; + case 0x0c: + account_policy_get(AP_LOCK_ACCOUNT_DURATION, (int *)&u_lock_duration); + account_policy_get(AP_RESET_COUNT_TIME, (int *)&u_reset_time); + account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &lockout); + + unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration); + unix_to_nt_time_abs(&nt_reset_time, u_reset_time); + + init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + init_samr_r_query_dom_info(r_u, q_u->switch_value, ctr, NT_STATUS_OK); + + DEBUG(5,("_samr_query_dom_info: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + _api_samr_create_user + Create an account, can be either a normal user or a machine. + This funcion will need to be updated for bdc/domain trusts. + ********************************************************************/ + +NTSTATUS _api_samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREATE_USER *r_u) +{ + SAM_ACCOUNT *sam_pass=NULL; + fstring account; + DOM_SID sid; + pstring add_script; + POLICY_HND dom_pol = q_u->domain_pol; + UNISTR2 user_account = q_u->uni_name; + uint16 acb_info = q_u->acb_info; + POLICY_HND *user_pol = &r_u->user_pol; + struct samr_info *info = NULL; + BOOL ret; + NTSTATUS nt_status; + struct passwd *pw; + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &dom_pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + /* find the account: tell the caller if it exists. + lkclXXXX i have *no* idea if this is a problem or not + or even if you are supposed to construct a different + reply if the account already exists... + */ + + rpcstr_pull(account, user_account.buffer, sizeof(account), user_account.uni_str_len*2, 0); + strlower(account); + + pdb_init_sam(&sam_pass); + + become_root(); + ret = pdb_getsampwnam(sam_pass, account); + unbecome_root(); + if (ret == True) { + /* this account exists: say so */ + pdb_free_sam(&sam_pass); + return NT_STATUS_USER_EXISTS; + } + + pdb_free_sam(&sam_pass); + + /* + * NB. VERY IMPORTANT ! This call must be done as the current pipe user, + * *NOT* surrounded by a become_root()/unbecome_root() call. This ensures + * that only people with write access to the smbpasswd file will be able + * to create a user. JRA. + */ + + /* + * add the user in the /etc/passwd file or the unix authority system. + * We don't check if the smb_create_user() function succed or not for 2 reasons: + * a) local_password_change() checks for us if the /etc/passwd account really exists + * b) smb_create_user() would return an error if the account already exists + * and as it could return an error also if it can't create the account, it would be tricky. + * + * So we go the easy way, only check after if the account exists. + * JFM (2/3/2001), to clear any possible bad understanding (-: + * + * We now have seperate script paramaters for adding users/machines so we + * now have some sainity-checking to match. + */ + + DEBUG(10,("checking account %s at pos %d for $ termination\n",account, strlen(account)-1)); +#if 0 + if ((acb_info & ACB_WSTRUST) && (account[strlen(account)-1] == '$')) { + pstrcpy(add_script, lp_addmachine_script()); + } else if ((!(acb_info & ACB_WSTRUST)) && (account[strlen(account)-1] != '$')) { + pstrcpy(add_script, lp_adduser_script()); + } else { + DEBUG(0, ("_api_samr_create_user: mismatch between trust flags and $ termination\n")); + pdb_free_sam(&sam_pass); + return NT_STATUS_UNSUCCESSFUL; + } +#endif + + /* + * we can't check both the ending $ and the acb_info. + * + * UserManager creates trust accounts (ending in $, + * normal that hidden accounts) with the acb_info equals to ACB_NORMAL. + * JFM, 11/29/2001 + */ + if (account[strlen(account)-1] == '$') + pstrcpy(add_script, lp_addmachine_script()); + else + pstrcpy(add_script, lp_adduser_script()); + + if(*add_script) { + int add_ret; + all_string_sub(add_script, "%u", account, sizeof(account)); + add_ret = smbrun(add_script,NULL); + DEBUG(3,("_api_samr_create_user: Running the command `%s' gave %d\n", add_script, add_ret)); + } + + pw = getpwnam_alloc(account); + + if (pw) { + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sam_pass, pw))) { + passwd_free(&pw); + return nt_status; + } + passwd_free(&pw); /* done with this now */ + } else { + DEBUG(3,("attempting to create non-unix account %s\n", account)); + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sam_pass))) { + return nt_status; + } + + if (!pdb_set_username(sam_pass, account)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + } + + pdb_set_acct_ctrl(sam_pass, acb_info); + + if (!pdb_add_sam_account(sam_pass)) { + pdb_free_sam(&sam_pass); + DEBUG(0, ("could not add user/computer %s to passdb. Check permissions?\n", + account)); + return NT_STATUS_ACCESS_DENIED; + } + + pdb_reset_sam(sam_pass); + + if (!pdb_getsampwnam(sam_pass, account)) { + pdb_free_sam(&sam_pass); + DEBUG(0, ("could not find user/computer %s just added to passdb?!?\n", + account)); + return NT_STATUS_ACCESS_DENIED; + } + + /* Get the domain SID stored in the domain policy */ + if(!get_lsa_policy_samr_sid(p, &dom_pol, &sid)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_INVALID_HANDLE; + } + + /* append the user's RID to it */ + if(!sid_append_rid(&sid, pdb_get_user_rid(sam_pass) )) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + /* associate the user's SID with the new handle. */ + if ((info = get_samr_info_by_sid(&sid)) == NULL) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCTP(info); + info->sid = sid; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + r_u->user_rid=pdb_get_user_rid(sam_pass); + r_u->unknown_0 = 0x000703ff; + + pdb_free_sam(&sam_pass); + + return NT_STATUS_OK; +} + +/******************************************************************* + samr_reply_connect_anon + ********************************************************************/ + +NTSTATUS _samr_connect_anon(pipes_struct *p, SAMR_Q_CONNECT_ANON *q_u, SAMR_R_CONNECT_ANON *r_u) +{ + struct samr_info *info = NULL; + + /* set up the SAMR connect_anon response */ + + r_u->status = NT_STATUS_OK; + + /* associate the user's SID with the new handle. */ + if ((info = get_samr_info_by_sid(NULL)) == NULL) + return NT_STATUS_NO_MEMORY; + + info->status = q_u->unknown_0; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return r_u->status; +} + +/******************************************************************* + samr_reply_connect + ********************************************************************/ + +NTSTATUS _samr_connect(pipes_struct *p, SAMR_Q_CONNECT *q_u, SAMR_R_CONNECT *r_u) +{ + struct samr_info *info = NULL; + + DEBUG(5,("_samr_connect: %d\n", __LINE__)); + + r_u->status = NT_STATUS_OK; + + /* associate the user's SID with the new handle. */ + if ((info = get_samr_info_by_sid(NULL)) == NULL) + return NT_STATUS_NO_MEMORY; + + info->status = q_u->access_mask; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->connect_pol, free_samr_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + DEBUG(5,("_samr_connect: %d\n", __LINE__)); + + return r_u->status; +} + +/********************************************************************** + api_samr_lookup_domain + **********************************************************************/ + +NTSTATUS _samr_lookup_domain(pipes_struct *p, SAMR_Q_LOOKUP_DOMAIN *q_u, SAMR_R_LOOKUP_DOMAIN *r_u) +{ + fstring domain_name; + DOM_SID sid; + + r_u->status = NT_STATUS_OK; + + if (!find_policy_by_hnd(p, &q_u->connect_pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + rpcstr_pull(domain_name, q_u->uni_domain.buffer, sizeof(domain_name), q_u->uni_domain.uni_str_len*2, 0); + + ZERO_STRUCT(sid); + + if (!secrets_fetch_domain_sid(domain_name, &sid)) { + r_u->status = NT_STATUS_NO_SUCH_DOMAIN; + } + + DEBUG(2,("Returning domain sid for domain %s -> %s\n", domain_name, sid_string_static(&sid))); + + init_samr_r_lookup_domain(r_u, &sid, r_u->status); + + return r_u->status; +} + +/****************************************************************** +makes a SAMR_R_ENUM_DOMAINS structure. +********************************************************************/ + +static BOOL make_enum_domains(TALLOC_CTX *ctx, SAM_ENTRY **pp_sam, + UNISTR2 **pp_uni_name, uint32 num_sam_entries, fstring doms[]) +{ + uint32 i; + SAM_ENTRY *sam; + UNISTR2 *uni_name; + + DEBUG(5, ("make_enum_domains\n")); + + *pp_sam = NULL; + *pp_uni_name = NULL; + + if (num_sam_entries == 0) + return True; + + sam = (SAM_ENTRY *)talloc_zero(ctx, sizeof(SAM_ENTRY)*num_sam_entries); + uni_name = (UNISTR2 *)talloc_zero(ctx, sizeof(UNISTR2)*num_sam_entries); + + if (sam == NULL || uni_name == NULL) + return False; + + for (i = 0; i < num_sam_entries; i++) { + int len = doms[i] != NULL ? strlen(doms[i]) : 0; + + init_sam_entry(&sam[i], len, 0); + init_unistr2(&uni_name[i], doms[i], len); + } + + *pp_sam = sam; + *pp_uni_name = uni_name; + + return True; +} + +/********************************************************************** + api_samr_enum_domains + **********************************************************************/ + +NTSTATUS _samr_enum_domains(pipes_struct *p, SAMR_Q_ENUM_DOMAINS *q_u, SAMR_R_ENUM_DOMAINS *r_u) +{ + uint32 num_entries = 2; + fstring dom[2]; + char *name; + + r_u->status = NT_STATUS_OK; + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + name = global_myworkgroup; + break; + default: + name = global_myname; + } + + fstrcpy(dom[0],name); + strupper(dom[0]); + fstrcpy(dom[1],"Builtin"); + + if (!make_enum_domains(p->mem_ctx, &r_u->sam, &r_u->uni_dom_name, num_entries, dom)) + return NT_STATUS_NO_MEMORY; + + init_samr_r_enum_domains(r_u, q_u->start_idx + num_entries, num_entries); + + return r_u->status; +} + +/******************************************************************* + api_samr_open_alias + ********************************************************************/ + +NTSTATUS _api_samr_open_alias(pipes_struct *p, SAMR_Q_OPEN_ALIAS *q_u, SAMR_R_OPEN_ALIAS *r_u) +{ + DOM_SID sid; + POLICY_HND domain_pol = q_u->dom_pol; + uint32 alias_rid = q_u->rid_alias; + POLICY_HND *alias_pol = &r_u->pol; + struct samr_info *info = NULL; + + r_u->status = NT_STATUS_OK; + + /* get the domain policy. */ + if (!find_policy_by_hnd(p, &domain_pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + /* Get the domain SID stored in the domain policy */ + if(!get_lsa_policy_samr_sid(p, &domain_pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + /* append the alias' RID to it */ + if(!sid_append_rid(&sid, alias_rid)) + return NT_STATUS_NO_SUCH_USER; + + /* + * we should check if the rid really exist !!! + * JFM. + */ + + /* associate the user's SID with the new handle. */ + if ((info = get_samr_info_by_sid(&sid)) == NULL) + return NT_STATUS_NO_MEMORY; + + /* 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 r_u->status; +} + +/******************************************************************* + set_user_info_10 + ********************************************************************/ + +static BOOL set_user_info_10(const SAM_USER_INFO_10 *id10, uint32 rid) +{ + SAM_ACCOUNT *pwd =NULL; + BOOL ret; + + pdb_init_sam(&pwd); + + ret = pdb_getsampwrid(pwd, rid); + + if(ret==False) { + pdb_free_sam(&pwd); + return False; + } + + if (id10 == NULL) { + DEBUG(5, ("set_user_info_10: NULL id10\n")); + pdb_free_sam(&pwd); + return False; + } + + if (!pdb_set_acct_ctrl(pwd, id10->acb_info)) { + pdb_free_sam(&pwd); + return False; + } + + if(!pdb_update_sam_account(pwd)) { + pdb_free_sam(&pwd); + return False; + } + + pdb_free_sam(&pwd); + + return True; +} + +/******************************************************************* + set_user_info_12 + ********************************************************************/ + +static BOOL set_user_info_12(SAM_USER_INFO_12 *id12, uint32 rid) +{ + SAM_ACCOUNT *pwd = NULL; + + pdb_init_sam(&pwd); + + if(!pdb_getsampwrid(pwd, rid)) { + pdb_free_sam(&pwd); + return False; + } + + if (id12 == NULL) { + DEBUG(2, ("set_user_info_12: id12 is NULL\n")); + pdb_free_sam(&pwd); + return False; + } + + if (!pdb_set_lanman_passwd (pwd, id12->lm_pwd)) { + pdb_free_sam(&pwd); + return False; + } + if (!pdb_set_nt_passwd (pwd, id12->nt_pwd)) { + pdb_free_sam(&pwd); + return False; + } + if (!pdb_set_pass_changed_now (pwd)) { + pdb_free_sam(&pwd); + return False; + } + + if(!pdb_update_sam_account(pwd)) { + pdb_free_sam(&pwd); + return False; + } + + pdb_free_sam(&pwd); + return True; +} + +/******************************************************************* + set_user_info_21 + ********************************************************************/ + +static BOOL set_user_info_21(SAM_USER_INFO_21 *id21, uint32 rid) +{ + SAM_ACCOUNT *pwd = NULL; + + if (id21 == NULL) { + DEBUG(5, ("set_user_info_21: NULL id21\n")); + return False; + } + + pdb_init_sam(&pwd); + + if (!pdb_getsampwrid(pwd, rid)) { + pdb_free_sam(&pwd); + return False; + } + + copy_id21_to_sam_passwd(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 + */ + + /* write the change out */ + if(!pdb_update_sam_account(pwd)) { + pdb_free_sam(&pwd); + return False; + } + + pdb_free_sam(&pwd); + + return True; +} + +/******************************************************************* + set_user_info_23 + ********************************************************************/ + +static BOOL set_user_info_23(SAM_USER_INFO_23 *id23, uint32 rid) +{ + SAM_ACCOUNT *pwd = NULL; + pstring plaintext_buf; + uint32 len; + uint16 acct_ctrl; + + if (id23 == NULL) { + DEBUG(5, ("set_user_info_23: NULL id23\n")); + return False; + } + + pdb_init_sam(&pwd); + + if (!pdb_getsampwrid(pwd, rid)) { + pdb_free_sam(&pwd); + return False; + } + + DEBUG(5, ("Attempting administrator password change (level 23) for user %s\n", + pdb_get_username(pwd))); + + acct_ctrl = pdb_get_acct_ctrl(pwd); + + copy_id23_to_sam_passwd(pwd, id23); + + if (!decode_pw_buffer((char*)id23->pass, plaintext_buf, 256, &len)) { + pdb_free_sam(&pwd); + return False; + } + + if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) { + pdb_free_sam(&pwd); + return False; + } + + /* if it's a trust account, don't update /etc/passwd */ + if ( (!IS_SAM_UNIX_USER(pwd)) || + ( (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() ) + if(!chgpasswd(pdb_get_username(pwd), "", plaintext_buf, True)) { + pdb_free_sam(&pwd); + return False; + } + } + + ZERO_STRUCT(plaintext_buf); + + if(!pdb_update_sam_account(pwd)) { + pdb_free_sam(&pwd); + return False; + } + + pdb_free_sam(&pwd); + + return True; +} + +/******************************************************************* + set_user_info_pw + ********************************************************************/ + +static BOOL set_user_info_pw(char *pass, uint32 rid) +{ + SAM_ACCOUNT *pwd = NULL; + uint32 len; + pstring plaintext_buf; + uint16 acct_ctrl; + + pdb_init_sam(&pwd); + + if (!pdb_getsampwrid(pwd, rid)) { + pdb_free_sam(&pwd); + return False; + } + + DEBUG(5, ("Attempting administrator password change for user %s\n", + pdb_get_username(pwd))); + + acct_ctrl = pdb_get_acct_ctrl(pwd); + + ZERO_STRUCT(plaintext_buf); + + if (!decode_pw_buffer(pass, plaintext_buf, 256, &len)) { + pdb_free_sam(&pwd); + return False; + } + + if (!pdb_set_plaintext_passwd (pwd, plaintext_buf)) { + pdb_free_sam(&pwd); + return False; + } + + /* if it's a trust account, don't update /etc/passwd */ + if ( (!IS_SAM_UNIX_USER(pwd)) || + ( (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()) { + if(!chgpasswd(pdb_get_username(pwd), "", plaintext_buf, True)) { + pdb_free_sam(&pwd); + return False; + } + } + } + + ZERO_STRUCT(plaintext_buf); + + DEBUG(5,("set_user_info_pw: pdb_update_pwd()\n")); + + /* update the SAMBA password */ + if(!pdb_update_sam_account(pwd)) { + pdb_free_sam(&pwd); + return False; + } + + pdb_free_sam(&pwd); + + return True; +} + +/******************************************************************* + samr_reply_set_userinfo + ********************************************************************/ + +NTSTATUS _samr_set_userinfo(pipes_struct *p, SAMR_Q_SET_USERINFO *q_u, SAMR_R_SET_USERINFO *r_u) +{ + uint32 rid = 0x0; + DOM_SID sid; + POLICY_HND *pol = &q_u->pol; + uint16 switch_value = q_u->switch_value; + SAM_USERINFO_CTR *ctr = q_u->ctr; + + DEBUG(5, ("_samr_set_userinfo: %d\n", __LINE__)); + + r_u->status = NT_STATUS_OK; + + /* find the policy handle. open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_split_rid(&sid, &rid); + + DEBUG(5, ("_samr_set_userinfo: rid:0x%x, level:%d\n", rid, switch_value)); + + if (ctr == NULL) { + DEBUG(5, ("_samr_set_userinfo: NULL info level\n")); + return NT_STATUS_INVALID_INFO_CLASS; + } + + /* ok! user info levels (lots: see MSDEV help), off we go... */ + switch (switch_value) { + case 0x12: + if (!set_user_info_12(ctr->info.id12, rid)) + return NT_STATUS_ACCESS_DENIED; + break; + + case 24: + SamOEMhash(ctr->info.id24->pass, p->session_key, 516); + + dump_data(100, (char *)ctr->info.id24->pass, 516); + + if (!set_user_info_pw((char *)ctr->info.id24->pass, rid)) + return NT_STATUS_ACCESS_DENIED; + break; + + case 25: +#if 0 + /* + * Currently we don't really know how to unmarshall + * the level 25 struct, and the password encryption + * is different. This is a placeholder for when we + * do understand it. In the meantime just return INVALID + * info level and W2K SP2 drops down to level 23... JRA. + */ + + SamOEMhash(ctr->info.id25->pass, p->session_key, 532); + + dump_data(100, (char *)ctr->info.id25->pass, 532); + + if (!set_user_info_pw(ctr->info.id25->pass, rid)) + return NT_STATUS_ACCESS_DENIED; + break; +#endif + return NT_STATUS_INVALID_INFO_CLASS; + + case 23: + SamOEMhash(ctr->info.id23->pass, p->session_key, 516); + + dump_data(100, (char *)ctr->info.id23->pass, 516); + + if (!set_user_info_23(ctr->info.id23, rid)) + return NT_STATUS_ACCESS_DENIED; + break; + + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + return r_u->status; +} + +/******************************************************************* + samr_reply_set_userinfo2 + ********************************************************************/ + +NTSTATUS _samr_set_userinfo2(pipes_struct *p, SAMR_Q_SET_USERINFO2 *q_u, SAMR_R_SET_USERINFO2 *r_u) +{ + DOM_SID sid; + uint32 rid = 0x0; + SAM_USERINFO_CTR *ctr = q_u->ctr; + POLICY_HND *pol = &q_u->pol; + uint16 switch_value = q_u->switch_value; + + DEBUG(5, ("samr_reply_set_userinfo2: %d\n", __LINE__)); + + r_u->status = NT_STATUS_OK; + + /* find the policy handle. open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_split_rid(&sid, &rid); + + DEBUG(5, ("samr_reply_set_userinfo2: rid:0x%x\n", rid)); + + if (ctr == NULL) { + DEBUG(5, ("samr_reply_set_userinfo2: NULL info level\n")); + return NT_STATUS_INVALID_INFO_CLASS; + } + + switch_value=ctr->switch_value; + + /* ok! user info levels (lots: see MSDEV help), off we go... */ + switch (switch_value) { + case 21: + if (!set_user_info_21(ctr->info.id21, rid)) + return NT_STATUS_ACCESS_DENIED; + break; + case 16: + if (!set_user_info_10(ctr->info.id10, rid)) + return NT_STATUS_ACCESS_DENIED; + break; + case 18: + /* Used by AS/U JRA. */ + if (!set_user_info_12(ctr->info.id12, rid)) + return NT_STATUS_ACCESS_DENIED; + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + return r_u->status; +} + +/********************************************************************* + _samr_query_aliasmem +*********************************************************************/ + +NTSTATUS _samr_query_useraliases(pipes_struct *p, SAMR_Q_QUERY_USERALIASES *q_u, SAMR_R_QUERY_USERALIASES *r_u) +{ + int num_groups = 0, tmp_num_groups=0; + uint32 *rids=NULL, *new_rids=NULL, *tmp_rids=NULL; + struct samr_info *info = NULL; + int i,j; + /* until i see a real useraliases query, we fack one up */ + + /* I have seen one, JFM 2/12/2001 */ + /* + * Explanation of what this call does: + * for all the SID given in the request: + * return a list of alias (local groups) + * that have those SID as members. + * + * and that's the alias in the domain specified + * in the policy_handle + * + * if the policy handle is on an incorrect sid + * for example a user's sid + * we should reply NT_STATUS_OBJECT_TYPE_MISMATCH + */ + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_query_useraliases: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_check_is_domain(&info->sid) && + !sid_check_is_builtin(&info->sid)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + + for (i=0; i<q_u->num_sids1; i++) { + + r_u->status=get_alias_user_groups(p->mem_ctx, &info->sid, &tmp_num_groups, &tmp_rids, &(q_u->sid[i].sid)); + + /* + * if there is an error, we just continue as + * it can be an unfound user or group + */ + if (NT_STATUS_IS_ERR(r_u->status)) { + DEBUG(10,("_samr_query_useraliases: an error occured while getting groups\n")); + continue; + } + + if (tmp_num_groups==0) { + DEBUG(10,("_samr_query_useraliases: no groups found\n")); + continue; + } + + new_rids=(uint32 *)talloc_realloc(p->mem_ctx, rids, (num_groups+tmp_num_groups)*sizeof(uint32)); + if (new_rids==NULL) { + DEBUG(0,("_samr_query_useraliases: could not realloc memory\n")); + return NT_STATUS_NO_MEMORY; + } + rids=new_rids; + + for (j=0; j<tmp_num_groups; j++) + rids[j+num_groups]=tmp_rids[j]; + + safe_free(tmp_rids); + + num_groups+=tmp_num_groups; + } + + init_samr_r_query_useraliases(r_u, num_groups, rids, NT_STATUS_OK); + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_query_aliasmem +*********************************************************************/ + +NTSTATUS _samr_query_aliasmem(pipes_struct *p, SAMR_Q_QUERY_ALIASMEM *q_u, SAMR_R_QUERY_ALIASMEM *r_u) +{ + int i; + + GROUP_MAP map; + int num_uids = 0; + DOM_SID2 *sid; + uid_t *uid=NULL; + + DOM_SID alias_sid; + DOM_SID als_sid; + uint32 alias_rid; + fstring alias_sid_str; + DOM_SID temp_sid; + + SAM_ACCOUNT *sam_user = NULL; + BOOL check; + + /* find the policy handle. open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_copy(&als_sid, &alias_sid); + sid_to_string(alias_sid_str, &alias_sid); + sid_split_rid(&alias_sid, &alias_rid); + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + + if (sid_equal(&alias_sid, &global_sid_Builtin)) { + DEBUG(10, ("lookup on Builtin SID (S-1-5-32)\n")); + if(!get_local_group_from_sid(als_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + } else { + if (sid_equal(&alias_sid, &global_sam_sid)) { + DEBUG(10, ("lookup on Server SID\n")); + if(!get_local_group_from_sid(als_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + } + } + + if(!get_uid_list_of_group(map.gid, &uid, &num_uids)) + return NT_STATUS_NO_SUCH_ALIAS; + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_SID2) * num_uids); + if (num_uids!=0 && sid == NULL) + return NT_STATUS_NO_MEMORY; + + for (i = 0; i < num_uids; i++) { + struct passwd *pass; + uint32 rid; + + sid_copy(&temp_sid, &global_sam_sid); + + pass = getpwuid_alloc(uid[i]); + if (!pass) continue; + + if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user))) { + passwd_free(&pass); + continue; + } + + become_root(); + check = pdb_getsampwnam(sam_user, pass->pw_name); + unbecome_root(); + + if (check != True) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + rid = pdb_get_user_rid(sam_user); + if (rid == 0) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + pdb_free_sam(&sam_user); + passwd_free(&pass); + + sid_append_rid(&temp_sid, rid); + + init_dom_sid2(&sid[i], &temp_sid); + } + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + init_samr_r_query_aliasmem(r_u, num_uids, sid, NT_STATUS_OK); + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_query_groupmem +*********************************************************************/ + +NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u) +{ + int num_uids = 0; + int i; + DOM_SID group_sid; + uint32 group_rid; + fstring group_sid_str; + uid_t *uid=NULL; + + GROUP_MAP map; + + uint32 *rid=NULL; + uint32 *attr=NULL; + + SAM_ACCOUNT *sam_user = NULL; + BOOL check; + + + /* find the policy handle. open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + /* todo: change to use sid_compare_front */ + + sid_split_rid(&group_sid, &group_rid); + sid_to_string(group_sid_str, &group_sid); + DEBUG(10, ("sid is %s\n", group_sid_str)); + + /* can we get a query for an SID outside our domain ? */ + if (!sid_equal(&group_sid, &global_sam_sid)) + return NT_STATUS_NO_SUCH_GROUP; + + sid_append_rid(&group_sid, group_rid); + DEBUG(10, ("lookup on Domain SID\n")); + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + if(!get_uid_list_of_group(map.gid, &uid, &num_uids)) + return NT_STATUS_NO_SUCH_GROUP; + + rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num_uids); + attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num_uids); + + if (num_uids!=0 && (rid==NULL || attr==NULL)) + return NT_STATUS_NO_MEMORY; + + for (i=0; i<num_uids; i++) { + struct passwd *pass; + uint32 urid; + + pass = getpwuid_alloc(uid[i]); + if (!pass) continue; + + if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user))) { + passwd_free(&pass); + continue; + } + + become_root(); + check = pdb_getsampwnam(sam_user, pass->pw_name); + unbecome_root(); + + if (check != True) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + urid = pdb_get_user_rid(sam_user); + if (urid == 0) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + pdb_free_sam(&sam_user); + passwd_free(&pass); + + rid[i] = urid; + attr[i] = SID_NAME_USER; + } + + init_samr_r_query_groupmem(r_u, num_uids, rid, attr, NT_STATUS_OK); + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_add_aliasmem +*********************************************************************/ + +NTSTATUS _samr_add_aliasmem(pipes_struct *p, SAMR_Q_ADD_ALIASMEM *q_u, SAMR_R_ADD_ALIASMEM *r_u) +{ + DOM_SID alias_sid; + fstring alias_sid_str; + uid_t uid; + struct passwd *pwd; + struct group *grp; + fstring grp_name; + uint32 rid; + GROUP_MAP map; + NTSTATUS ret; + SAM_ACCOUNT *sam_user = NULL; + BOOL check; + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_to_string(alias_sid_str, &alias_sid); + DEBUG(10, ("sid is %s\n", alias_sid_str)); + + if (sid_compare(&alias_sid, &global_sam_sid)>0) { + DEBUG(10, ("adding member on Server SID\n")); + if(!get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + } else { + if (sid_compare(&alias_sid, &global_sid_Builtin)>0) { + DEBUG(10, ("adding member on BUILTIN SID\n")); + if( !get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + } else + return NT_STATUS_NO_SUCH_ALIAS; + } + + sid_split_rid(&q_u->sid.sid, &rid); + + ret = pdb_init_sam(&sam_user); + if (NT_STATUS_IS_ERR(ret)) + return ret; + + check = pdb_getsampwrid(sam_user, rid); + + if (check != True) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + uid = pdb_get_uid(sam_user); + if (uid == -1) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + pdb_free_sam(&sam_user); + + if ((pwd=getpwuid(uid)) == NULL) + return NT_STATUS_NO_SUCH_USER; + + if ((grp=getgrgid(map.gid)) == NULL) + return NT_STATUS_NO_SUCH_ALIAS; + + /* we need to copy the name otherwise it's overloaded in user_in_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* if the user is already in the group */ + if(user_in_group_list(pwd->pw_name, grp_name)) + return NT_STATUS_MEMBER_IN_ALIAS; + + /* + * ok, the group exist, the user exist, the user is not in the group, + * we can (finally) add it to the group ! + */ + smb_add_user_group(grp_name, pwd->pw_name); + + /* check if the user has been added then ... */ + if(!user_in_group_list(pwd->pw_name, grp_name)) + return NT_STATUS_MEMBER_NOT_IN_ALIAS; /* don't know what to reply else */ + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_del_aliasmem +*********************************************************************/ + +NTSTATUS _samr_del_aliasmem(pipes_struct *p, SAMR_Q_DEL_ALIASMEM *q_u, SAMR_R_DEL_ALIASMEM *r_u) +{ + DOM_SID alias_sid; + fstring alias_sid_str; + struct group *grp; + fstring grp_name; + uint32 rid; + GROUP_MAP map; + SAM_ACCOUNT *sam_pass=NULL; + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_to_string(alias_sid_str, &alias_sid); + DEBUG(10, ("_samr_del_aliasmem:sid is %s\n", alias_sid_str)); + + if (!sid_check_is_in_our_domain(&alias_sid) && + !sid_check_is_in_builtin(&alias_sid)) { + DEBUG(10, ("_samr_del_aliasmem:invalid alias group\n")); + return NT_STATUS_NO_SUCH_ALIAS; + } + + if( !get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + if ((grp=getgrgid(map.gid)) == NULL) + return NT_STATUS_NO_SUCH_ALIAS; + + /* we need to copy the name otherwise it's overloaded in user_in_group_list */ + fstrcpy(grp_name, grp->gr_name); + + sid_peek_rid(&q_u->sid.sid, &rid); + + /* check if the user exists before trying to remove it from the group */ + pdb_init_sam(&sam_pass); + if(!pdb_getsampwrid(sam_pass, rid)) { + DEBUG(5,("_samr_del_aliasmem:User %s doesn't exist.\n", pdb_get_username(sam_pass))); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + /* if the user is not in the group */ + if(!user_in_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_MEMBER_IN_ALIAS; + } + + smb_delete_user_group(grp_name, pdb_get_username(sam_pass)); + + /* check if the user has been removed then ... */ + if(user_in_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_MEMBER_NOT_IN_ALIAS; /* don't know what to reply else */ + } + + pdb_free_sam(&sam_pass); + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_add_groupmem +*********************************************************************/ + +NTSTATUS _samr_add_groupmem(pipes_struct *p, SAMR_Q_ADD_GROUPMEM *q_u, SAMR_R_ADD_GROUPMEM *r_u) +{ + DOM_SID group_sid; + fstring group_sid_str; + struct passwd *pwd; + struct group *grp; + fstring grp_name; + GROUP_MAP map; + uid_t uid; + NTSTATUS ret; + SAM_ACCOUNT *sam_user; + BOOL check; + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_to_string(group_sid_str, &group_sid); + DEBUG(10, ("sid is %s\n", group_sid_str)); + + if (sid_compare(&group_sid, &global_sam_sid)<=0) + return NT_STATUS_NO_SUCH_GROUP; + + DEBUG(10, ("lookup on Domain SID\n")); + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + ret = pdb_init_sam(&sam_user); + if (NT_STATUS_IS_ERR(ret)) + return ret; + + check = pdb_getsampwrid(sam_user, q_u->rid); + + if (check != True) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + uid = pdb_get_uid(sam_user); + if (uid == -1) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + pdb_free_sam(&sam_user); + + if ((pwd=getpwuid(uid)) == NULL) + return NT_STATUS_NO_SUCH_USER; + + if ((grp=getgrgid(map.gid)) == NULL) + return NT_STATUS_NO_SUCH_GROUP; + + /* we need to copy the name otherwise it's overloaded in user_in_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* if the user is already in the group */ + if(user_in_group_list(pwd->pw_name, grp_name)) + return NT_STATUS_MEMBER_IN_GROUP; + + /* + * ok, the group exist, the user exist, the user is not in the group, + * + * we can (finally) add it to the group ! + */ + + smb_add_user_group(grp_name, pwd->pw_name); + + /* check if the user has been added then ... */ + if(!user_in_group_list(pwd->pw_name, grp_name)) + return NT_STATUS_MEMBER_NOT_IN_GROUP; /* don't know what to reply else */ + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_del_groupmem +*********************************************************************/ + +NTSTATUS _samr_del_groupmem(pipes_struct *p, SAMR_Q_DEL_GROUPMEM *q_u, SAMR_R_DEL_GROUPMEM *r_u) +{ + DOM_SID group_sid; + SAM_ACCOUNT *sam_pass=NULL; + uint32 rid; + GROUP_MAP map; + fstring grp_name; + struct group *grp; + + /* + * delete the group member named q_u->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, &q_u->pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + if(!sid_check_is_in_our_domain(&group_sid)) + return NT_STATUS_NO_SUCH_GROUP; + + rid=q_u->rid; + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + if ((grp=getgrgid(map.gid)) == NULL) + return NT_STATUS_NO_SUCH_GROUP; + + /* we need to copy the name otherwise it's overloaded in user_in_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* check if the user exists before trying to remove it from the group */ + pdb_init_sam(&sam_pass); + if(!pdb_getsampwrid(sam_pass, rid)) { + DEBUG(5,("User %s doesn't exist.\n", pdb_get_username(sam_pass))); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + /* if the user is not in the group */ + if(!user_in_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_MEMBER_NOT_IN_GROUP; + } + + smb_delete_user_group(grp_name, pdb_get_username(sam_pass)); + + /* check if the user has been removed then ... */ + if(user_in_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_ACCESS_DENIED; /* don't know what to reply else */ + } + + pdb_free_sam(&sam_pass); + return NT_STATUS_OK; + +} + +/********************************************************************* + _samr_delete_dom_user +*********************************************************************/ + +NTSTATUS _samr_delete_dom_user(pipes_struct *p, SAMR_Q_DELETE_DOM_USER *q_u, SAMR_R_DELETE_DOM_USER *r_u ) +{ + DOM_SID user_sid; + SAM_ACCOUNT *sam_pass=NULL; + uint32 rid; + + DEBUG(5, ("_samr_delete_dom_user: %d\n", __LINE__)); + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->user_pol, &user_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_check_is_in_our_domain(&user_sid)) + return NT_STATUS_CANNOT_DELETE; + + sid_peek_rid(&user_sid, &rid); + + /* check if the user exists before trying to delete */ + pdb_init_sam(&sam_pass); + if(!pdb_getsampwrid(sam_pass, rid)) { + DEBUG(5,("_samr_delete_dom_user:User %s doesn't exist.\n", pdb_get_username(sam_pass))); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + /* delete the unix side */ + /* + * note: we don't check if the delete really happened + * as the script is not necessary present + * and maybe the sysadmin doesn't want to delete the unix side + */ + smb_delete_user(pdb_get_username(sam_pass)); + + /* and delete the samba side */ + if (!pdb_delete_sam_account(sam_pass)) { + DEBUG(5,("_samr_delete_dom_user:Failed to delete entry for user %s.\n", pdb_get_username(sam_pass))); + pdb_free_sam(&sam_pass); + return NT_STATUS_CANNOT_DELETE; + } + + pdb_free_sam(&sam_pass); + + if (!close_policy_hnd(p, &q_u->user_pol)) + return NT_STATUS_OBJECT_NAME_INVALID; + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_delete_dom_group +*********************************************************************/ + +NTSTATUS _samr_delete_dom_group(pipes_struct *p, SAMR_Q_DELETE_DOM_GROUP *q_u, SAMR_R_DELETE_DOM_GROUP *r_u) +{ + DOM_SID group_sid; + DOM_SID dom_sid; + uint32 group_rid; + fstring group_sid_str; + gid_t gid; + struct group *grp; + GROUP_MAP map; + + DEBUG(5, ("samr_delete_dom_group: %d\n", __LINE__)); + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->group_pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_copy(&dom_sid, &group_sid); + sid_to_string(group_sid_str, &dom_sid); + sid_split_rid(&dom_sid, &group_rid); + + DEBUG(10, ("sid is %s\n", group_sid_str)); + + /* we check if it's our SID before deleting */ + if (!sid_equal(&dom_sid, &global_sam_sid)) + return NT_STATUS_NO_SUCH_GROUP; + + DEBUG(10, ("lookup on Domain SID\n")); + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + gid=map.gid; + + /* check if group really exists */ + if ( (grp=getgrgid(gid)) == NULL) + return NT_STATUS_NO_SUCH_GROUP; + + /* we can delete the UNIX group */ + smb_delete_group(grp->gr_name); + + /* check if the group has been successfully deleted */ + if ( (grp=getgrgid(gid)) != NULL) + return NT_STATUS_ACCESS_DENIED; + + if(!group_map_remove(group_sid)) + return NT_STATUS_ACCESS_DENIED; + + if (!close_policy_hnd(p, &q_u->group_pol)) + return NT_STATUS_OBJECT_NAME_INVALID; + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_delete_dom_alias +*********************************************************************/ + +NTSTATUS _samr_delete_dom_alias(pipes_struct *p, SAMR_Q_DELETE_DOM_ALIAS *q_u, SAMR_R_DELETE_DOM_ALIAS *r_u) +{ + DOM_SID alias_sid; + DOM_SID dom_sid; + uint32 alias_rid; + fstring alias_sid_str; + gid_t gid; + struct group *grp; + GROUP_MAP map; + + DEBUG(5, ("_samr_delete_dom_alias: %d\n", __LINE__)); + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &alias_sid)) + return NT_STATUS_INVALID_HANDLE; + + sid_copy(&dom_sid, &alias_sid); + sid_to_string(alias_sid_str, &dom_sid); + sid_split_rid(&dom_sid, &alias_rid); + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + + /* we check if it's our SID before deleting */ + if (!sid_equal(&dom_sid, &global_sam_sid)) + return NT_STATUS_NO_SUCH_ALIAS; + + DEBUG(10, ("lookup on Local SID\n")); + + if(!get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + gid=map.gid; + + /* check if group really exists */ + if ( (grp=getgrgid(gid)) == NULL) + return NT_STATUS_NO_SUCH_ALIAS; + + /* we can delete the UNIX group */ + smb_delete_group(grp->gr_name); + + /* check if the group has been successfully deleted */ + if ( (grp=getgrgid(gid)) != NULL) + return NT_STATUS_ACCESS_DENIED; + + /* don't check if we removed it as it could be an un-mapped group */ + group_map_remove(alias_sid); + + if (!close_policy_hnd(p, &q_u->alias_pol)) + return NT_STATUS_OBJECT_NAME_INVALID; + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_create_dom_group +*********************************************************************/ + +NTSTATUS _samr_create_dom_group(pipes_struct *p, SAMR_Q_CREATE_DOM_GROUP *q_u, SAMR_R_CREATE_DOM_GROUP *r_u) +{ + DOM_SID dom_sid; + DOM_SID info_sid; + fstring name; + fstring sid_string; + struct group *grp; + struct samr_info *info; + PRIVILEGE_SET priv_set; + + init_privilege(&priv_set); + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &dom_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_equal(&dom_sid, &global_sam_sid)) + return NT_STATUS_ACCESS_DENIED; + + /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/ + + unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1); + + /* check if group already exist */ + if ((grp=getgrnam(name)) != NULL) + return NT_STATUS_GROUP_EXISTS; + + /* we can create the UNIX group */ + smb_create_group(name); + + /* check if the group has been successfully created */ + if ((grp=getgrnam(name)) == NULL) + return NT_STATUS_ACCESS_DENIED; + + r_u->rid=pdb_gid_to_group_rid(grp->gr_gid); + + /* add the group to the mapping table */ + sid_copy(&info_sid, &global_sam_sid); + sid_append_rid(&info_sid, r_u->rid); + sid_to_string(sid_string, &info_sid); + + if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_DOM_GRP, name, NULL, priv_set, PR_ACCESS_FROM_NETWORK)) + return NT_STATUS_ACCESS_DENIED; + + if ((info = get_samr_info_by_sid(&info_sid)) == NULL) + return NT_STATUS_NO_MEMORY; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_create_dom_alias +*********************************************************************/ + +NTSTATUS _samr_create_dom_alias(pipes_struct *p, SAMR_Q_CREATE_DOM_ALIAS *q_u, SAMR_R_CREATE_DOM_ALIAS *r_u) +{ + DOM_SID dom_sid; + DOM_SID info_sid; + fstring name; + fstring sid_string; + struct group *grp; + struct samr_info *info; + PRIVILEGE_SET priv_set; + + init_privilege(&priv_set); + + /* Find the policy handle. Open a policy on it. */ + if (!get_lsa_policy_samr_sid(p, &q_u->dom_pol, &dom_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (!sid_equal(&dom_sid, &global_sam_sid)) + return NT_STATUS_ACCESS_DENIED; + + /* TODO: check if allowed to create group and add a become_root/unbecome_root pair.*/ + + unistr2_to_ascii(name, &q_u->uni_acct_desc, sizeof(name)-1); + + /* check if group already exists */ + if ( (grp=getgrnam(name)) != NULL) + return NT_STATUS_GROUP_EXISTS; + + /* we can create the UNIX group */ + smb_create_group(name); + + /* check if the group has been successfully created */ + if ((grp=getgrnam(name)) == NULL) + return NT_STATUS_ACCESS_DENIED; + + r_u->rid=pdb_gid_to_group_rid(grp->gr_gid); + + sid_copy(&info_sid, &global_sam_sid); + sid_append_rid(&info_sid, r_u->rid); + sid_to_string(sid_string, &info_sid); + + /* add the group to the mapping table */ + if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_ALIAS, name, NULL, priv_set, PR_ACCESS_FROM_NETWORK)) + return NT_STATUS_ACCESS_DENIED; + + if ((info = get_samr_info_by_sid(&info_sid)) == NULL) + return NT_STATUS_NO_MEMORY; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->alias_pol, free_samr_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_query_groupinfo + +sends the name/comment pair of a domain group +level 1 send also the number of users of that group +*********************************************************************/ + +NTSTATUS _samr_query_groupinfo(pipes_struct *p, SAMR_Q_QUERY_GROUPINFO *q_u, SAMR_R_QUERY_GROUPINFO *r_u) +{ + DOM_SID group_sid; + GROUP_MAP map; + uid_t *uid=NULL; + int num_uids=0; + GROUP_INFO_CTR *ctr; + + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_INVALID_HANDLE; + + ctr=(GROUP_INFO_CTR *)talloc_zero(p->mem_ctx, sizeof(GROUP_INFO_CTR)); + if (ctr==NULL) + return NT_STATUS_NO_MEMORY; + + switch (q_u->switch_level) { + case 1: + ctr->switch_value1 = 1; + if(!get_uid_list_of_group(map.gid, &uid, &num_uids)) + return NT_STATUS_NO_SUCH_GROUP; + init_samr_group_info1(&ctr->group.info1, map.nt_name, map.comment, num_uids); + SAFE_FREE(uid); + break; + case 3: + ctr->switch_value1 = 3; + init_samr_group_info3(&ctr->group.info3); + break; + case 4: + ctr->switch_value1 = 4; + init_samr_group_info4(&ctr->group.info4, map.comment); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + init_samr_r_query_groupinfo(r_u, ctr, NT_STATUS_OK); + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_set_groupinfo + + update a domain group's comment. +*********************************************************************/ + +NTSTATUS _samr_set_groupinfo(pipes_struct *p, SAMR_Q_SET_GROUPINFO *q_u, SAMR_R_SET_GROUPINFO *r_u) +{ + DOM_SID group_sid; + GROUP_MAP map; + GROUP_INFO_CTR *ctr; + + if (!get_lsa_policy_samr_sid(p, &q_u->pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + ctr=q_u->ctr; + + switch (ctr->switch_value1) { + case 1: + unistr2_to_ascii(map.comment, &(ctr->group.info1.uni_acct_desc), sizeof(map.comment)-1); + break; + case 4: + unistr2_to_ascii(map.comment, &(ctr->group.info4.uni_acct_desc), sizeof(map.comment)-1); + break; + default: + free_privilege(&map.priv_set); + return NT_STATUS_INVALID_INFO_CLASS; + } + + if(!add_mapping_entry(&map, TDB_REPLACE)) { + free_privilege(&map.priv_set); + return NT_STATUS_NO_SUCH_GROUP; + } + + free_privilege(&map.priv_set); + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_set_groupinfo + + update a domain group's comment. +*********************************************************************/ + +NTSTATUS _samr_set_aliasinfo(pipes_struct *p, SAMR_Q_SET_ALIASINFO *q_u, SAMR_R_SET_ALIASINFO *r_u) +{ + DOM_SID group_sid; + GROUP_MAP map; + ALIAS_INFO_CTR *ctr; + + if (!get_lsa_policy_samr_sid(p, &q_u->alias_pol, &group_sid)) + return NT_STATUS_INVALID_HANDLE; + + if (!get_local_group_from_sid(group_sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + ctr=&q_u->ctr; + + switch (ctr->switch_value1) { + case 3: + unistr2_to_ascii(map.comment, &(ctr->alias.info3.uni_acct_desc), sizeof(map.comment)-1); + break; + default: + free_privilege(&map.priv_set); + return NT_STATUS_INVALID_INFO_CLASS; + } + + if(!add_mapping_entry(&map, TDB_REPLACE)) { + free_privilege(&map.priv_set); + return NT_STATUS_NO_SUCH_GROUP; + } + + free_privilege(&map.priv_set); + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_get_dom_pwinfo +*********************************************************************/ + +NTSTATUS _samr_get_dom_pwinfo(pipes_struct *p, SAMR_Q_GET_DOM_PWINFO *q_u, SAMR_R_GET_DOM_PWINFO *r_u) +{ + /* Actually, returning zeros here works quite well :-). */ + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_open_group +*********************************************************************/ + +NTSTATUS _samr_open_group(pipes_struct *p, SAMR_Q_OPEN_GROUP *q_u, SAMR_R_OPEN_GROUP *r_u) +{ + DOM_SID sid; + DOM_SID info_sid; + GROUP_MAP map; + struct samr_info *info; + fstring sid_string; + + if (!get_lsa_policy_samr_sid(p, &q_u->domain_pol, &sid)) + return NT_STATUS_INVALID_HANDLE; + + /* this should not be hard-coded like this */ + if (!sid_equal(&sid, &global_sam_sid)) + return NT_STATUS_ACCESS_DENIED; + + sid_copy(&info_sid, &global_sam_sid); + sid_append_rid(&info_sid, q_u->rid_group); + sid_to_string(sid_string, &info_sid); + + if ((info = get_samr_info_by_sid(&info_sid)) == NULL) + return NT_STATUS_NO_MEMORY; + + DEBUG(10, ("_samr_open_group:Opening SID: %s\n", sid_string)); + + /* check if that group really exists */ + if (!get_domain_group_from_sid(info->sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + /* get a (unique) handle. open a policy on it. */ + if (!create_policy_hnd(p, &r_u->pol, free_samr_info, (void *)info)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_unknown_2d +*********************************************************************/ + +NTSTATUS _samr_unknown_2d(pipes_struct *p, SAMR_Q_UNKNOWN_2D *q_u, SAMR_R_UNKNOWN_2D *r_u) +{ + DEBUG(0,("_samr_unknown_2d: Not yet implemented.\n")); + return NT_STATUS_NOT_IMPLEMENTED; +} + +/******************************************************************* + _samr_unknown_2e + ********************************************************************/ + +NTSTATUS _samr_unknown_2e(pipes_struct *p, SAMR_Q_UNKNOWN_2E *q_u, SAMR_R_UNKNOWN_2E *r_u) +{ + struct samr_info *info = NULL; + SAM_UNK_CTR *ctr; + uint32 min_pass_len,pass_hist,flag; + 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 num_users=0, num_groups=0, num_aliases=0; + + if ((ctr = (SAM_UNK_CTR *)talloc_zero(p->mem_ctx, sizeof(SAM_UNK_CTR))) == NULL) + return NT_STATUS_NO_MEMORY; + + ZERO_STRUCTP(ctr); + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->domain_pol, (void **)&info)) + return NT_STATUS_INVALID_HANDLE; + + switch (q_u->switch_value) { + case 0x01: + account_policy_get(AP_MIN_PASSWORD_LEN, &min_pass_len); + account_policy_get(AP_PASSWORD_HISTORY, &pass_hist); + account_policy_get(AP_USER_MUST_LOGON_TO_CHG_PASS, &flag); + account_policy_get(AP_MAX_PASSWORD_AGE, (int *)&u_expire); + account_policy_get(AP_MIN_PASSWORD_AGE, (int *)&u_min_age); + + unix_to_nt_time_abs(&nt_expire, u_expire); + unix_to_nt_time_abs(&nt_min_age, u_min_age); + + init_unk_info1(&ctr->info.inf1, (uint16)min_pass_len, (uint16)pass_hist, + flag, nt_expire, nt_min_age); + break; + case 0x02: + become_root(); + r_u->status=load_sampwd_entries(info, ACB_NORMAL); + unbecome_root(); + if (NT_STATUS_IS_ERR(r_u->status)) { + DEBUG(5, ("_samr_query_dispinfo: load_sampwd_entries failed\n")); + return r_u->status; + } + num_users=info->disp_info.num_user_account; + free_samr_db(info); + + r_u->status=load_group_domain_entries(info, &global_sam_sid); + if (NT_STATUS_IS_ERR(r_u->status)) { + DEBUG(5, ("_samr_query_dispinfo: load_group_domain_entries failed\n")); + return r_u->status; + } + num_groups=info->disp_info.num_group_account; + free_samr_db(info); + + /* The time call below is to get a sequence number for the sam. FIXME !!! JRA. */ + init_unk_info2(&ctr->info.inf2, global_myworkgroup, global_myname, (uint32) time(NULL), + num_users, num_groups, num_aliases); + break; + case 0x03: + account_policy_get(AP_TIME_TO_LOGOUT, (int *)&u_logout); + unix_to_nt_time_abs(&nt_logout, u_logout); + + init_unk_info3(&ctr->info.inf3, nt_logout); + break; + case 0x05: + init_unk_info5(&ctr->info.inf5, global_myname); + break; + case 0x06: + init_unk_info6(&ctr->info.inf6); + break; + case 0x07: + init_unk_info7(&ctr->info.inf7); + break; + case 0x0c: + account_policy_get(AP_LOCK_ACCOUNT_DURATION, (int *)&u_lock_duration); + account_policy_get(AP_RESET_COUNT_TIME, (int *)&u_reset_time); + account_policy_get(AP_BAD_ATTEMPT_LOCKOUT, &lockout); + + unix_to_nt_time_abs(&nt_lock_duration, u_lock_duration); + unix_to_nt_time_abs(&nt_reset_time, u_reset_time); + + init_unk_info12(&ctr->info.inf12, nt_lock_duration, nt_reset_time, (uint16)lockout); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + init_samr_r_samr_unknown_2e(r_u, q_u->switch_value, ctr, NT_STATUS_OK); + + DEBUG(5,("_samr_unknown_2e: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + _samr_ + ********************************************************************/ + +NTSTATUS _samr_set_dom_info(pipes_struct *p, SAMR_Q_SET_DOMAIN_INFO *q_u, SAMR_R_SET_DOMAIN_INFO *r_u) +{ + time_t u_expire, u_min_age; + time_t u_logout; + time_t u_lock_duration, u_reset_time; + + r_u->status = NT_STATUS_OK; + + DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__)); + + /* find the policy handle. open a policy on it. */ + if (!find_policy_by_hnd(p, &q_u->domain_pol, NULL)) + return NT_STATUS_INVALID_HANDLE; + + DEBUG(5,("_samr_set_dom_info: switch_value: %d\n", q_u->switch_value)); + + switch (q_u->switch_value) { + case 0x01: + u_expire=nt_time_to_unix_abs(&q_u->ctr->info.inf1.expire); + u_min_age=nt_time_to_unix_abs(&q_u->ctr->info.inf1.min_passwordage); + + account_policy_set(AP_MIN_PASSWORD_LEN, (uint32)q_u->ctr->info.inf1.min_length_password); + account_policy_set(AP_PASSWORD_HISTORY, (uint32)q_u->ctr->info.inf1.password_history); + account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, (uint32)q_u->ctr->info.inf1.flag); + account_policy_set(AP_MAX_PASSWORD_AGE, (int)u_expire); + account_policy_set(AP_MIN_PASSWORD_AGE, (int)u_min_age); + break; + case 0x02: + break; + case 0x03: + u_logout=nt_time_to_unix_abs(&q_u->ctr->info.inf3.logout); + account_policy_set(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(&q_u->ctr->info.inf12.duration); + u_reset_time=nt_time_to_unix_abs(&q_u->ctr->info.inf12.reset_count); + + account_policy_set(AP_LOCK_ACCOUNT_DURATION, (int)u_lock_duration); + account_policy_set(AP_RESET_COUNT_TIME, (int)u_reset_time); + account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, (uint32)q_u->ctr->info.inf12.bad_attempt_lockout); + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + init_samr_r_set_domain_info(r_u, NT_STATUS_OK); + + DEBUG(5,("_samr_set_dom_info: %d\n", __LINE__)); + + return r_u->status; +} + diff --git a/source3/rpc_server/srv_spoolss.c b/source3/rpc_server/srv_spoolss.c new file mode 100755 index 0000000000..e6c152c668 --- /dev/null +++ b/source3/rpc_server/srv_spoolss.c @@ -0,0 +1,1468 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +/******************************************************************** + * 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)) { + 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; + + 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; +} + +/******************************************************************* +\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_ENUMPRINTERKEY", SPOOLSS_ENUMPRINTERKEY, api_spoolss_enumprinterkey }, + {"SPOOLSS_ENUMPRINTERDATAEX", SPOOLSS_ENUMPRINTERDATAEX, api_spoolss_enumprinterdataex }, +#if 0 + /* Disabled because it doesn't fix the bug I am looking at but it would be + a shame to throw away the code. -tpot */ + {"SPOOLSS_GETPRINTPROCESSORDIRECTORY",SPOOLSS_GETPRINTPROCESSORDIRECTORY,api_spoolss_getprintprocessordirectory}, +#endif + { NULL, 0, NULL } +}; + +/******************************************************************* +receives a spoolss pipe and responds. +********************************************************************/ +BOOL api_spoolss_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_spoolss_rpc", api_spoolss_cmds); +} diff --git a/source3/rpc_server/srv_spoolss_nt.c b/source3/rpc_server/srv_spoolss_nt.c new file mode 100644 index 0000000000..3bc91c2472 --- /dev/null +++ b/source3/rpc_server/srv_spoolss_nt.c @@ -0,0 +1,7837 @@ +/* + * 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 2000-2001, + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* 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" + +#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_" +#define PRINTER_HANDLE_IS_PRINTER 0 +#define PRINTER_HANDLE_IS_PRINTSERVER 1 + +struct table_node { + char *long_archi; + char *short_archi; + int version; +}; + + +/* structure to store the printer handles */ +/* and a reference to what it's pointing to */ +/* and the notify info asked about */ +/* that's the central struct */ +typedef struct _Printer{ + struct _Printer *prev, *next; + BOOL document_started; + BOOL page_started; + int jobid; /* jobid in printing backend */ + BOOL printer_type; + union { + fstring handlename; + fstring printerservername; + } dev; + uint32 type; + uint32 access_granted; + struct { + uint32 flags; + uint32 options; + fstring localmachine; + uint32 printerlocal; + SPOOL_NOTIFY_OPTION *option; + POLICY_HND client_hnd; + uint32 client_connected; + } notify; + struct { + fstring machine; + fstring user; + } client; +} Printer_entry; + +static Printer_entry *printers_list; + +typedef struct _counter_printer_0 { + ubi_dlNode Next; + ubi_dlNode Prev; + + int snum; + uint32 counter; +} counter_printer_0; + +static ubi_dlList counter_list; + +static struct cli_state cli; +static uint32 smb_connections=0; + + +/* in printing/nt_printing.c */ +extern STANDARD_MAPPING printer_std_mapping; + +#define OUR_HANDLE(hnd) (((hnd)==NULL)?"NULL":(IVAL((hnd)->data5,4)==(uint32)sys_getpid()?"OURS":"OTHER")), \ +((unsigned int)IVAL((hnd)->data5,4)),((unsigned int)sys_getpid()) + +/* 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(POLICY_HND *handle) +{ + WERROR result; + + /* weird if the test succeds !!! */ + if (smb_connections==0) { + DEBUG(0,("srv_spoolss_replycloseprinter:Trying to close non-existant notify backchannel !\n")); + return; + } + + result = cli_spoolss_reply_close_printer(&cli, cli.mem_ctx, 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) { + if(!spoolss_disconnect_from_client(&cli)) + return; + + message_deregister(MSG_PRINTER_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) + srv_spoolss_replycloseprinter(&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; + + /* 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. +****************************************************************************/ + +SPOOL_NOTIFY_OPTION *dup_spool_notify_option(SPOOL_NOTIFY_OPTION *sp) +{ + SPOOL_NOTIFY_OPTION *new_sp = NULL; + + if (!sp) + return NULL; + + new_sp = (SPOOL_NOTIFY_OPTION *)malloc(sizeof(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 **)&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. +****************************************************************************/ + +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; + } + + if (del_a_printer(Printer->dev.handlename) != 0) { + DEBUG(3,("Error deleting printer %s\n", Printer->dev.handlename)); + return WERR_BADFID; + } + + /* Check calling user has permission to delete printer. Note that + since we set the snum parameter to -1 only administrators can + delete the printer. This stops people with the Full Control + permission from deleting the printer. */ + + if (!print_access_check(NULL, -1, PRINTER_ACCESS_ADMINISTER)) { + DEBUG(3, ("printer delete denied by security descriptor\n")); + return WERR_ACCESS_DENIED; + } + + if (*lp_deleteprinter_cmd()) { + + char *cmd = lp_deleteprinter_cmd(); + pstring command; + int ret; + int i; + + /* Printer->dev.handlename equals portname equals sharename */ + slprintf(command, sizeof(command)-1, "%s \"%s\"", cmd, + Printer->dev.handlename); + + DEBUG(10,("Running [%s]\n", command)); + ret = smbrun(command, NULL); + if (ret != 0) { + return WERR_BADFID; /* What to return here? */ + } + DEBUGADD(10,("returned [%d]\n", ret)); + + /* Send SIGHUP to process group... is there a better way? */ + kill(0, SIGHUP); + + if ( ( i = lp_servicenumber( Printer->dev.handlename ) ) >= 0 ) { + lp_killservice( i ); + return WERR_OK; + } else + return WERR_ACCESS_DENIED; + } + + return WERR_OK; +} + +/**************************************************************************** + Return the snum of a printer corresponding to an handle. +****************************************************************************/ + +static BOOL get_printer_snum(pipes_struct *p, POLICY_HND *hnd, int *number) +{ + 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 PRINTER_HANDLE_IS_PRINTER: + DEBUG(4,("short name:%s\n", Printer->dev.handlename)); + *number = print_queue_snum(Printer->dev.handlename); + return (*number != -1); + case PRINTER_HANDLE_IS_PRINTSERVER: + 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 = PRINTER_HANDLE_IS_PRINTSERVER; + } + /* it's a printer */ + else { + DEBUGADD(4,("Printer is a printer\n")); + Printer->printer_type = PRINTER_HANDLE_IS_PRINTER; + } + + return True; +} + +/**************************************************************************** + Set printer handle name. +****************************************************************************/ + +static BOOL set_printer_hnd_name(Printer_entry *Printer, char *handlename) +{ + int snum; + int n_services=lp_numservices(); + char *aprinter; + fstring sname; + BOOL found=False; + + DEBUG(4,("Setting printer name=%s (len=%d)\n", handlename, strlen(handlename))); + + if (Printer->printer_type==PRINTER_HANDLE_IS_PRINTSERVER) { + ZERO_STRUCT(Printer->dev.printerservername); + strncpy(Printer->dev.printerservername, handlename, strlen(handlename)); + return True; + } + + if (Printer->printer_type!=PRINTER_HANDLE_IS_PRINTER) + return False; + + if (*handlename=='\\') { + aprinter=strchr_m(handlename+2, '\\'); + aprinter++; + } + else { + aprinter=handlename; + } + + DEBUGADD(5,("searching for [%s] (len=%d)\n", aprinter, strlen(aprinter))); + + /* + * The original code allowed smbd to store a printer name that + * was different from the share name. This is not possible + * anymore, so I've simplified this loop greatly. Here + * we are just verifying that the printer name is a valid + * printer service defined in smb.conf + * --jerry [Fri Feb 15 11:17:46 CST 2002] + */ + + for (snum=0; snum<n_services; snum++) { + + if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) ) + continue; + + fstrcpy(sname, lp_servicename(snum)); + + DEBUGADD(5,("share:%s\n",sname)); + + if (! StrCaseCmp(sname, aprinter)) { + found = True; + break; + } + + } + + + if (!found) { + DEBUGADD(4,("Printer not found\n")); + return False; + } + + DEBUGADD(4,("set_printer_hnd_name: Printer found: %s -> %s\n", aprinter, sname)); + + ZERO_STRUCT(Printer->dev.handlename); + fstrcpy(Printer->dev.handlename, 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=(Printer_entry *)malloc(sizeof(Printer_entry))) == NULL) + return False; + + ZERO_STRUCTP(new_printer); + + new_printer->notify.option=NULL; + + /* Add to the internal list. */ + DLIST_ADD(printers_list, new_printer); + + if (!create_policy_hnd(p, hnd, free_printer_entry, new_printer)) { + SAFE_FREE(new_printer); + 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; +} + +/**************************************************************************** + Allocate more memory for a BUFFER. +****************************************************************************/ + +static BOOL alloc_buffer_size(NEW_BUFFER *buffer, uint32 buffer_size) +{ + prs_struct *ps; + uint32 extra_space; + uint32 old_offset; + + ps= &buffer->prs; + + /* damn, I'm doing the reverse operation of prs_grow() :) */ + if (buffer_size < prs_data_size(ps)) + extra_space=0; + else + extra_space = buffer_size - prs_data_size(ps); + + /* + * save the offset and move to the end of the buffer + * prs_grow() checks the extra_space against the offset + */ + old_offset=prs_offset(ps); + prs_set_offset(ps, prs_data_size(ps)); + + if (!prs_grow(ps, extra_space)) + return False; + + prs_set_offset(ps, old_offset); + + buffer->string_at_end=prs_data_size(ps); + + return True; +} +/*************************************************************************** + Always give preference Printer_entry.notify.option over + Printer_entry.notify.flags. Return True if we should send notification + events using SPOOLSS_RRPCN. False means that we should use + SPOOLSS_ROUTERREPLYPRINTER. + **************************************************************************/ +static BOOL valid_notify_options(Printer_entry *printer) +{ + if (printer->notify.option == NULL) + return False; + + return True; +} + +/*************************************************************************** + Simple check to see if the client motify handle is set to watch for events + represented by 'flags' + + FIXME!!!! only a stub right now --jerry + **************************************************************************/ + +static BOOL is_client_monitoring_event(Printer_entry *p, uint32 flags) +{ + + return True; +} + +/*************************************************************************** + Server wrapper for cli_spoolss_routerreplyprinter() since the client + function can only send a single change notification at a time. + + FIXME!!! only handles one change currently (PRINTER_CHANGE_SET_PRINTER_DRIVER) + --jerry + **************************************************************************/ + +static WERROR srv_spoolss_routerreplyprinter (struct cli_state *reply_cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, PRINTER_MESSAGE_INFO *info, + NT_PRINTER_INFO_LEVEL *printer) +{ + WERROR result; + uint32 condition = 0x0; + + if (info->flags & PRINTER_MESSAGE_DRIVER) + condition = PRINTER_CHANGE_SET_PRINTER_DRIVER; + + result = cli_spoolss_routerreplyprinter(reply_cli, mem_ctx, pol, condition, + printer->info_2->changeid); + + return result; +} + +/*********************************************************************** + Wrapper around the decision of which RPC use to in the change + notification + **********************************************************************/ + +static WERROR srv_spoolss_send_event_to_client(Printer_entry* Printer, + struct cli_state *send_cli, PRINTER_MESSAGE_INFO *msg, + NT_PRINTER_INFO_LEVEL *info) +{ + WERROR result; + + if (valid_notify_options(Printer)) { + /* This is a single call that can send information about multiple changes */ + if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) + msg->flags |= PRINTER_MESSAGE_ATTRIBUTES; + + result = cli_spoolss_reply_rrpcn(send_cli, send_cli->mem_ctx, &Printer->notify.client_hnd, + msg, info); + } + else { + /* This requires that the server send an individual event notification for each change */ + result = srv_spoolss_routerreplyprinter(send_cli, send_cli->mem_ctx, &Printer->notify.client_hnd, + msg, info); + } + + return result; +} + + +/*********************************************************************** + Send a change notication message on all handles which have a call + back registered + **********************************************************************/ + +static void send_spoolss_event_notification(PRINTER_MESSAGE_INFO *msg) +{ + Printer_entry *find_printer; + WERROR result; + NT_PRINTER_INFO_LEVEL *printer = NULL; + + if (!msg) { + DEBUG(0,("send_spoolss_event_notification: NULL msg pointer!\n")); + return; + } + + for(find_printer = printers_list; find_printer; find_printer = find_printer->next) { + + /* + * If the entry has a connected client we send the message. There should + * only be one of these normally when dealing with the NT/2k spooler. + * However, iterate over all to make sure we deal with user applications + * in addition to spooler service. + * + * While we are only maintaining a single connection to the client, + * the FindFirstPrinterChangeNotification() call is made on a printer + * handle, so "client_connected" represents the whether or not the + * client asked for change notication on this handle. + * + * --jerry + */ + + if (find_printer->notify.client_connected==True) { + + /* does the client care about what changed? */ + + if (msg->flags && !is_client_monitoring_event(find_printer, msg->flags)) { + DEBUG(10,("send_spoolss_event_notification: Client [%s] not monitoring these events\n", + find_printer->client.machine)); + continue; + } + + if (find_printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) + DEBUG(10,("send_spoolss_event_notification: printserver [%s]\n", find_printer->dev.printerservername )); + else + DEBUG(10,("send_spoolss_event_notification: printer [%s]\n", find_printer->dev.handlename)); + + /* + * if handle is a printer, only send if the printer_name matches. + * ...else if handle is a printerserver, send to all + */ + + if (*msg->printer_name && (find_printer->printer_type==PRINTER_HANDLE_IS_PRINTER) + && !strequal(msg->printer_name, find_printer->dev.handlename)) + { + DEBUG(10,("send_spoolss_event_notification: ignoring message sent to %s [%s]\n", + msg->printer_name, find_printer->dev.handlename )); + continue; + } + + + /* lookup the printer if we have a name if we don't already have a + valid NT_PRINTER_INFO_LEVEL structure. And yes I'm assuming we + will always have a non-empty msg.printer_name */ + + if (!printer || !printer->info_2 || strcmp(msg->printer_name, printer->info_2->printername)) + { + + if (printer) { + free_a_printer(&printer, 2); + printer = NULL; + } + + result = get_a_printer(&printer, 2, msg->printer_name); + if (!W_ERROR_IS_OK(result)) + continue; + } + + /* issue the client call */ + + result = srv_spoolss_send_event_to_client(find_printer, &cli, msg, printer); + + if (!W_ERROR_IS_OK(result)) { + DEBUG(5,("send_spoolss_event_notification: Event notification failed [%s]\n", + dos_errstr(result))); + } + } +} + + return; +} +/*************************************************************************** + Receive the notify message and decode the message. Do not send + notification if we sent this originally as that would result in + duplicates. +****************************************************************************/ + +static void srv_spoolss_receive_message(int msg_type, pid_t src, void *buf, size_t len) +{ + PRINTER_MESSAGE_INFO msg; + + if (len < sizeof(msg)) { + DEBUG(2,("srv_spoolss_receive_message: got incorrect message size (%u)!\n", (unsigned int)len)); + return; + } + + memcpy(&msg, buf, sizeof(PRINTER_MESSAGE_INFO)); + + DEBUG(10,("srv_spoolss_receive_message: Got message printer change [queue = %s] low=0x%x high=0x%x flags=0x%x\n", + msg.printer_name, (unsigned int)msg.low, (unsigned int)msg.high, msg.flags )); + + /* Iterate the printer list */ + + send_spoolss_event_notification(&msg); + +} + +/*************************************************************************** + Send a notify event. +****************************************************************************/ + +static BOOL srv_spoolss_sendnotify(char* printer_name, uint32 high, uint32 low, uint32 flags) +{ + char msg[sizeof(PRINTER_MESSAGE_INFO)]; + PRINTER_MESSAGE_INFO info; + + ZERO_STRUCT(info); + + info.low = low; + info.high = high; + info.flags = flags; + fstrcpy(info.printer_name, printer_name); + + memcpy(msg, &info, sizeof(PRINTER_MESSAGE_INFO)); + + DEBUG(10,("srv_spoolss_sendnotify: printer change low=0x%x high=0x%x [%s], flags=0x%x\n", + low, high, printer_name, flags)); + + message_send_all(conn_tdb_ctx(), MSG_PRINTER_NOTIFY, msg, sizeof(PRINTER_MESSAGE_INFO), + False, NULL); + + return True; +} + +/******************************************************************** + 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 = 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(ctx, len*2); + if (unistrcpy(d->devicename.buffer, devmode->devicename.buffer) != len) + return NULL; + } + + + len = unistrlen(devmode->formname.buffer); + if (len != -1) { + d->devicename.buffer = talloc(ctx, len*2); + if (unistrcpy(d->formname.buffer, devmode->formname.buffer) != len) + return NULL; + } + + d->private = talloc_memdup(ctx, devmode->private, devmode->driverextra); + + 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 void 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; + + DEBUG(8,("convert_to_openprinterex\n")); + + q_u_ex->printername_ptr = q_u->printername_ptr; + + if (q_u->printername_ptr) + copy_unistr2(&q_u_ex->printername, &q_u->printername); + + copy_printer_default(ctx, &q_u_ex->printer_default, &q_u->printer_default); +} + +/******************************************************************** + * 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() */ + + convert_to_openprinterex(p->mem_ctx, &q_u_ex, q_u); + + 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)); + + return r_u->status; +} + +/******************************************************************** + * spoolss_open_printer + * + * called from the spoolss dispatcher + ********************************************************************/ + +WERROR _spoolss_open_printer_ex( pipes_struct *p, SPOOL_Q_OPEN_PRINTER_EX *q_u, SPOOL_R_OPEN_PRINTER_EX *r_u) +{ + UNISTR2 *printername = NULL; + PRINTER_DEFAULT *printer_default = &q_u->printer_default; +/* uint32 user_switch = q_u->user_switch; - notused */ +/* SPOOL_USER_CTR user_ctr = q_u->user_ctr; - notused */ + POLICY_HND *handle = &r_u->handle; + + fstring name; + int snum; + struct current_user user; + Printer_entry *Printer=NULL; + + if (q_u->printername_ptr != 0) + printername = &q_u->printername; + + if (printername == NULL) + return WERR_INVALID_PRINTER_NAME; + + /* some sanity check because you can open a printer or a print server */ + /* aka: \\server\printer or \\server */ + unistr2_to_ascii(name, printername, sizeof(name)-1); + + DEBUGADD(3,("checking name: %s\n",name)); + + if (!open_printer_hnd(p, handle, name, 0)) + return WERR_INVALID_PRINTER_NAME; + + 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_PRINTER_NAME; + } + +/* + if (printer_default->datatype_ptr != NULL) + { + unistr2_to_ascii(datatype, printer_default->datatype, sizeof(datatype)-1); + set_printer_hnd_datatype(handle, datatype); + } + else + set_printer_hnd_datatype(handle, ""); +*/ + + /* + 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. + + */ + + get_current_user(&user, p); + + if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) { + if (printer_default->access_required == 0) { + return WERR_OK; + } + else if ((printer_default->access_required & SERVER_ACCESS_ADMINISTER ) == SERVER_ACCESS_ADMINISTER) { + + /* Printserver handles use global struct... */ + snum = -1; + + if (!lp_ms_add_printer_wizard()) { + close_printer_handle(p, handle); + return WERR_ACCESS_DENIED; + } + else if (user.uid == 0 || user_in_list(uidtoname(user.uid), lp_printer_admin(snum))) { + return WERR_OK; + } + else { + close_printer_handle(p, handle); + return WERR_ACCESS_DENIED; + } + } + } + else + { + /* 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)) + 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; + } + + if (!print_access_check(&user, 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=%x\n", printer_default->access_required)); + Printer->access_granted = printer_default->access_required; + + /* + * If we have a default device pointer in the + * printer_default struct, then we need to get + * the printer info from the tdb and if there is + * no default devicemode there then we do a *SET* + * here ! This is insanity.... JRA. + */ + + /* + * If the openprinterex rpc call contains a devmode, + * it's a per-user one. This per-user devmode is derivated + * from the global devmode. Openprinterex() contains a per-user + * devmode for when you do EMF printing and spooling. + * In the EMF case, the NT workstation is only doing half the job + * of rendering the page. The other half is done by running the printer + * driver on the server. + * The EMF file doesn't contain the page description (paper size, orientation, ...). + * The EMF file only contains what is to be printed on the page. + * So in order for the server to know how to print, the NT client sends + * a devicemode attached to the openprinterex call. + * But this devicemode is short lived, it's only valid for the current print job. + * + * If Samba would have supported EMF spooling, this devicemode would + * have been attached to the handle, to sent it to the driver to correctly + * rasterize the EMF file. + * + * As Samba only supports RAW spooling, we only receive a ready-to-print file, + * we just act as a pass-thru between windows and the printer. + * + * In order to know that Samba supports only RAW spooling, NT has to call + * getprinter() at level 2 (attribute field) or NT has to call startdoc() + * and until NT sends a RAW job, we refuse it. + * + * But to call getprinter() or startdoc(), you first need a valid handle, + * and to get an handle you have to call openprintex(). Hence why you have + * a devicemode in the openprinterex() call. + * + * + * Differences between NT4 and NT 2000. + * NT4: + * --- + * On NT4, you only have a global devicemode. This global devicemode can be changed + * by the administrator (or by a user with enough privs). Everytime a user + * wants to print, the devicemode is resetted to the default. In Word, everytime + * you print, the printer's characteristics are always reset to the global devicemode. + * + * NT 2000: + * ------- + * In W2K, there is the notion of per-user devicemode. The first time you use + * a printer, a per-user devicemode is build from the global devicemode. + * If you change your per-user devicemode, it is saved in the registry, under the + * H_KEY_CURRENT_KEY sub_tree. So that everytime you print, you have your default + * printer preferences available. + * + * To change the per-user devicemode: it's the "Printing Preferences ..." button + * on the General Tab of the printer properties windows. + * + * To change the global devicemode: it's the "Printing Defaults..." button + * on the Advanced Tab of the printer properties window. + * + * JFM. + */ + + + +#if 0 + if (printer_default->devmode_cont.devmode != NULL) { + result = printer_write_default_dev( snum, printer_default); + if (result != 0) { + close_printer_handle(p, handle); + return result; + } + } +#endif + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static BOOL convert_printer_info(const SPOOL_PRINTER_INFO_LEVEL *uni, + NT_PRINTER_INFO_LEVEL *printer, uint32 level) +{ + BOOL ret = True; + + switch (level) { + case 2: + ret = uni_2_asc_printer_info_2(uni->info_2, &printer->info_2); + break; + default: + break; + } + + return ret; +} + +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(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->private != NULL)) { + SAFE_FREE(nt_devmode->private); + nt_devmode->driverextra=devmode->driverextra; + if((nt_devmode->private=(uint8 *)malloc(nt_devmode->driverextra * sizeof(uint8))) == NULL) + return False; + memcpy(nt_devmode->private, devmode->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); + + if (!Printer) { + DEBUG(2,("_spoolss_enddocprinter_internal: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + Printer->document_started=False; + print_job_end(Printer->jobid,True); + /* 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); + + if (W_ERROR_IS_OK(result)) { + srv_spoolss_sendnotify(Printer->dev.handlename, 0, PRINTER_CHANGE_DELETE_PRINTER, 0x0); + } + + 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 }, + {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 + * + * We currently delete the driver for the architecture only. + * This can leave the driver for other archtectures. However, + * since every printer associates a "Windows NT x86" driver name + * and we cannot delete that one while it is in use, **and** since + * it is impossible to assign a driver to a Samba printer without + * having the "Windows NT x86" driver installed,... + * + * ....we should not get into trouble here. + * + * --jerry + ********************************************************************/ + +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; + int version; + + unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 ); + unistr2_to_ascii(arch, &q_u->arch, sizeof(arch)-1 ); + + /* 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; + } + + ZERO_STRUCT(info); + if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) { + return WERR_UNKNOWN_PRINTER_DRIVER; + } + + + if (printer_driver_in_use(arch, driver)) + { + return WERR_PRINTER_DRIVER_IN_USE; + } + + return delete_printer_driver(info.info_3); +} + +/******************************************************************** + GetPrinterData on a printer server Handle. +********************************************************************/ + +static BOOL 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 (!strcmp(value, "W3SvcInstalled")) { + *type = 0x4; + if((*data = (uint8 *)talloc_zero(ctx, 4*sizeof(uint8) )) == NULL) + return False; + *needed = 0x4; + return True; + } + + if (!strcmp(value, "BeepEnabled")) { + *type = 0x4; + if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL) + return False; + SIVAL(*data, 0, 0x00); + *needed = 0x4; + return True; + } + + if (!strcmp(value, "EventLog")) { + *type = 0x4; + if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL) + return False; + /* formally was 0x1b */ + SIVAL(*data, 0, 0x0); + *needed = 0x4; + return True; + } + + if (!strcmp(value, "NetPopup")) { + *type = 0x4; + if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL) + return False; + SIVAL(*data, 0, 0x00); + *needed = 0x4; + return True; + } + + if (!strcmp(value, "MajorVersion")) { + *type = 0x4; + if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL) + return False; + SIVAL(*data, 0, 2); + *needed = 0x4; + return True; + } + + if (!strcmp(value, "DefaultSpoolDirectory")) { + fstring string; + + fstrcpy(string, string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH)); + *type = 0x1; + *needed = 2*(strlen(string)+1); + if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL) + return False; + 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 True; + } + + if (!strcmp(value, "Architecture")) { + pstring string="Windows NT x86"; + *type = 0x1; + *needed = 2*(strlen(string)+1); + if((*data = (uint8 *)talloc(ctx, ((*needed > in_size) ? *needed:in_size) *sizeof(uint8))) == NULL) + return False; + 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 True; + } + + return False; +} + +/******************************************************************** + GetPrinterData on a printer Handle. +********************************************************************/ + +static BOOL getprinterdata_printer(pipes_struct *p, TALLOC_CTX *ctx, POLICY_HND *handle, + fstring value, uint32 *type, + uint8 **data, uint32 *needed, uint32 in_size ) +{ + NT_PRINTER_INFO_LEVEL *printer = NULL; + int snum=0; + uint8 *idata=NULL; + uint32 len; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + + DEBUG(5,("getprinterdata_printer\n")); + + if (!Printer) { + DEBUG(2,("getprinterdata_printer: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle))); + return False; + } + + if(!get_printer_snum(p, handle, &snum)) + return False; + + if (!W_ERROR_IS_OK(get_a_printer(&printer, 2, lp_servicename(snum)))) + return False; + + if (!get_specific_param(*printer, 2, value, &idata, type, &len)) { + free_a_printer(&printer, 2); + return False; + } + + free_a_printer(&printer, 2); + + DEBUG(5,("getprinterdata_printer:allocating %d\n", in_size)); + + if (in_size) { + if((*data = (uint8 *)talloc(ctx, in_size *sizeof(uint8) )) == NULL) { + return False; + } + + memset(*data, 0, in_size *sizeof(uint8)); + /* copy the min(in_size, len) */ + memcpy(*data, idata, (len>in_size)?in_size:len *sizeof(uint8)); + } else { + *data = NULL; + } + + *needed = len; + + DEBUG(5,("getprinterdata_printer:copy done\n")); + + SAFE_FREE(idata); + + return True; +} + +/******************************************************************** + * 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; + + fstring value; + BOOL found=False; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + + /* + * 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) { + if((*data=(uint8 *)talloc_zero(p->mem_ctx, 4*sizeof(uint8))) == NULL) + return WERR_NOMEM; + DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + unistr2_to_ascii(value, valuename, sizeof(value)-1); + + if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) + found=getprinterdata_printer_server(p->mem_ctx, value, type, data, needed, *out_size); + else + found= getprinterdata_printer(p, p->mem_ctx, handle, value, type, data, needed, *out_size); + + if (found==False) { + DEBUG(5, ("value not found, 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) + return WERR_NOMEM; + } else { + *data = NULL; + } + + /* error depends on handle type */ + + if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) + return WERR_INVALID_PARAM; + else + return WERR_BADFILE; + } + + if (*needed > *out_size) + return WERR_MORE_DATA; + else + return WERR_OK; +} + +/*************************************************************************** + Connect to the client. +****************************************************************************/ + +static BOOL srv_spoolss_replyopenprinter(char *printer, uint32 localprinter, uint32 type, POLICY_HND *handle) +{ + WERROR result; + + /* + * If it's the first connection, contact the client + * and connect to the IPC$ share anonumously + */ + 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(&cli, unix_printer)) + return False; + + message_register(MSG_PRINTER_NOTIFY, srv_spoolss_receive_message); + + } + + smb_connections++; + + result = cli_spoolss_reply_open_printer(&cli, cli.mem_ctx, 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 + * + * jfmxxxx: before replying OK: status=0 + * should do a rpc call to the workstation asking ReplyOpenPrinter + * have to code it, later. + * + * 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; + SPOOL_NOTIFY_OPTION *option = q_u->option; + + /* 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)-1); + + /* connect to the client machine and send a ReplyOpenPrinter */ + if(srv_spoolss_replyopenprinter(Printer->notify.localmachine, + Printer->notify.printerlocal, 1, + &Printer->notify.client_hnd)) + { + 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) +{ + pstring temp_name, temp; + uint32 len; + + slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name()); + + len = rpcstr_push(temp, temp_name, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + 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(temp, p, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, lp_servicename(snum), sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + /* even if it's strange, that's consistant in all the code */ + + len = rpcstr_push(temp, printer->info_2->portname, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, printer->info_2->drivername, sizeof(temp)-2, STR_TERMINATE); + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + if (*printer->info_2->comment == '\0') + len = rpcstr_push(temp, lp_comment(snum), sizeof(temp)-2, STR_TERMINATE); + else + len = rpcstr_push(temp, printer->info_2->comment, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, printer->info_2->location,sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, printer->info_2->sepfile, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, printer->info_2->printprocessor, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, printer->info_2->parameters, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, printer->info_2->datatype, sizeof(pstring)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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.data.length=0; + data->notify_data.data.string = NULL; +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, queue->fs_user, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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) +{ + pstring temp; + uint32 len; + + len = rpcstr_push(temp, queue->fs_file, sizeof(temp)-2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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. + */ + + char *p = ""; + pstring temp; + 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(temp, p, sizeof(temp) - 2, STR_TERMINATE); + + data->notify_data.data.length = len / 2 - 1; + data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len); + + if (!data->notify_data.data.string) { + data->notify_data.data.length = 0; + return; + } + + memcpy(data->notify_data.data.string, temp, len); +} + +/******************************************************************* + * 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); +} + +#define END 65535 + +struct s_notify_info_data_table +{ + uint16 type; + uint16 field; + 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); +}; + +struct s_notify_info_data_table notify_info_data_table[] = +{ +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME, "PRINTER_NOTIFY_SERVER_NAME", POINTER, spoolss_notify_server_name }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME, "PRINTER_NOTIFY_PRINTER_NAME", POINTER, spoolss_notify_printer_name }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME, "PRINTER_NOTIFY_SHARE_NAME", POINTER, spoolss_notify_share_name }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME, "PRINTER_NOTIFY_PORT_NAME", POINTER, spoolss_notify_port_name }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME, "PRINTER_NOTIFY_DRIVER_NAME", POINTER, spoolss_notify_driver_name }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT, "PRINTER_NOTIFY_COMMENT", POINTER, spoolss_notify_comment }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION, "PRINTER_NOTIFY_LOCATION", POINTER, spoolss_notify_location }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE, "PRINTER_NOTIFY_DEVMODE", POINTER, spoolss_notify_devmode }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE, "PRINTER_NOTIFY_SEPFILE", POINTER, spoolss_notify_sepfile }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR, "PRINTER_NOTIFY_PRINT_PROCESSOR", POINTER, spoolss_notify_print_processor }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS, "PRINTER_NOTIFY_PARAMETERS", POINTER, spoolss_notify_parameters }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE, "PRINTER_NOTIFY_DATATYPE", POINTER, spoolss_notify_datatype }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", POINTER, spoolss_notify_security_desc }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES, "PRINTER_NOTIFY_ATTRIBUTES", ONE_VALUE, spoolss_notify_attributes }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY, "PRINTER_NOTIFY_PRIORITY", ONE_VALUE, spoolss_notify_priority }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY, "PRINTER_NOTIFY_DEFAULT_PRIORITY", ONE_VALUE, spoolss_notify_default_priority }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME, "PRINTER_NOTIFY_START_TIME", ONE_VALUE, spoolss_notify_start_time }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME, "PRINTER_NOTIFY_UNTIL_TIME", ONE_VALUE, spoolss_notify_until_time }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS, "PRINTER_NOTIFY_STATUS", ONE_VALUE, spoolss_notify_status }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING, "PRINTER_NOTIFY_STATUS_STRING", POINTER, NULL }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS, "PRINTER_NOTIFY_CJOBS", ONE_VALUE, spoolss_notify_cjobs }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM, "PRINTER_NOTIFY_AVERAGE_PPM", ONE_VALUE, spoolss_notify_average_ppm }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES, "PRINTER_NOTIFY_TOTAL_PAGES", POINTER, NULL }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED, "PRINTER_NOTIFY_PAGES_PRINTED", POINTER, NULL }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES, "PRINTER_NOTIFY_TOTAL_BYTES", POINTER, NULL }, +{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED, "PRINTER_NOTIFY_BYTES_PRINTED", POINTER, NULL }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINTER_NAME, "JOB_NOTIFY_PRINTER_NAME", POINTER, spoolss_notify_printer_name }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_MACHINE_NAME, "JOB_NOTIFY_MACHINE_NAME", POINTER, spoolss_notify_server_name }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PORT_NAME, "JOB_NOTIFY_PORT_NAME", POINTER, spoolss_notify_port_name }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME, "JOB_NOTIFY_USER_NAME", POINTER, spoolss_notify_username }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_NOTIFY_NAME, "JOB_NOTIFY_NOTIFY_NAME", POINTER, spoolss_notify_username }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DATATYPE, "JOB_NOTIFY_DATATYPE", POINTER, spoolss_notify_datatype }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRINT_PROCESSOR, "JOB_NOTIFY_PRINT_PROCESSOR", POINTER, spoolss_notify_print_processor }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PARAMETERS, "JOB_NOTIFY_PARAMETERS", POINTER, spoolss_notify_parameters }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DRIVER_NAME, "JOB_NOTIFY_DRIVER_NAME", POINTER, spoolss_notify_driver_name }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DEVMODE, "JOB_NOTIFY_DEVMODE", POINTER, spoolss_notify_devmode }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS, "JOB_NOTIFY_STATUS", ONE_VALUE, spoolss_notify_job_status }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_STATUS_STRING, "JOB_NOTIFY_STATUS_STRING", POINTER, spoolss_notify_job_status_string }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SECURITY_DESCRIPTOR, "JOB_NOTIFY_SECURITY_DESCRIPTOR", POINTER, NULL }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT, "JOB_NOTIFY_DOCUMENT", POINTER, spoolss_notify_job_name }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PRIORITY, "JOB_NOTIFY_PRIORITY", ONE_VALUE, spoolss_notify_priority }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_POSITION, "JOB_NOTIFY_POSITION", ONE_VALUE, spoolss_notify_job_position }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED, "JOB_NOTIFY_SUBMITTED", POINTER, spoolss_notify_submitted_time }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_START_TIME, "JOB_NOTIFY_START_TIME", ONE_VALUE, spoolss_notify_start_time }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_UNTIL_TIME, "JOB_NOTIFY_UNTIL_TIME", ONE_VALUE, spoolss_notify_until_time }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TIME, "JOB_NOTIFY_TIME", ONE_VALUE, spoolss_notify_job_time }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_PAGES, "JOB_NOTIFY_TOTAL_PAGES", ONE_VALUE, spoolss_notify_total_pages }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_PAGES_PRINTED, "JOB_NOTIFY_PAGES_PRINTED", ONE_VALUE, spoolss_notify_pages_printed }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_TOTAL_BYTES, "JOB_NOTIFY_TOTAL_BYTES", ONE_VALUE, spoolss_notify_job_size }, +{ JOB_NOTIFY_TYPE, JOB_NOTIFY_BYTES_PRINTED, "JOB_NOTIFY_BYTES_PRINTED", ONE_VALUE, NULL }, +{ END, END, "", END, NULL } +}; + +/******************************************************************* + Return the size of info_data structure. +********************************************************************/ + +static uint32 size_of_notify_info_data(uint16 type, uint16 field) +{ + int i=0; + + while (notify_info_data_table[i].type != END) + { + if ( (notify_info_data_table[i].type == type ) && + (notify_info_data_table[i].field == field ) ) + { + return (notify_info_data_table[i].size); + } + i++; + } + return (65535); +} + +/******************************************************************* + Return the type of notify_info_data. +********************************************************************/ + +static BOOL type_of_notify_info_data(uint16 type, uint16 field) +{ + int i=0; + + while (notify_info_data_table[i].type != END) + { + if ( (notify_info_data_table[i].type == type ) && + (notify_info_data_table[i].field == field ) ) + { + if (notify_info_data_table[i].size == POINTER) + { + return (False); + } + else + { + return (True); + } + } + i++; + } + return (False); +} + +/**************************************************************************** +****************************************************************************/ + +static int search_notify(uint16 type, uint16 field, int *value) +{ + int j; + BOOL found; + + for (j=0, found=False; found==False && notify_info_data_table[j].type != END ; j++) + { + if ( (notify_info_data_table[j].type == type ) && + (notify_info_data_table[j].field == field ) ) + found=True; + } + *value=--j; + + if ( found && (notify_info_data_table[j].fn != NULL) ) + return True; + else + 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->id = id; + info_data->size = size_of_notify_info_data(type, field); + info_data->enc_type = type_of_notify_info_data(type, field); +} + + +/******************************************************************* + * + * fill a notify_info struct with info asked + * + ********************************************************************/ + +static BOOL construct_notify_printer_info(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, *tid; + 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(&printer, 2, lp_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((tid=(SPOOL_NOTIFY_INFO_DATA *)Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) { + DEBUG(2,("construct_notify_printer_info: failed to enlarge buffer info->data!\n")); + return False; + } + else info->data = tid; + + 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, *tid; + + 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((tid=Realloc(info->data, (info->count+1)*sizeof(SPOOL_NOTIFY_INFO_DATA))) == NULL) { + DEBUG(2,("construct_notify_jobs_info: failed to enlarg buffer info->data!\n")); + return False; + } + else info->data = tid; + + 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; + uint32 id; + 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; + id=1; + info->version=2; + info->data=NULL; + info->count=0; + + 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) ) + if (construct_notify_printer_info + (info, snum, option_type, id, mem_ctx)) + id++; + } + + /* + * 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; +} + +/******************************************************************* + * + * 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; + + get_printer_snum(p, hnd, &snum); + + 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(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, 2, + lp_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; +/* uint32 change = q_u->change; - notused. */ +/* SPOOL_NOTIFY_OPTION *option = q_u->option; - notused. */ + 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)); + + /* jfm: the change value isn't used right now. + * we will honour it when + * a) we'll be able to send notification to the client + * b) we'll have a way to communicate between the spoolss process. + * + * same thing for option->flags + * 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. + */ + + /* just ignore the SPOOL_NOTIFY_OPTION */ + + switch (Printer->printer_type) { + case PRINTER_HANDLE_IS_PRINTSERVER: + result = printserver_notify_info(p, handle, info, p->mem_ctx); + break; + + case PRINTER_HANDLE_IS_PRINTER: + result = printer_notify_info(p, handle, info, p->mem_ctx); + break; + } + + done: + return result; +} + +/******************************************************************** + * construct_printer_info_0 + * fill a printer_info_0 struct + ********************************************************************/ + +static BOOL construct_printer_info_0(PRINTER_INFO_0 *printer, int snum) +{ + pstring chaine; + 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; + + if (!W_ERROR_IS_OK(get_a_printer(&ntprinter, 2, lp_servicename(snum)))) + return False; + + count = print_queue_length(snum, &status); + + /* check if we already have a counter for this printer */ + session_counter = (counter_printer_0 *)ubi_dlFirst(&counter_list); + + for(; session_counter; session_counter = (counter_printer_0 *)ubi_dlNext(session_counter)) { + if (session_counter->snum == snum) + break; + } + + /* it's the first time, add it to the list */ + if (session_counter==NULL) { + if((session_counter=(counter_printer_0 *)malloc(sizeof(counter_printer_0))) == NULL) { + free_a_printer(&ntprinter, 2); + return False; + } + ZERO_STRUCTP(session_counter); + session_counter->snum=snum; + session_counter->counter=0; + ubi_dlAddHead( &counter_list, (ubi_dlNode *)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; + + pstrcpy(chaine,ntprinter->info_2->printername); + + init_unistr(&printer->printername, chaine); + + slprintf(chaine,sizeof(chaine)-1,"\\\\%s", get_called_name()); + init_unistr(&printer->servername, chaine); + + 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; +#if 0 /* JERRY */ + printer->major_version = 0x0004; /* NT 4 */ + printer->build_version = 0x0565; /* build 1381 */ +#else + printer->major_version = 0x0005; /* NT 5 */ + printer->build_version = 0x0893; /* build 2195 */ +#endif + 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(uint32 flags, PRINTER_INFO_1 *printer, int snum) +{ + pstring chaine; + pstring chaine2; + NT_PRINTER_INFO_LEVEL *ntprinter = NULL; + + if (!W_ERROR_IS_OK(get_a_printer(&ntprinter, 2, lp_servicename(snum)))) + return False; + + printer->flags=flags; + + if (*ntprinter->info_2->comment == '\0') { + init_unistr(&printer->comment, lp_comment(snum)); + slprintf(chaine,sizeof(chaine)-1,"%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. */ + slprintf(chaine,sizeof(chaine)-1,"%s,%s,%s", ntprinter->info_2->printername, + ntprinter->info_2->drivername, ntprinter->info_2->comment); + } + + slprintf(chaine2,sizeof(chaine)-1,"%s", ntprinter->info_2->printername); + + init_unistr(&printer->description, chaine); + init_unistr(&printer->name, chaine2); + + 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->private); + SAFE_FREE(dev); +} + +/**************************************************************************** + Create a DEVMODE struct. Returns malloced memory. +****************************************************************************/ + +static DEVICEMODE *construct_dev_mode(int snum) +{ + char adevice[32]; + char aform[32]; + NT_PRINTER_INFO_LEVEL *printer = NULL; + NT_DEVICEMODE *ntdevmode = NULL; + DEVICEMODE *devmode = NULL; + + DEBUG(7,("construct_dev_mode\n")); + + DEBUGADD(8,("getting printer characteristics\n")); + + if ((devmode = (DEVICEMODE *)malloc(sizeof(DEVICEMODE))) == NULL) { + DEBUG(2,("construct_dev_mode: malloc fail.\n")); + return NULL; + } + + ZERO_STRUCTP(devmode); + + if (!W_ERROR_IS_OK(get_a_printer(&printer, 2, lp_servicename(snum)))) + goto fail; + + if (printer->info_2->devmode) + ntdevmode = dup_nt_devicemode(printer->info_2->devmode); + + if (ntdevmode == NULL) + goto fail; + + DEBUGADD(8,("loading DEVICEMODE\n")); + + slprintf(adevice, sizeof(adevice)-1, printer->info_2->printername); + init_unistr(&devmode->devicename, adevice); + + slprintf(aform, sizeof(aform)-1, ntdevmode->formname); + init_unistr(&devmode->formname, aform); + + 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->private != NULL) { + if ((devmode->private=(uint8 *)memdup(ntdevmode->private, ntdevmode->driverextra)) == NULL) + goto fail; + } + + free_nt_devicemode(&ntdevmode); + free_a_printer(&printer,2); + + return devmode; + + fail: + + if (ntdevmode) + free_nt_devicemode(&ntdevmode); + if (printer) + free_a_printer(&printer,2); + free_dev_mode(devmode); + + return NULL; +} + +/******************************************************************** + * construct_printer_info_2 + * fill a printer_info_2 struct + ********************************************************************/ + +static BOOL construct_printer_info_2(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(&ntprinter, 2, lp_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(snum)) == NULL) { + DEBUG(8, ("Returning NULL Devicemode!\n")); + } + + if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) { + /* steal the printer info sec_desc structure. [badly done]. */ + printer->secdesc = ntprinter->info_2->secdesc_buf->sec; + ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen memory. */ + ntprinter->info_2->secdesc_buf->len = 0; /* Stolen memory. */ + ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen memory. */ + } + else { + printer->secdesc = NULL; + } + + free_a_printer(&ntprinter, 2); + return True; +} + +/******************************************************************** + * construct_printer_info_3 + * fill a printer_info_3 struct + ********************************************************************/ + +static BOOL construct_printer_info_3(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(&ntprinter, 2, lp_servicename(snum)))) + return False; + + *pp_printer = NULL; + if ((printer = (PRINTER_INFO_3 *)malloc(sizeof(PRINTER_INFO_3))) == NULL) { + DEBUG(2,("construct_printer_info_3: malloc fail.\n")); + return False; + } + + ZERO_STRUCTP(printer); + + printer->flags = 4; /* These are the components of the SD we are returning. */ + if (ntprinter->info_2->secdesc_buf && ntprinter->info_2->secdesc_buf->len != 0) { + /* steal the printer info sec_desc structure. [badly done]. */ + printer->secdesc = ntprinter->info_2->secdesc_buf->sec; + +#if 0 + /* + * Set the flags for the components we are returning. + */ + + if (printer->secdesc->owner_sid) + printer->flags |= OWNER_SECURITY_INFORMATION; + + if (printer->secdesc->grp_sid) + printer->flags |= GROUP_SECURITY_INFORMATION; + + if (printer->secdesc->dacl) + printer->flags |= DACL_SECURITY_INFORMATION; + + if (printer->secdesc->sacl) + printer->flags |= SACL_SECURITY_INFORMATION; +#endif + + ntprinter->info_2->secdesc_buf->sec = NULL; /* Stolen the malloced memory. */ + ntprinter->info_2->secdesc_buf->len = 0; /* Stolen the malloced memory. */ + ntprinter->info_2->secdesc_buf->max_len = 0; /* Stolen the malloced memory. */ + } + + 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_INFO_4 *printer, int snum) +{ + NT_PRINTER_INFO_LEVEL *ntprinter = NULL; + + if (!W_ERROR_IS_OK(get_a_printer(&ntprinter, 2, lp_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_INFO_5 *printer, int snum) +{ + NT_PRINTER_INFO_LEVEL *ntprinter = NULL; + + if (!W_ERROR_IS_OK(get_a_printer(&ntprinter, 2, lp_servicename(snum)))) + return False; + + init_unistr(&printer->printername, ntprinter->info_2->printername); /* printername*/ + init_unistr(&printer->portname, ntprinter->info_2->portname); /* portname */ + printer->attributes = ntprinter->info_2->attributes; + printer->device_not_selected_timeout = 0x3a98; + printer->transmission_retry_timeout = 0xafc8; + + free_a_printer(&ntprinter, 2); + return True; +} + +/******************************************************************** + Spoolss_enumprinters. +********************************************************************/ + +static WERROR enum_all_printers_info_1(uint32 flags, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + int snum; + int i; + int n_services=lp_numservices(); + PRINTER_INFO_1 *tp, *printers=NULL; + PRINTER_INFO_1 current_prt; + + 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(flags, ¤t_prt, snum)) { + if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_1))) == NULL) { + DEBUG(2,("enum_all_printers_info_1: failed to enlarge printers buffer!\n")); + SAFE_FREE(printers); + *returned=0; + return WERR_NOMEM; + } + else printers = tp; + 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 (!alloc_buffer_size(buffer, *needed)) + return WERR_INSUFFICIENT_BUFFER; + + /* fill the buffer with the structures */ + for (i=0; i<*returned; i++) + smb_io_printer_info_1("", buffer, &printers[i], 0); + + /* clear memory */ + SAFE_FREE(printers); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + else + return WERR_OK; +} + +/******************************************************************** + enum_all_printers_info_1_local. +*********************************************************************/ + +static WERROR enum_all_printers_info_1_local(NEW_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, NEW_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; +} + +/******************************************************************** + enum_all_printers_info_1_remote. +*********************************************************************/ + +static WERROR enum_all_printers_info_1_remote(fstring name, NEW_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")); + + /* 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 registring (sp ?) on the PDC (in the same domain) + * + * We should have a TDB here. The registration is done thru an undocumented RPC call. + */ + + if((printer=(PRINTER_INFO_1 *)malloc(sizeof(PRINTER_INFO_1))) == NULL) + return WERR_NOMEM; + + *returned=1; + + slprintf(printername, sizeof(printername)-1,"Windows NT Remote Printers!!\\\\%s", get_called_name()); + slprintf(desc, sizeof(desc)-1,"%s", get_called_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 (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_info_1("", buffer, printer, 0); + + /* clear memory */ + SAFE_FREE(printer); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + else + return WERR_OK; +} + +/******************************************************************** + enum_all_printers_info_1_network. +*********************************************************************/ + +static WERROR enum_all_printers_info_1_network(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + DEBUG(4,("enum_all_printers_info_1_network\n")); + + 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(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + int snum; + int i; + int n_services=lp_numservices(); + PRINTER_INFO_2 *tp, *printers=NULL; + PRINTER_INFO_2 current_prt; + + 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(¤t_prt, snum)) { + if((tp=Realloc(printers, (*returned +1)*sizeof(PRINTER_INFO_2))) == NULL) { + DEBUG(2,("enum_all_printers_info_2: failed to enlarge printers buffer!\n")); + SAFE_FREE(printers); + *returned = 0; + return WERR_NOMEM; + } + else printers = tp; + DEBUG(4,("ReAlloced memory for [%d] PRINTER_INFO_2\n", *returned)); + 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 (!alloc_buffer_size(buffer, *needed)) { + for (i=0; i<*returned; i++) { + free_devmode(printers[i].devmode); + } + SAFE_FREE(printers); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + for (i=0; i<*returned; i++) + smb_io_printer_info_2("", buffer, &(printers[i]), 0); + + /* clear memory */ + for (i=0; i<*returned; i++) { + free_devmode(printers[i].devmode); + } + SAFE_FREE(printers); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + else + return WERR_OK; +} + +/******************************************************************** + * handle enumeration of printers at level 1 + ********************************************************************/ + +static WERROR enumprinters_level1( uint32 flags, fstring name, + NEW_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 (flags & PRINTER_ENUM_REMOTE) + return enum_all_printers_info_1_remote(name, buffer, offered, needed, returned); + + if (flags & PRINTER_ENUM_NETWORK) + return enum_all_printers_info_1_network(buffer, offered, needed, returned); + + return WERR_OK; /* NT4sp5 does that */ +} + +/******************************************************************** + * handle enumeration of printers at level 2 + ********************************************************************/ + +static WERROR enumprinters_level2( uint32 flags, fstring servername, + NEW_BUFFER *buffer, uint32 offered, + uint32 *needed, uint32 *returned) +{ + char *s = servername; + + if (flags & PRINTER_ENUM_LOCAL) { + return enum_all_printers_info_2(buffer, offered, needed, returned); + } + + if (flags & PRINTER_ENUM_NAME) { + if ((servername[0] == '\\') && (servername[1] == '\\')) + s = servername + 2; + if (is_myname_or_ipaddr(s)) + 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, fstring servername, + NEW_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; + NEW_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 */ + spoolss_move_buffer(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)-1); + strupper(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(int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + PRINTER_INFO_0 *printer=NULL; + + if((printer=(PRINTER_INFO_0*)malloc(sizeof(PRINTER_INFO_0))) == NULL) + return WERR_NOMEM; + + construct_printer_info_0(printer, snum); + + /* check the required size. */ + *needed += spoolss_size_printer_info_0(printer); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_info_0("", buffer, printer, 0); + + /* clear memory */ + SAFE_FREE(printer); + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinter_level_1(int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + PRINTER_INFO_1 *printer=NULL; + + if((printer=(PRINTER_INFO_1*)malloc(sizeof(PRINTER_INFO_1))) == NULL) + return WERR_NOMEM; + + construct_printer_info_1(PRINTER_ENUM_ICON8, printer, snum); + + /* check the required size. */ + *needed += spoolss_size_printer_info_1(printer); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_info_1("", buffer, printer, 0); + + /* clear memory */ + SAFE_FREE(printer); + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinter_level_2(int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + PRINTER_INFO_2 *printer=NULL; + + if((printer=(PRINTER_INFO_2*)malloc(sizeof(PRINTER_INFO_2)))==NULL) + return WERR_NOMEM; + + construct_printer_info_2(printer, snum); + + /* check the required size. */ + *needed += spoolss_size_printer_info_2(printer); + + if (!alloc_buffer_size(buffer, *needed)) { + free_printer_info_2(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + if (!smb_io_printer_info_2("", buffer, printer, 0)) { + free_printer_info_2(printer); + return WERR_NOMEM; + } + + /* clear memory */ + free_printer_info_2(printer); + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinter_level_3(int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + PRINTER_INFO_3 *printer=NULL; + + if (!construct_printer_info_3(&printer, snum)) + return WERR_NOMEM; + + /* check the required size. */ + *needed += spoolss_size_printer_info_3(printer); + + if (!alloc_buffer_size(buffer, *needed)) { + free_printer_info_3(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_info_3("", buffer, printer, 0); + + /* clear memory */ + free_printer_info_3(printer); + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinter_level_4(int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + PRINTER_INFO_4 *printer=NULL; + + if((printer=(PRINTER_INFO_4*)malloc(sizeof(PRINTER_INFO_4)))==NULL) + return WERR_NOMEM; + + if (!construct_printer_info_4(printer, snum)) + return WERR_NOMEM; + + /* check the required size. */ + *needed += spoolss_size_printer_info_4(printer); + + if (!alloc_buffer_size(buffer, *needed)) { + free_printer_info_4(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_info_4("", buffer, printer, 0); + + /* clear memory */ + free_printer_info_4(printer); + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinter_level_5(int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + PRINTER_INFO_5 *printer=NULL; + + if((printer=(PRINTER_INFO_5*)malloc(sizeof(PRINTER_INFO_5)))==NULL) + return WERR_NOMEM; + + if (!construct_printer_info_5(printer, snum)) + return WERR_NOMEM; + + /* check the required size. */ + *needed += spoolss_size_printer_info_5(printer); + + if (!alloc_buffer_size(buffer, *needed)) { + free_printer_info_5(printer); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_info_5("", buffer, printer, 0); + + /* clear memory */ + free_printer_info_5(printer); + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +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; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + + int snum; + + /* that's an [in out] buffer */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + *needed=0; + + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + + switch (level) { + case 0: + return getprinter_level_0(snum, buffer, offered, needed); + case 1: + return getprinter_level_1(snum, buffer, offered, needed); + case 2: + return getprinter_level_2(snum, buffer, offered, needed); + case 3: + return getprinter_level_3(snum, buffer, offered, needed); + case 4: + return getprinter_level_4(snum, buffer, offered, needed); + case 5: + return getprinter_level_5(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, fstring 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, fstring 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(&printer, 2, lp_servicename(snum)))) + return WERR_INVALID_PRINTER_NAME; + + if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version))) + 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, fstring servername) +{ + pstring temp; + + 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)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath); + init_unistr( &info->driverpath, temp ); + } else + init_unistr( &info->driverpath, "" ); + + if (strlen(driver.info_3->datafile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile); + init_unistr( &info->datafile, temp ); + } else + init_unistr( &info->datafile, "" ); + + if (strlen(driver.info_3->configfile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, 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, fstring 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(&printer, 2, lp_servicename(snum)))) + return WERR_INVALID_PRINTER_NAME; + + if (!W_ERROR_IS_OK(get_a_printer_driver(&driver, 3, printer->info_2->drivername, architecture, version))) + 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 void init_unistr_array(uint16 **uni_array, fstring *char_array, char *servername) +{ + int i=0; + int j=0; + char *v; + pstring line; + uint16 *tuary; + + DEBUG(6,("init_unistr_array\n")); + *uni_array=NULL; + + while (1) { + if (char_array == NULL) + v = ""; + else { + v = char_array[i]; + if (!v) v = ""; /* hack to handle null lists */ + } + if (strlen(v) == 0) break; + slprintf(line, sizeof(line)-1, "\\\\%s%s", servername, v); + DEBUGADD(6,("%d:%s:%d\n", i, line, strlen(line))); + if((tuary=Realloc(*uni_array, (j+strlen(line)+2)*sizeof(uint16))) == NULL) { + DEBUG(2,("init_unistr_array: Realloc error\n" )); + return; + } else + *uni_array = tuary; + j += (rpcstr_push((*uni_array+j), line, sizeof(uint16)*strlen(line)+2, 0)/ sizeof(uint16)); + i++; + } + + if (*uni_array) { + (*uni_array)[j]=0x0000; + } + + DEBUGADD(6,("last one:done\n")); +} + +/******************************************************************** + * 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, fstring servername) +{ + pstring temp; + + 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)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath); + init_unistr( &info->driverpath, temp ); + } else + init_unistr( &info->driverpath, "" ); + + if (strlen(driver.info_3->datafile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile); + init_unistr( &info->datafile, temp ); + } else + init_unistr( &info->datafile, "" ); + + if (strlen(driver.info_3->configfile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile); + init_unistr( &info->configfile, temp ); + } else + init_unistr( &info->configfile, "" ); + + if (strlen(driver.info_3->helpfile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile); + init_unistr( &info->helpfile, temp ); + } else + init_unistr( &info->helpfile, "" ); + + 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); +} + +/******************************************************************** + * construct_printer_info_3 + * fill a printer_info_3 struct + ********************************************************************/ + +static WERROR construct_printer_driver_info_3(DRIVER_INFO_3 *info, int snum, fstring 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(&printer, 2, lp_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, fstring servername) +{ + pstring temp; + fstring nullstr; + + 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)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->driverpath); + init_unistr( &info->driverpath, temp ); + } else + init_unistr( &info->driverpath, "" ); + + if (strlen(driver.info_3->datafile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->datafile); + init_unistr( &info->datafile, temp ); + } else + init_unistr( &info->datafile, "" ); + + if (strlen(driver.info_3->configfile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->configfile); + init_unistr( &info->configfile, temp ); + } else + init_unistr( &info->configfile, "" ); + + if (strlen(driver.info_3->helpfile)) { + slprintf(temp, sizeof(temp)-1, "\\\\%s%s", servername, driver.info_3->helpfile); + init_unistr( &info->helpfile, temp ); + } else + init_unistr( &info->helpfile, "" ); + + 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.low=0; + info->driver_date.high=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, fstring 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(&printer, 2, lp_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); + + 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(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + DRIVER_INFO_1 *info=NULL; + WERROR status; + + if((info=(DRIVER_INFO_1 *)malloc(sizeof(DRIVER_INFO_1))) == NULL) + return WERR_NOMEM; + + status=construct_printer_driver_info_1(info, snum, servername, architecture, version); + if (!W_ERROR_IS_OK(status)) { + SAFE_FREE(info); + return status; + } + + /* check the required size. */ + *needed += spoolss_size_printer_driver_info_1(info); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(info); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_driver_info_1("", buffer, info, 0); + + /* clear memory */ + SAFE_FREE(info); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinterdriver2_level2(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + DRIVER_INFO_2 *info=NULL; + WERROR status; + + if((info=(DRIVER_INFO_2 *)malloc(sizeof(DRIVER_INFO_2))) == NULL) + return WERR_NOMEM; + + status=construct_printer_driver_info_2(info, snum, servername, architecture, version); + if (!W_ERROR_IS_OK(status)) { + SAFE_FREE(info); + return status; + } + + /* check the required size. */ + *needed += spoolss_size_printer_driver_info_2(info); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(info); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_driver_info_2("", buffer, info, 0); + + /* clear memory */ + SAFE_FREE(info); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinterdriver2_level3(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + DRIVER_INFO_3 info; + WERROR status; + + ZERO_STRUCT(info); + + status=construct_printer_driver_info_3(&info, snum, servername, architecture, version); + if (!W_ERROR_IS_OK(status)) { + return status; + } + + /* check the required size. */ + *needed += spoolss_size_printer_driver_info_3(&info); + + if (!alloc_buffer_size(buffer, *needed)) { + free_printer_driver_info_3(&info); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_driver_info_3("", buffer, &info, 0); + + free_printer_driver_info_3(&info); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getprinterdriver2_level6(fstring servername, fstring architecture, uint32 version, int snum, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + DRIVER_INFO_6 info; + WERROR status; + + ZERO_STRUCT(info); + + status=construct_printer_driver_info_6(&info, snum, servername, architecture, version); + if (!W_ERROR_IS_OK(status)) { + return status; + } + + /* check the required size. */ + *needed += spoolss_size_printer_driver_info_6(&info); + + if (!alloc_buffer_size(buffer, *needed)) { + free_printer_driver_info_6(&info); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + smb_io_printer_driver_info_6("", buffer, &info, 0); + + free_printer_driver_info_6(&info); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +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; +/* uint32 clientminorversion = q_u->clientminorversion; - notused. */ + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *servermajorversion = &r_u->servermajorversion; + uint32 *serverminorversion = &r_u->serverminorversion; + + fstring servername; + fstring architecture; + int snum; + + /* that's an [in out] buffer */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + DEBUG(4,("_spoolss_getprinterdriver2\n")); + + *needed=0; + *servermajorversion=0; + *serverminorversion=0; + + pstrcpy(servername, get_called_name()); + unistr2_to_ascii(architecture, uni_arch, sizeof(architecture)-1); + + if (!get_printer_snum(p, handle, &snum)) + 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); + } + + 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; + + 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; + } + + Printer->page_started=False; + print_job_endpage(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; +/* uint32 level = q_u->doc_info_container.level; - notused. */ + DOC_INFO *docinfo = &q_u->doc_info_container.docinfo; + uint32 *jobid = &r_u->jobid; + + DOC_INFO_1 *info_1 = &docinfo->doc_info_1; + int snum; + pstring jobname; + fstring datatype; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + struct current_user user; + + if (!Printer) { + DEBUG(2,("_spoolss_startdocprinter: Invalid handle (%s:%u:%u)\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + get_current_user(&user, p); + + /* + * 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 ... + * + * lkclXXXX jean-francois, i love this kind of thing. oh, well, + * there's a bug in NT client-side code, so we'll fix it in the + * server-side code. *nnnnnggggh!* + */ + + 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)) { + return WERR_BADFID; + } + + unistr2_to_ascii(jobname, &info_1->docname, sizeof(jobname)); + + Printer->jobid = print_job_start(&user, snum, jobname); + + /* 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; + + 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; + } + + (*buffer_written) = print_job_write(Printer->jobid, (char *)buffer, buffer_size); + + + 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) +{ + struct current_user user; + int snum; + WERROR errcode = WERR_BADFUNC; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + + get_current_user(&user, p); + + 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)) + return WERR_BADFID; + + switch (command) { + case PRINTER_CONTROL_PAUSE: + if (print_queue_pause(&user, snum, &errcode)) { + errcode = WERR_OK; + } + break; + case PRINTER_CONTROL_RESUME: + case PRINTER_CONTROL_UNPAUSE: + if (print_queue_resume(&user, snum, &errcode)) { + errcode = WERR_OK; + } + break; + case PRINTER_CONTROL_PURGE: + if (print_queue_purge(&user, snum, &errcode)) { + errcode = WERR_OK; + } + break; + default: + return WERR_UNKNOWN_LEVEL; + } + + return errcode; +} + +/******************************************************************** + * api_spoolss_abortprinter + ********************************************************************/ + +WERROR _spoolss_abortprinter(pipes_struct *p, SPOOL_Q_ABORTPRINTER *q_u, SPOOL_R_ABORTPRINTER *r_u) +{ + POLICY_HND *handle = &q_u->handle; + + return control_printer(handle, PRINTER_CONTROL_PURGE, p); +} + +/******************************************************************** + * 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; + struct current_user user; + WERROR result; + int snum; + + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + + if (!Printer || !get_printer_snum(p, handle, &snum)) { + DEBUG(2,("update_printer_sec: Invalid handle (%s:%u:%u)\n", + OUR_HANDLE(handle))); + + result = WERR_BADFID; + goto done; + } + + /* NT seems to like setting the security descriptor even though + nothing may have actually changed. This causes annoying + dialog boxes when the user doesn't have permission to change + the security descriptor. */ + + nt_printing_getsec(p->mem_ctx, Printer->dev.handlename, &old_secdesc_ctr); + + if (DEBUGLEVEL >= 10) { + SEC_ACL *the_acl; + int i; + + the_acl = old_secdesc_ctr->sec->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++) { + fstring sid_str; + + sid_to_string(sid_str, &the_acl->ace[i].trustee); + + DEBUG(10, ("%s 0x%08x\n", sid_str, + the_acl->ace[i].info.mask)); + } + + the_acl = secdesc_ctr->sec->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++) { + fstring sid_str; + + sid_to_string(sid_str, &the_acl->ace[i].trustee); + + DEBUG(10, ("%s 0x%08x\n", sid_str, + the_acl->ace[i].info.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 (sec_desc_equal(new_secdesc_ctr->sec, old_secdesc_ctr->sec)) { + result = WERR_OK; + goto done; + } + + /* Work out which user is performing the operation */ + + get_current_user(&user, p); + + /* 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 (!print_access_check(&user, snum, PRINTER_ACCESS_ADMINISTER)) { + result = WERR_ACCESS_DENIED; + goto done; + } + + result = nt_printing_setsec(Printer->dev.handlename, new_secdesc_ctr); + + done: + + return result; +} + +/******************************************************************** + Do Samba sanity checks on a printer info struct. + this has changed purpose: it now "canonicalises" printer + info from a client rather than just checking it is correct + ********************************************************************/ + +static BOOL check_printer_ok(NT_PRINTER_INFO_LEVEL_2 *info, int snum) +{ + 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", get_called_name()); + fstrcpy(info->sharename, lp_servicename(snum)); + slprintf(info->printername, sizeof(info->printername)-1, "\\\\%s\\%s", + get_called_name(), info->sharename); + info->attributes = PRINTER_ATTRIBUTE_SHARED | PRINTER_ATTRIBUTE_NETWORK; + + return True; +} + +/**************************************************************************** +****************************************************************************/ + +static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer) +{ + char *cmd = lp_addprinter_cmd(); + char **qlines; + pstring command; + pstring driverlocation; + int numlines; + int ret; + int fd; + fstring remote_machine = "%m"; + + /* build driver path... only 9X architecture is needed for legacy reasons */ + slprintf(driverlocation, sizeof(driverlocation)-1, "\\\\%s\\print$\\WIN40\\0", + get_called_name()); + /* change \ to \\ for the shell */ + all_string_sub(driverlocation,"\\","\\\\",sizeof(pstring)); + + slprintf(command, sizeof(command)-1, "%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, driverlocation, remote_machine); + + /* Convert script args to unix-codepage */ + DEBUG(10,("Running [%s]\n", command)); + ret = smbrun(command, &fd); + DEBUGADD(10,("returned [%d]\n", ret)); + + if ( ret != 0 ) { + if (fd != -1) + close(fd); + return False; + } + + numlines = 0; + /* Get lines and convert them back to dos-codepage */ + qlines = fd_lines_load(fd, &numlines); + DEBUGADD(10,("Lines returned = [%d]\n", numlines)); + close(fd); + + 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])); + + /* Send SIGHUP to process group... is there a better way? */ + kill(0, SIGHUP); + add_all_printers(); + } + + file_lines_free(qlines); + return True; +} + +#if 0 /* JERRY */ + +/* Return true if two devicemodes are equal */ + +#define DEVMODE_CHECK_INT(field) \ + if (d1->field != d2->field) { \ + DEBUG(10, ("nt_devicemode_equal(): " #field " not equal (%d != %d)\n", \ + d1->field, d2->field)); \ + return False; \ + } + +/************************************************************************ + Handy, but currently unused functions + ***********************************************************************/ + +static BOOL nt_devicemode_equal(NT_DEVICEMODE *d1, NT_DEVICEMODE *d2) +{ + if (!d1 && !d2) goto equal; /* if both are NULL they are equal */ + + if (!d1 ^ !d2) { + DEBUG(10, ("nt_devicemode_equal(): pointers not equal\n")); + return False; /* if either is exclusively NULL are not equal */ + } + + if (!strequal(d1->devicename, d2->devicename)) { + DEBUG(10, ("nt_devicemode_equal(): device not equal (%s != %s)\n", d1->devicename, d2->devicename)); + return False; + } + + if (!strequal(d1->formname, d2->formname)) { + DEBUG(10, ("nt_devicemode_equal(): formname not equal (%s != %s)\n", d1->formname, d2->formname)); + return False; + } + + DEVMODE_CHECK_INT(specversion); + DEVMODE_CHECK_INT(driverversion); + DEVMODE_CHECK_INT(driverextra); + DEVMODE_CHECK_INT(orientation); + DEVMODE_CHECK_INT(papersize); + DEVMODE_CHECK_INT(paperlength); + DEVMODE_CHECK_INT(paperwidth); + DEVMODE_CHECK_INT(scale); + DEVMODE_CHECK_INT(copies); + DEVMODE_CHECK_INT(defaultsource); + DEVMODE_CHECK_INT(printquality); + DEVMODE_CHECK_INT(color); + DEVMODE_CHECK_INT(duplex); + DEVMODE_CHECK_INT(yresolution); + DEVMODE_CHECK_INT(ttoption); + DEVMODE_CHECK_INT(collate); + DEVMODE_CHECK_INT(logpixels); + + DEVMODE_CHECK_INT(fields); + DEVMODE_CHECK_INT(bitsperpel); + DEVMODE_CHECK_INT(pelswidth); + DEVMODE_CHECK_INT(pelsheight); + DEVMODE_CHECK_INT(displayflags); + DEVMODE_CHECK_INT(displayfrequency); + DEVMODE_CHECK_INT(icmmethod); + DEVMODE_CHECK_INT(icmintent); + DEVMODE_CHECK_INT(mediatype); + DEVMODE_CHECK_INT(dithertype); + DEVMODE_CHECK_INT(reserved1); + DEVMODE_CHECK_INT(reserved2); + DEVMODE_CHECK_INT(panningwidth); + DEVMODE_CHECK_INT(panningheight); + + /* compare the private data if it exists */ + if (!d1->driverextra && !d2->driverextra) goto equal; + + + DEVMODE_CHECK_INT(driverextra); + + if (memcmp(d1->private, d2->private, d1->driverextra)) { + DEBUG(10, ("nt_devicemode_equal(): private data not equal\n")); + return False; + } + + equal: + DEBUG(10, ("nt_devicemode_equal(): devicemodes identical\n")); + return True; +} + +/* Return true if two NT_PRINTER_PARAM structures are equal */ + +static BOOL nt_printer_param_equal(NT_PRINTER_PARAM *p1, + NT_PRINTER_PARAM *p2) +{ + if (!p1 && !p2) goto equal; + + if ((!p1 && p2) || (p1 && !p2)) { + DEBUG(10, ("nt_printer_param_equal(): pointers differ\n")); + return False; + } + + /* Compare lists of printer parameters */ + + while (p1) { + BOOL found = False; + NT_PRINTER_PARAM *q = p1; + + /* Find the parameter in the second structure */ + + while(q) { + + if (strequal(p1->value, q->value)) { + + if (p1->type != q->type) { + DEBUG(10, ("nt_printer_param_equal():" + "types for %s differ (%d != %d)\n", + p1->value, p1->type, + q->type)); + break; + } + + if (p1->data_len != q->data_len) { + DEBUG(10, ("nt_printer_param_equal():" + "len for %s differs (%d != %d)\n", + p1->value, p1->data_len, + q->data_len)); + break; + } + + if (memcmp(p1->data, q->data, p1->data_len) == 0) { + found = True; + } else { + DEBUG(10, ("nt_printer_param_equal():" + "data for %s differs\n", p1->value)); + } + + break; + } + + q = q->next; + } + + if (!found) { + DEBUG(10, ("nt_printer_param_equal(): param %s " + "does not exist\n", p1->value)); + return False; + } + + p1 = p1->next; + } + + equal: + + DEBUG(10, ("nt_printer_param_equal(): printer params identical\n")); + return True; +} + +/******************************************************************** + * Called by update_printer when trying to work out whether to + * actually update printer info. + ********************************************************************/ + +#define PI_CHECK_INT(field) \ + if (pi1->field != pi2->field) { \ + DEBUG(10, ("nt_printer_info_level_equal(): " #field " not equal (%d != %d)\n", \ + pi1->field, pi2->field)); \ + return False; \ + } + +#define PI_CHECK_STR(field) \ + if (!strequal(pi1->field, pi2->field)) { \ + DEBUG(10, ("nt_printer_info_level_equal(): " #field " not equal (%s != %s)\n", \ + pi1->field, pi2->field)); \ + return False; \ + } + +static BOOL nt_printer_info_level_equal(NT_PRINTER_INFO_LEVEL *p1, + NT_PRINTER_INFO_LEVEL *p2) +{ + NT_PRINTER_INFO_LEVEL_2 *pi1, *pi2; + + /* Trivial conditions */ + + if ((!p1 && !p2) || (!p1->info_2 && !p2->info_2)) { + goto equal; + } + + if ((!p1 && p2) || (p1 && !p2) || + (!p1->info_2 && p2->info_2) || + (p1->info_2 && !p2->info_2)) { + DEBUG(10, ("nt_printer_info_level_equal(): info levels " + "differ\n")); + return False; + } + + /* Compare two nt_printer_info_level structures. Don't compare + status or cjobs as they seem to have something to do with the + printer queue. */ + + pi1 = p1->info_2; + pi2 = p2->info_2; + + /* Don't check the attributes as we stomp on the value in + check_printer_ok() anyway. */ + +#if 0 + PI_CHECK_INT(attributes); +#endif + + PI_CHECK_INT(priority); + PI_CHECK_INT(default_priority); + PI_CHECK_INT(starttime); + PI_CHECK_INT(untiltime); + PI_CHECK_INT(averageppm); + + /* Yuck - don't check the printername or servername as the + mod_a_printer() code plays games with them. You can't + change the printername or the sharename through this interface + in Samba. */ + + PI_CHECK_STR(sharename); + PI_CHECK_STR(portname); + PI_CHECK_STR(drivername); + PI_CHECK_STR(comment); + PI_CHECK_STR(location); + + if (!nt_devicemode_equal(pi1->devmode, pi2->devmode)) { + return False; + } + + PI_CHECK_STR(sepfile); + PI_CHECK_STR(printprocessor); + PI_CHECK_STR(datatype); + PI_CHECK_STR(parameters); + + if (!nt_printer_param_equal(pi1->specific, pi2->specific)) { + return False; + } + + if (!sec_desc_equal(pi1->secdesc_buf->sec, pi2->secdesc_buf->sec)) { + return False; + } + + PI_CHECK_INT(changeid); + PI_CHECK_INT(c_setprinter); + PI_CHECK_INT(setuptime); + + equal: + DEBUG(10, ("nt_printer_info_level_equal(): infos are identical\n")); + return True; +} + +#endif + +/******************************************************************** + * 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); + PRINTER_MESSAGE_INFO msg; + WERROR result; + + DEBUG(8,("update_printer\n")); + + ZERO_STRUCT(msg); + + result = WERR_OK; + + if (level!=2) { + DEBUG(0,("update_printer: Send a mail to samba@samba.org\n")); + DEBUGADD(0,("with the following message: update_printer: level!=2\n")); + result = WERR_UNKNOWN_LEVEL; + goto done; + } + + if (!Printer) { + result = WERR_BADFID; + goto done; + } + + if (!get_printer_snum(p, handle, &snum)) { + result = WERR_BADFID; + goto done; + } + + if (!W_ERROR_IS_OK(get_a_printer(&printer, 2, lp_servicename(snum))) || + (!W_ERROR_IS_OK(get_a_printer(&old_printer, 2, lp_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 (info->info_2->devmode_ptr != 0) { + /* 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; + } + +#if 0 /* JERRY */ + + /* + * Another one of those historical misunderstandings... + * This is reminisent of a similar call we had in _spoolss_setprinterdata() + * I'm leaving it here as a reminder. --jerry + */ + + if (nt_printer_info_level_equal(printer, old_printer)) { + DEBUG(3, ("update_printer: printer info has not changed\n")); + result = WERR_OK; + goto done; + } + +#endif + + /* 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 */ + + if (*lp_addprinter_cmd()) { + if ( !add_printer_hook(printer) ) { + result = WERR_ACCESS_DENIED; + goto done; + } + } + + /* + * Set the DRIVER_INIT info in the tdb; trigger on magic value for the + * DEVMODE.displayfrequency, which is not used for printer drivers. This + * requires Win32 client code (see other notes elsewhere in the code). + */ + if (printer->info_2->devmode && + printer->info_2->devmode->displayfrequency == MAGIC_DISPLAY_FREQUENCY) { + + DEBUG(10,("update_printer: Save printer driver init data\n")); + printer->info_2->devmode->displayfrequency = 0; + + if (update_driver_init(*printer, 2)!=0) { + DEBUG(10,("update_printer: error updating printer driver init DEVMODE\n")); + result = WERR_ACCESS_DENIED; + goto done; + } + } else { + /* + * 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)){ + set_driver_init(printer, 2); + msg.flags |= PRINTER_MESSAGE_DRIVER; + } + } + + /* Update printer info */ + result = mod_a_printer(*printer, 2); + + /* flag which changes actually occured. This is a small subset of + all the possible changes */ + + if (!strequal(printer->info_2->comment, old_printer->info_2->comment)) + msg.flags |= PRINTER_MESSAGE_COMMENT; + + if (!strequal(printer->info_2->sharename, old_printer->info_2->sharename)) + msg.flags |= PRINTER_MESSAGE_SHARENAME; + + if (!strequal(printer->info_2->portname, old_printer->info_2->portname)) + msg.flags |= PRINTER_MESSAGE_PORT; + + if (!strequal(printer->info_2->location, old_printer->info_2->location)) + msg.flags |= PRINTER_MESSAGE_LOCATION; + + ZERO_STRUCT(msg); + + msg.low = PRINTER_CHANGE_ADD_PRINTER; + fstrcpy(msg.printer_name, printer->info_2->printername); + + /* only send a notify if something changed */ + if (msg.flags) { + srv_spoolss_sendnotify(msg.printer_name, 0, PRINTER_CHANGE_ADD_PRINTER, msg.flags); + } + + done: + free_a_printer(&printer, 2); + free_a_printer(&old_printer, 2); + + + return result; +} + +/**************************************************************************** +****************************************************************************/ + +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; + + 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: + return update_printer(p, handle, level, info, devmode_ctr.devmode); + case 3: + return update_printer_sec(handle, level, info, p, + secdesc_ctr); + 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) + srv_spoolss_replycloseprinter(&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 (despite appearences to the contrary) */ + spoolss_move_buffer(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, print_queue_struct *queue, + int position, int snum) +{ + pstring temp_name; + + struct tm *t; + + t=gmtime(&queue->time); + slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name()); + + job_info->jobid=queue->job; + init_unistr(&job_info->printername, lp_servicename(snum)); + init_unistr(&job_info->machinename, temp_name); + 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, print_queue_struct *queue, + int position, int snum, + NT_PRINTER_INFO_LEVEL *ntprinter, + DEVICEMODE *devmode) +{ + pstring temp_name; + pstring chaine; + struct tm *t; + + t=gmtime(&queue->time); + slprintf(temp_name, sizeof(temp_name)-1, "\\\\%s", get_called_name()); + + job_info->jobid=queue->job; + + slprintf(chaine, sizeof(chaine)-1, "\\\\%s\\%s", get_called_name(), ntprinter->info_2->printername); + + init_unistr(&job_info->printername, chaine); + + init_unistr(&job_info->machinename, temp_name); + 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(print_queue_struct *queue, int snum, + NEW_BUFFER *buffer, uint32 offered, + uint32 *needed, uint32 *returned) +{ + JOB_INFO_1 *info; + int i; + + info=(JOB_INFO_1 *)malloc(*returned*sizeof(JOB_INFO_1)); + if (info==NULL) { + SAFE_FREE(queue); + *returned=0; + return WERR_NOMEM; + } + + for (i=0; i<*returned; i++) + fill_job_info_1(&info[i], &queue[i], i, snum); + + SAFE_FREE(queue); + + /* check the required size. */ + for (i=0; i<*returned; i++) + (*needed) += spoolss_size_job_info_1(&info[i]); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(info); + return WERR_INSUFFICIENT_BUFFER; + } + + /* fill the buffer with the structures */ + for (i=0; i<*returned; i++) + smb_io_job_info_1("", buffer, &info[i], 0); + + /* clear memory */ + SAFE_FREE(info); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + Enumjobs at level 2. +****************************************************************************/ + +static WERROR enumjobs_level2(print_queue_struct *queue, int snum, + NEW_BUFFER *buffer, uint32 offered, + uint32 *needed, uint32 *returned) +{ + NT_PRINTER_INFO_LEVEL *ntprinter = NULL; + JOB_INFO_2 *info = NULL; + int i; + WERROR result; + DEVICEMODE *devmode = NULL; + + info=(JOB_INFO_2 *)malloc(*returned*sizeof(JOB_INFO_2)); + if (info==NULL) { + *returned=0; + result = WERR_NOMEM; + goto done; + } + + result = get_a_printer(&ntprinter, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(result)) { + *returned = 0; + goto done; + } + + if (!(devmode = construct_dev_mode(snum))) { + *returned = 0; + result = WERR_NOMEM; + goto done; + } + + for (i=0; i<*returned; i++) + fill_job_info_2(&(info[i]), &queue[i], i, snum, ntprinter, + devmode); + + free_a_printer(&ntprinter, 2); + SAFE_FREE(queue); + + /* check the required size. */ + for (i=0; i<*returned; i++) + (*needed) += spoolss_size_job_info_2(&info[i]); + + if (*needed > offered) { + *returned=0; + result = WERR_INSUFFICIENT_BUFFER; + goto done; + } + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(info); + result = WERR_INSUFFICIENT_BUFFER; + goto done; + } + + /* fill the buffer with the structures */ + for (i=0; i<*returned; i++) + smb_io_job_info_2("", buffer, &info[i], 0); + + result = WERR_OK; + + done: + free_a_printer(&ntprinter, 2); + free_devmode(devmode); + SAFE_FREE(queue); + SAFE_FREE(info); + + 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 firstjob = q_u->firstjob; - notused. */ +/* uint32 numofjobs = q_u->numofjobs; - notused. */ + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *returned = &r_u->returned; + + int snum; + print_status_struct prt_status; + print_queue_struct *queue=NULL; + + /* that's an [in out] buffer */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + DEBUG(4,("_spoolss_enumjobs\n")); + + *needed=0; + *returned=0; + + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + + *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); + return WERR_OK; + } + + switch (level) { + case 1: + return enumjobs_level1(queue, snum, buffer, offered, needed, returned); + case 2: + return enumjobs_level2(queue, snum, buffer, offered, needed, returned); + default: + SAFE_FREE(queue); + *returned=0; + return WERR_UNKNOWN_LEVEL; + } +} + +/**************************************************************************** +****************************************************************************/ + +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 level = q_u->level; - notused. */ +/* JOB_INFO *ctr = &q_u->ctr; - notused. */ + uint32 command = q_u->command; + + struct current_user user; + int snum; + WERROR errcode = WERR_BADFUNC; + + if (!get_printer_snum(p, handle, &snum)) { + return WERR_BADFID; + } + + if (!print_job_exists(jobid)) { + return WERR_INVALID_PRINTER_NAME; + } + + get_current_user(&user, p); + + switch (command) { + case JOB_CONTROL_CANCEL: + case JOB_CONTROL_DELETE: + if (print_job_delete(&user, jobid, &errcode)) { + errcode = WERR_OK; + } + break; + case JOB_CONTROL_PAUSE: + if (print_job_pause(&user, jobid, &errcode)) { + errcode = WERR_OK; + } + break; + case JOB_CONTROL_RESTART: + case JOB_CONTROL_RESUME: + if (print_job_resume(&user, jobid, &errcode)) { + errcode = WERR_OK; + } + break; + default: + return WERR_UNKNOWN_LEVEL; + } + + return errcode; +} + +/**************************************************************************** + Enumerates all printer drivers at level 1. +****************************************************************************/ + +static WERROR enumprinterdrivers_level1(fstring servername, fstring architecture, NEW_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 *tdi1, *driver_info_1=NULL; + + *returned=0; + +#define MAX_VERSION 4 + + for (version=0; version<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) + return WERR_NOMEM; + + if(ndrivers != 0) { + if((tdi1=(DRIVER_INFO_1 *)Realloc(driver_info_1, (*returned+ndrivers) * sizeof(DRIVER_INFO_1))) == NULL) { + DEBUG(0,("enumprinterdrivers_level1: failed to enlarge driver info buffer!\n")); + SAFE_FREE(driver_info_1); + SAFE_FREE(list); + return WERR_NOMEM; + } + else driver_info_1 = tdi1; + } + + 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); + 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 (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(driver_info_1); + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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); + } + + SAFE_FREE(driver_info_1); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + Enumerates all printer drivers at level 2. +****************************************************************************/ + +static WERROR enumprinterdrivers_level2(fstring servername, fstring architecture, NEW_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 *tdi2, *driver_info_2=NULL; + + *returned=0; + +#define MAX_VERSION 4 + + for (version=0; version<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) + return WERR_NOMEM; + + if(ndrivers != 0) { + if((tdi2=(DRIVER_INFO_2 *)Realloc(driver_info_2, (*returned+ndrivers) * sizeof(DRIVER_INFO_2))) == NULL) { + DEBUG(0,("enumprinterdrivers_level2: failed to enlarge driver info buffer!\n")); + SAFE_FREE(driver_info_2); + SAFE_FREE(list); + return WERR_NOMEM; + } + else driver_info_2 = tdi2; + } + + 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); + 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 (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(driver_info_2); + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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); + } + + SAFE_FREE(driver_info_2); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + Enumerates all printer drivers at level 3. +****************************************************************************/ + +static WERROR enumprinterdrivers_level3(fstring servername, fstring architecture, NEW_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_3 *tdi3, *driver_info_3=NULL; + + *returned=0; + +#define MAX_VERSION 4 + + for (version=0; version<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) + return WERR_NOMEM; + + if(ndrivers != 0) { + if((tdi3=(DRIVER_INFO_3 *)Realloc(driver_info_3, (*returned+ndrivers) * sizeof(DRIVER_INFO_3))) == NULL) { + DEBUG(0,("enumprinterdrivers_level3: failed to enlarge driver info buffer!\n")); + SAFE_FREE(driver_info_3); + SAFE_FREE(list); + return WERR_NOMEM; + } + else driver_info_3 = tdi3; + } + + 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); + 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 (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(driver_info_3); + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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); + } + + for (i=0; i<*returned; i++) + SAFE_FREE(driver_info_3[i].dependentfiles); + + SAFE_FREE(driver_info_3); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + Enumerates all printer drivers. +****************************************************************************/ + +WERROR _spoolss_enumprinterdrivers( pipes_struct *p, SPOOL_Q_ENUMPRINTERDRIVERS *q_u, SPOOL_R_ENUMPRINTERDRIVERS *r_u) +{ +/* UNISTR2 *name = &q_u->name; - notused. */ + UNISTR2 *environment = &q_u->environment; + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *returned = &r_u->returned; + + fstring *list = NULL; + fstring servername; + fstring architecture; + + /* that's an [in out] buffer */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + DEBUG(4,("_spoolss_enumprinterdrivers\n")); + fstrcpy(servername, get_called_name()); + *needed=0; + *returned=0; + + unistr2_to_ascii(architecture, environment, sizeof(architecture)-1); + + switch (level) { + case 1: + return enumprinterdrivers_level1(servername, architecture, buffer, offered, needed, returned); + case 2: + return enumprinterdrivers_level2(servername, architecture, buffer, offered, needed, returned); + case 3: + return enumprinterdrivers_level3(servername, architecture, buffer, offered, needed, returned); + default: + *returned=0; + SAFE_FREE(list); + 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) +{ +/* POLICY_HND *handle = &q_u->handle; - notused. */ + uint32 level = q_u->level; + NEW_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 */ + spoolss_move_buffer(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) return WERR_NO_MORE_ITEMS; + + switch (level) { + case 1: + if ((forms_1=(FORM_1 *)malloc(*numofforms * sizeof(FORM_1))) == NULL) { + *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 (!alloc_buffer_size(buffer, buffer_size)){ + SAFE_FREE(forms_1); + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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); + + if (*needed > offered) { + *numofforms=0; + return WERR_INSUFFICIENT_BUFFER; + } + else + 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) +{ +/* POLICY_HND *handle = &q_u->handle; - notused. */ + uint32 level = q_u->level; + UNISTR2 *uni_formname = &q_u->formname; + NEW_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 */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + unistr2_to_ascii(form_name, uni_formname, sizeof(form_name)-1); + + 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 (!alloc_buffer_size(buffer, buffer_size)){ + return WERR_INSUFFICIENT_BUFFER; + } + + if (*needed > offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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, char *name) +{ + init_unistr(&port->port_name, name); +} + +/**************************************************************************** +****************************************************************************/ + +static void fill_port_2(PORT_INFO_2 *port, char *name) +{ + init_unistr(&port->port_name, name); + init_unistr(&port->monitor_name, "Local Monitor"); + init_unistr(&port->description, "Local Port"); +#define PORT_TYPE_WRITE 1 + port->port_type=PORT_TYPE_WRITE; + port->reserved=0x0; +} + +/**************************************************************************** + enumports level 1. +****************************************************************************/ + +static WERROR enumports_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + PORT_INFO_1 *ports=NULL; + int i=0; + + if (*lp_enumports_cmd()) { + char *cmd = lp_enumports_cmd(); + char **qlines; + pstring command; + int numlines; + int ret; + int fd; + + slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 1); + + DEBUG(10,("Running [%s]\n", command)); + ret = smbrun(command, &fd); + DEBUG(10,("Returned [%d]\n", ret)); + if (ret != 0) { + if (fd != -1) + close(fd); + /* Is this the best error to return here? */ + return WERR_ACCESS_DENIED; + } + + numlines = 0; + qlines = fd_lines_load(fd, &numlines); + DEBUGADD(10,("Lines returned = [%d]\n", numlines)); + close(fd); + + if(numlines) { + if((ports=(PORT_INFO_1 *)malloc( numlines * sizeof(PORT_INFO_1) )) == 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; + + } else { + *returned = 1; /* Sole Samba port returned. */ + + if((ports=(PORT_INFO_1 *)malloc( sizeof(PORT_INFO_1) )) == NULL) + return WERR_NOMEM; + + DEBUG(10,("enumports_level_1: port name %s\n", SAMBA_PRINTER_PORT_NAME)); + + fill_port_1(&ports[0], SAMBA_PRINTER_PORT_NAME); + } + + /* 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 (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(ports); + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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); + } + + SAFE_FREE(ports); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + enumports level 2. +****************************************************************************/ + +static WERROR enumports_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + PORT_INFO_2 *ports=NULL; + int i=0; + + if (*lp_enumports_cmd()) { + char *cmd = lp_enumports_cmd(); + char *path; + char **qlines; + pstring tmp_file; + pstring command; + int numlines; + int ret; + int fd; + + if (*lp_pathname(lp_servicenumber(PRINTERS_NAME))) + path = lp_pathname(lp_servicenumber(PRINTERS_NAME)); + else + path = lp_lockdir(); + + slprintf(tmp_file, sizeof(tmp_file)-1, "%s/smbcmd.%u.", path, (unsigned int)sys_getpid()); + slprintf(command, sizeof(command)-1, "%s \"%d\"", cmd, 2); + + unlink(tmp_file); + DEBUG(10,("Running [%s > %s]\n", command,tmp_file)); + ret = smbrun(command, &fd); + DEBUGADD(10,("returned [%d]\n", ret)); + if (ret != 0) { + if (fd != -1) + close(fd); + /* Is this the best error to return here? */ + return WERR_ACCESS_DENIED; + } + + numlines = 0; + qlines = fd_lines_load(fd, &numlines); + DEBUGADD(10,("Lines returned = [%d]\n", numlines)); + close(fd); + + if(numlines) { + if((ports=(PORT_INFO_2 *)malloc( numlines * sizeof(PORT_INFO_2) )) == 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; + + } else { + + *returned = 1; + + if((ports=(PORT_INFO_2 *)malloc( sizeof(PORT_INFO_2) )) == NULL) + return WERR_NOMEM; + + DEBUG(10,("enumports_level_2: port name %s\n", SAMBA_PRINTER_PORT_NAME)); + + fill_port_2(&ports[0], SAMBA_PRINTER_PORT_NAME); + } + + /* 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 (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(ports); + return WERR_INSUFFICIENT_BUFFER; + } + + /* 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); + } + + SAFE_FREE(ports); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + enumports. +****************************************************************************/ + +WERROR _spoolss_enumports( pipes_struct *p, SPOOL_Q_ENUMPORTS *q_u, SPOOL_R_ENUMPORTS *r_u) +{ +/* UNISTR2 *name = &q_u->name; - notused. */ + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *returned = &r_u->returned; + + /* that's an [in out] buffer */ + spoolss_move_buffer(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 = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) { + DEBUG(0,("spoolss_addprinterex_level_2: malloc fail.\n")); + return WERR_NOMEM; + } + + ZERO_STRUCTP(printer); + + /* 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: 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; + } + + if (*lp_addprinter_cmd() ) + if ( !add_printer_hook(printer) ) { + free_a_printer(&printer,2); + return WERR_ACCESS_DENIED; + } + + slprintf(name, sizeof(name)-1, "\\\\%s\\%s", get_called_name(), + 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; + } + + set_driver_init(printer, 2); + + /* 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); + + srv_spoolss_sendnotify(printer->info_2->printername, 0, PRINTER_CHANGE_ADD_PRINTER, 0x0); + + 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) +{ +/* UNISTR2 *server_name = &q_u->server_name; - notused. */ + uint32 level = q_u->level; + SPOOL_PRINTER_DRIVER_INFO_LEVEL *info = &q_u->info; + WERROR err = WERR_OK; + NT_PRINTER_DRIVER_INFO_LEVEL driver; + struct current_user user; + + ZERO_STRUCT(driver); + + get_current_user(&user, p); + + 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, &user); + if (!W_ERROR_IS_OK(err)) + goto done; + + DEBUG(5,("Moving driver to final destination\n")); + if(!move_driver_to_download_area(driver, level, &user, &err)) { + if (W_ERROR_IS_OK(err)) + err = WERR_ACCESS_DENIED; + goto done; + } + + if (add_a_printer_driver(driver, level)!=0) { + err = WERR_ACCESS_DENIED; + goto done; + } + + done: + free_a_printer_driver(driver, level); + return err; +} + +/**************************************************************************** +****************************************************************************/ + +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, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + pstring path; + pstring long_archi; + pstring short_archi; + DRIVER_DIRECTORY_1 *info=NULL; + + unistr2_to_ascii(long_archi, uni_environment, sizeof(long_archi)-1); + + if (get_short_archi(short_archi, long_archi)==False) + return WERR_INVALID_ENVIRONMENT; + + if((info=(DRIVER_DIRECTORY_1 *)malloc(sizeof(DRIVER_DIRECTORY_1))) == NULL) + return WERR_NOMEM; + + slprintf(path, sizeof(path)-1, "\\\\%s\\print$\\%s", get_called_name(), short_archi); + + DEBUG(4,("printer driver directory: [%s]\n", path)); + + fill_driverdir_1(info, path); + + *needed += spoolss_size_driverdir_info_1(info); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(info); + return WERR_INSUFFICIENT_BUFFER; + } + + smb_io_driverdir_1("", buffer, info, 0); + + SAFE_FREE(info); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +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; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + + /* that's an [in out] buffer */ + spoolss_move_buffer(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; + + fstring value; + + uint32 param_index; + uint32 biggest_valuesize; + uint32 biggest_datasize; + uint32 data_len; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + int snum; + uint8 *data=NULL; + uint32 type; + WERROR result; + + ZERO_STRUCT(printer); + + *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)) + return WERR_BADFID; + + result = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(result)) + return result; + + /* + * The NT machine wants to know the biggest size of value and data + * + * cf: MSDN EnumPrinterData remark section + */ + if ( (in_value_len==0) && (in_data_len==0) ) { + DEBUGADD(6,("Activating NT mega-hack to find sizes\n")); + +#if 0 + /* + * NT can ask for a specific parameter size - we need to return NO_MORE_ITEMS + * if this parameter size doesn't exist. + * Ok - my opinion here is that the client is not asking for the greatest + * possible size of all the parameters, but is asking specifically for the size needed + * for this specific parameter. In that case we can remove the loop below and + * simplify this lookup code considerably. JF - comments welcome. JRA. + */ + + if (!get_specific_param_by_index(*printer, 2, idx, value, &data, &type, &data_len)) { + SAFE_FREE(data); + free_a_printer(&printer, 2); + return WERR_NO_MORE_ITEMS; + } +#endif + + SAFE_FREE(data); + + param_index=0; + biggest_valuesize=0; + biggest_datasize=0; + + while (get_specific_param_by_index(*printer, 2, param_index, value, &data, &type, &data_len)) { + if (strlen(value) > biggest_valuesize) biggest_valuesize=strlen(value); + if (data_len > biggest_datasize) biggest_datasize=data_len; + + DEBUG(6,("current values: [%d], [%d]\n", biggest_valuesize, biggest_datasize)); + + SAFE_FREE(data); + param_index++; + } + + /* the value is an UNICODE string but realvaluesize is the length in bytes including the leading 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)); + + free_a_printer(&printer, 2); + return WERR_OK; + } + + /* + * the value len is wrong in NT sp3 + * that's the number of bytes not the number of unicode chars + */ + + if (!get_specific_param_by_index(*printer, 2, idx, value, &data, &type, &data_len)) { + + SAFE_FREE(data); + free_a_printer(&printer, 2); + + /* out_value should default to "" or else NT4 has + problems unmarshalling the response */ + + *out_max_value_len=(in_value_len/sizeof(uint16)); + if((*out_value=(uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL) + return WERR_NOMEM; + + *out_value_len = rpcstr_push((char *)*out_value, "", in_value_len, 0); + + /* the data is counted in bytes */ + *out_max_data_len = in_data_len; + *out_data_len = in_data_len; + if((*data_out=(uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) + return WERR_NOMEM; + + return WERR_NO_MORE_ITEMS; + } + + free_a_printer(&printer, 2); + + /* + * 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 + */ + + *out_max_value_len=(in_value_len/sizeof(uint16)); + if((*out_value=(uint16 *)talloc_zero(p->mem_ctx,in_value_len*sizeof(uint8))) == NULL) { + SAFE_FREE(data); + return WERR_NOMEM; + } + + *out_value_len = rpcstr_push((char *)*out_value,value, in_value_len, 0); + + *out_type=type; + + /* the data is counted in bytes */ + *out_max_data_len=in_data_len; + if((*data_out=(uint8 *)talloc_zero(p->mem_ctx, in_data_len*sizeof(uint8))) == NULL) { + SAFE_FREE(data); + return WERR_NOMEM; + } + + memcpy(*data_out, data, (size_t)data_len); + *out_data_len=data_len; + + SAFE_FREE(data); + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +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; +/* uint32 max_len = q_u->max_len; - notused. */ + uint8 *data = q_u->data; + uint32 real_len = q_u->real_len; +/* uint32 numeric_data = q_u->numeric_data; - notused. */ + + NT_PRINTER_INFO_LEVEL *printer = NULL; + NT_PRINTER_PARAM *param = NULL, old_param; + int snum=0; + WERROR status = WERR_OK; + Printer_entry *Printer=find_printer_index_by_hnd(p, handle); + + DEBUG(5,("spoolss_setprinterdata\n")); + + if (!Printer) { + DEBUG(2,("_spoolss_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + if (!get_printer_snum(p,handle, &snum)) + return WERR_BADFID; + + ZERO_STRUCT(old_param); + + /* + * 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; + } + + /* Check if we are making any changes or not. Return true if + nothing is actually changing. This is not needed anymore but + has been left in as an optimization to keep from from + writing to disk as often --jerry */ + + status = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(status)) + return status; + + convert_specific_param(¶m, value , type, data, real_len); + + unlink_specific_param_if_exist(printer->info_2, param); + + /* + * 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 (param->type==3 && !strcmp( param->value, PHANTOM_DEVMODE_KEY)) { + /* + * Set devmode and printer initialization info + */ + status = save_driver_init(printer, 2, param); + } + else { + add_a_specific_param(printer->info_2, ¶m); + status = mod_a_printer(*printer, 2); + } + + done: + free_a_printer(&printer, 2); + if (param) + free_nt_printer_param(¶m); + SAFE_FREE(old_param.data); + + 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)) + 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; + NT_PRINTER_PARAM param; + int snum=0; + WERROR status = WERR_OK; + Printer_entry *Printer=find_printer_index_by_hnd(p, handle); + + 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)) + 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, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(status)) + return status; + + ZERO_STRUCTP(¶m); + unistr2_to_ascii(param.value, value, sizeof(param.value)-1); + + if(!unlink_specific_param_if_exist(printer->info_2, ¶m)) + status = WERR_INVALID_PARAM; + else + status = mod_a_printer(*printer, 2); + + free_a_printer(&printer, 2); + return status; +} + +/**************************************************************************** +****************************************************************************/ + +WERROR _spoolss_addform( pipes_struct *p, SPOOL_Q_ADDFORM *q_u, SPOOL_R_ADDFORM *r_u) +{ + POLICY_HND *handle = &q_u->handle; +/* uint32 level = q_u->level; - notused. */ + 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; + } + + if (!get_printer_snum(p,handle, &snum)) + return WERR_BADFID; + + if (Printer->access_granted != PRINTER_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)) { + return WERR_ALREADY_EXISTS; + } + + count=get_ntforms(&list); + if(!add_a_form(&list, form, &count)) + return WERR_NOMEM; + write_ntforms(&list, count); + + /* + * ChangeID must always be set + */ + + status = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(status)) + goto done; + + status = mod_a_printer(*printer, 2); + if (!W_ERROR_IS_OK(status)) + goto done; + +done: + 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; + WERROR ret = WERR_OK; + 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; + } + + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + + if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) { + DEBUG(2,("_spoolss_deleteform: denied by handle permissions\n")); + return WERR_ACCESS_DENIED; + } + + /* can't delete if builtin */ + if (get_a_builtin_ntform(form_name,&tmpForm)) { + return WERR_INVALID_PARAM; + } + + count = get_ntforms(&list); + if(!delete_a_form(&list, form_name, &count, &ret)) + return WERR_INVALID_PARAM; + + /* + * ChangeID must always be set + */ + + status = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(status)) + goto done; + + status = mod_a_printer(*printer, 2); + if (!W_ERROR_IS_OK(status)) + goto done; + +done: + free_a_printer(&printer, 2); + SAFE_FREE(list); + + return ret; +} + +/**************************************************************************** +****************************************************************************/ + +WERROR _spoolss_setform(pipes_struct *p, SPOOL_Q_SETFORM *q_u, SPOOL_R_SETFORM *r_u) +{ + POLICY_HND *handle = &q_u->handle; +/* UNISTR2 *uni_name = &q_u->name; - notused. */ +/* uint32 level = q_u->level; - notused. */ + 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; + } + + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + + if (Printer->access_granted != PRINTER_ACCESS_ADMINISTER) { + DEBUG(2,("_spoolss_setform: denied by handle permissions\n")); + return WERR_ACCESS_DENIED; + } + + /* can't set if builtin */ + if (get_a_builtin_ntform(&form->name,&tmpForm)) { + return WERR_INVALID_PARAM; + } + + count=get_ntforms(&list); + update_a_form(&list, form, count); + write_ntforms(&list, count); + + /* + * ChangeID must always be set + */ + + status = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(status)) + goto done; + + status = mod_a_printer(*printer, 2); + if (!W_ERROR_IS_OK(status)) + goto done; + +done: + free_a_printer(&printer, 2); + SAFE_FREE(list); + + return WERR_OK; +} + +/**************************************************************************** + enumprintprocessors level 1. +****************************************************************************/ + +static WERROR enumprintprocessors_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + PRINTPROCESSOR_1 *info_1=NULL; + + if((info_1 = (PRINTPROCESSOR_1 *)malloc(sizeof(PRINTPROCESSOR_1))) == NULL) + return WERR_NOMEM; + + (*returned) = 0x1; + + init_unistr(&info_1->name, "winprint"); + + *needed += spoolss_size_printprocessor_info_1(info_1); + + if (!alloc_buffer_size(buffer, *needed)) + return WERR_INSUFFICIENT_BUFFER; + + smb_io_printprocessor_info_1("", buffer, info_1, 0); + + SAFE_FREE(info_1); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +WERROR _spoolss_enumprintprocessors(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, SPOOL_R_ENUMPRINTPROCESSORS *r_u) +{ +/* UNISTR2 *name = &q_u->name; - notused. */ +/* UNISTR2 *environment = &q_u->environment; - notused. */ + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *returned = &r_u->returned; + + /* that's an [in out] buffer */ + spoolss_move_buffer(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(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + PRINTPROCDATATYPE_1 *info_1=NULL; + + if((info_1 = (PRINTPROCDATATYPE_1 *)malloc(sizeof(PRINTPROCDATATYPE_1))) == NULL) + return WERR_NOMEM; + + (*returned) = 0x1; + + init_unistr(&info_1->name, "RAW"); + + *needed += spoolss_size_printprocdatatype_info_1(info_1); + + if (!alloc_buffer_size(buffer, *needed)) + return WERR_INSUFFICIENT_BUFFER; + + smb_io_printprocdatatype_info_1("", buffer, info_1, 0); + + SAFE_FREE(info_1); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +WERROR _spoolss_enumprintprocdatatypes(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u) +{ +/* UNISTR2 *name = &q_u->name; - notused. */ +/* UNISTR2 *processor = &q_u->processor; - notused. */ + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *returned = &r_u->returned; + + /* that's an [in out] buffer */ + spoolss_move_buffer(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(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + PRINTMONITOR_1 *info_1=NULL; + + if((info_1 = (PRINTMONITOR_1 *)malloc(sizeof(PRINTMONITOR_1))) == NULL) + return WERR_NOMEM; + + (*returned) = 0x1; + + init_unistr(&info_1->name, "Local Port"); + + *needed += spoolss_size_printmonitor_info_1(info_1); + + if (!alloc_buffer_size(buffer, *needed)) + return WERR_INSUFFICIENT_BUFFER; + + smb_io_printmonitor_info_1("", buffer, info_1, 0); + + SAFE_FREE(info_1); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** + enumprintmonitors level 2. +****************************************************************************/ + +static WERROR enumprintmonitors_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned) +{ + PRINTMONITOR_2 *info_2=NULL; + + if((info_2 = (PRINTMONITOR_2 *)malloc(sizeof(PRINTMONITOR_2))) == NULL) + return WERR_NOMEM; + + (*returned) = 0x1; + + init_unistr(&info_2->name, "Local Port"); + init_unistr(&info_2->environment, "Windows NT X86"); + init_unistr(&info_2->dll_name, "localmon.dll"); + + *needed += spoolss_size_printmonitor_info_2(info_2); + + if (!alloc_buffer_size(buffer, *needed)) + return WERR_INSUFFICIENT_BUFFER; + + smb_io_printmonitor_info_2("", buffer, info_2, 0); + + SAFE_FREE(info_2); + + if (*needed > offered) { + *returned=0; + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +WERROR _spoolss_enumprintmonitors(pipes_struct *p, SPOOL_Q_ENUMPRINTMONITORS *q_u, SPOOL_R_ENUMPRINTMONITORS *r_u) +{ +/* UNISTR2 *name = &q_u->name; - notused. */ + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + uint32 *returned = &r_u->returned; + + /* that's an [in out] buffer */ + spoolss_move_buffer(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, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + int i=0; + BOOL found=False; + JOB_INFO_1 *info_1=NULL; + + info_1=(JOB_INFO_1 *)malloc(sizeof(JOB_INFO_1)); + + if (info_1 == NULL) { + SAFE_FREE(queue); + return WERR_NOMEM; + } + + for (i=0; i<count && found==False; i++) { + if (queue[i].job==(int)jobid) + found=True; + } + + if (found==False) { + SAFE_FREE(queue); + 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); + + SAFE_FREE(queue); + + *needed += spoolss_size_job_info_1(info_1); + + if (!alloc_buffer_size(buffer, *needed)) { + SAFE_FREE(info_1); + return WERR_INSUFFICIENT_BUFFER; + } + + smb_io_job_info_1("", buffer, info_1, 0); + + SAFE_FREE(info_1); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/**************************************************************************** +****************************************************************************/ + +static WERROR getjob_level_2(print_queue_struct *queue, int count, int snum, uint32 jobid, NEW_BUFFER *buffer, uint32 offered, uint32 *needed) +{ + int i=0; + BOOL found=False; + JOB_INFO_2 *info_2; + NT_PRINTER_INFO_LEVEL *ntprinter = NULL; + WERROR ret; + DEVICEMODE *devmode = NULL; + + info_2=(JOB_INFO_2 *)malloc(sizeof(JOB_INFO_2)); + + ZERO_STRUCTP(info_2); + + if (info_2 == NULL) { + ret = WERR_NOMEM; + goto done; + } + + for (i=0; i<count && found==False; i++) { + if (queue[i].job==(int)jobid) + found=True; + } + + if (found==False) { + /* NT treats not found as bad param... yet another bad + choice */ + ret = WERR_INVALID_PARAM; + goto done; + } + + ret = get_a_printer(&ntprinter, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(ret)) + goto done; + if (construct_dev_mode(snum) == NULL) { + ret = WERR_NOMEM; + goto done; + } + + fill_job_info_2(info_2, &(queue[i-1]), i, snum, ntprinter, devmode); + + *needed += spoolss_size_job_info_2(info_2); + + if (!alloc_buffer_size(buffer, *needed)) { + ret = WERR_INSUFFICIENT_BUFFER; + goto done; + } + + smb_io_job_info_2("", buffer, info_2, 0); + + if (*needed > offered) { + ret = WERR_INSUFFICIENT_BUFFER; + goto done; + } + + ret = WERR_OK; + + done: + /* Cleanup allocated memory */ + + SAFE_FREE(queue); + free_job_info_2(info_2); /* Also frees devmode */ + SAFE_FREE(info_2); + free_a_printer(&ntprinter, 2); + + return ret; +} + +/**************************************************************************** +****************************************************************************/ + +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; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + + int snum; + int count; + print_queue_struct *queue=NULL; + print_status_struct prt_status; + + /* that's an [in out] buffer */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + DEBUG(5,("spoolss_getjob\n")); + + *needed=0; + + if (!get_printer_snum(p, handle, &snum)) + return WERR_BADFID; + + 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: + return getjob_level_1(queue, count, snum, jobid, buffer, offered, needed); + case 2: + return getjob_level_2(queue, count, snum, jobid, buffer, offered, needed); + default: + SAFE_FREE(queue); + return WERR_UNKNOWN_LEVEL; + } +} + +/******************************************************************** + * spoolss_getprinterdataex + ********************************************************************/ + +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 key, value; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + BOOL found = False; + + DEBUG(4,("_spoolss_getprinterdataex\n")); + + unistr2_to_ascii(key, &q_u->keyname, sizeof(key) - 1); + unistr2_to_ascii(value, &q_u->valuename, sizeof(value) - 1); + + /* in case of problem, return some default values */ + *needed=0; + *type=0; + *out_size=0; + + + if (!Printer) { + if((*data=(uint8 *)talloc_zero(p->mem_ctx, 4*sizeof(uint8))) == NULL) + return WERR_NOMEM; + DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + + /* Is the handle to a printer or to the server? */ + + if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER) + { + DEBUG(10,("_spoolss_getprinterdatex: Not implemented for server handles yet\n")); + return WERR_INVALID_PARAM; + } + else + { + /* + * From MSDN documentation of GetPrinterDataEx: pass request + * to GetPrinterData if key is "PrinterDriverData". This is + * the only key we really support. Other keys to implement: + * (a) DsDriver + * (b) DsSpooler + * (c) PnPData + */ + + if (strcmp(key, "PrinterDriverData") != 0) + return WERR_BADFILE; + + DEBUG(10, ("_spoolss_getprinterdataex: pass me to getprinterdata\n")); + found = getprinterdata_printer(p, p->mem_ctx, handle, value, + type, data, needed, in_size); + + } + + if (!found) { + DEBUG(5, ("value not found, 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) + return WERR_NOMEM; + } else { + *data = NULL; + } + + return WERR_INVALID_PARAM; + } + + if (*needed > *out_size) + return WERR_MORE_DATA; + else + return WERR_OK; +} + +/******************************************************************** + * spoolss_setprinterdata + ********************************************************************/ + +WERROR _spoolss_setprinterdataex(pipes_struct *p, SPOOL_Q_SETPRINTERDATAEX *q_u, SPOOL_R_SETPRINTERDATAEX *r_u) +{ + SPOOL_Q_SETPRINTERDATA q_u_local; + SPOOL_R_SETPRINTERDATA r_u_local; + fstring key; + + DEBUG(4,("_spoolss_setprinterdataex\n")); + + /* From MSDN documentation of SetPrinterDataEx: pass request to + SetPrinterData if key is "PrinterDriverData" */ + + unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1); + + if (strcmp(key, "PrinterDriverData") != 0) + return WERR_INVALID_PARAM; + + ZERO_STRUCT(q_u_local); + ZERO_STRUCT(r_u_local); + + /* make a copy to call _spoolss_setprinterdata() */ + + memcpy(&q_u_local.handle, &q_u->handle, sizeof(POLICY_HND)); + copy_unistr2(&q_u_local.value, &q_u->value); + q_u_local.type = q_u->type; + q_u_local.max_len = q_u->max_len; + q_u_local.data = q_u->data; + q_u_local.real_len = q_u->real_len; + q_u_local.numeric_data = q_u->numeric_data; + + return _spoolss_setprinterdata(p, &q_u_local, &r_u_local); +} + +/******************************************************************** + * spoolss_enumprinterkey + ********************************************************************/ + +/* constants for EnumPrinterKey() */ +#define ENUMERATED_KEY_SIZE 19 + +WERROR _spoolss_enumprinterkey(pipes_struct *p, SPOOL_Q_ENUMPRINTERKEY *q_u, SPOOL_R_ENUMPRINTERKEY *r_u) +{ + fstring key; + uint16 enumkeys[ENUMERATED_KEY_SIZE+1]; + char* ptr = NULL; + int i; + char *PrinterKey = "PrinterDriverData"; + + DEBUG(4,("_spoolss_enumprinterkey\n")); + + unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1); + + /* + * we only support enumating all keys (key == "") + * Of course, the only key we support is the "PrinterDriverData" + * key + */ + if (strlen(key) == 0) + { + r_u->needed = ENUMERATED_KEY_SIZE *2; + if (q_u->size < r_u->needed) + return WERR_MORE_DATA; + + ptr = PrinterKey; + for (i=0; i<ENUMERATED_KEY_SIZE-2; i++) + { + enumkeys[i] = (uint16)(*ptr); + ptr++; + } + + /* tag of with 2 '\0's */ + enumkeys[i++] = '\0'; + enumkeys[i] = '\0'; + + if (!make_spoolss_buffer5(p->mem_ctx, &r_u->keys, ENUMERATED_KEY_SIZE, enumkeys)) + return WERR_BADFILE; + + return WERR_OK; + } + + /* The "PrinterDriverData" key should have no subkeys */ + if (strcmp(key, PrinterKey) == 0) + { + r_u-> needed = 2; + if (q_u->size < r_u->needed) + return WERR_MORE_DATA; + enumkeys[0] = 0x0; + if (!make_spoolss_buffer5(p->mem_ctx, &r_u->keys, 1, enumkeys)) + return WERR_BADFILE; + + return WERR_OK; + } + + + /* The return value for an unknown key is documented in MSDN + EnumPrinterKey description */ + return WERR_BADFILE; +} + +/******************************************************************** + * 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; + fstring key, value; + Printer_entry *Printer = find_printer_index_by_hnd(p, handle); + int snum; + uint32 param_index, + data_len, + type; + WERROR result; + uint8 *data=NULL; + + + DEBUG(4,("_spoolss_enumprinterdataex\n")); + + if (!Printer) { + DEBUG(2,("_spoolss_enumprinterdata: Invalid handle (%s:%u:%u1<).\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + + /* + * The only key we support is "PrinterDriverData". This should return + > an array of all the key/value pairs returned by EnumPrinterDataSee + * _spoolss_getprinterdataex() for details --jerry + */ + + unistr2_to_ascii(key, &q_u->key, sizeof(key) - 1); + if (strcmp(key, "PrinterDriverData") != 0) + { + DEBUG(10,("_spoolss_enumprinterdataex: Unknown keyname [%s]\n", key)); + return WERR_INVALID_PARAM; + } + + + if (!get_printer_snum(p,handle, &snum)) + return WERR_BADFID; + + ZERO_STRUCT(printer); + result = get_a_printer(&printer, 2, lp_servicename(snum)); + if (!W_ERROR_IS_OK(result)) + return result; + + + /* + * loop through all params and build the array to pass + * back to the client + */ + result = WERR_OK; + param_index = 0; + needed = 0; + num_entries = 0; + + while (get_specific_param_by_index(*printer, 2, param_index, value, &data, &type, &data_len)) + { + PRINTER_ENUM_VALUES *ptr; + uint32 add_len = 0; + + DEBUG(10,("retrieved value number [%d] [%s]\n", num_entries, value)); + + if ((ptr=talloc_realloc(p->mem_ctx, enum_values, (num_entries+1) * sizeof(PRINTER_ENUM_VALUES))) == NULL) + { + DEBUG(0,("talloc_realloc failed to allocate more memory!\n")); + result = WERR_NOMEM; + goto done; + } + enum_values = ptr; + + /* copy the data */ + init_unistr(&enum_values[num_entries].valuename, value); + enum_values[num_entries].value_len = (strlen(value)+1) * 2; + enum_values[num_entries].type = type; + + if (!(enum_values[num_entries].data=talloc_zero(p->mem_ctx, data_len+add_len))) { + DEBUG(0,("talloc_realloc failed to allocate more memory for data!\n")); + result = WERR_NOMEM; + goto done; + } + memcpy(enum_values[num_entries].data, data, data_len); + enum_values[num_entries].data_len = data_len + add_len; + + /* keep track of the size of the array in bytes */ + + needed += spoolss_size_printer_enum_values(&enum_values[num_entries]); + + num_entries++; + param_index++; + } + + r_u->needed = needed; + r_u->returned = num_entries; + + if (needed > in_size) { + result = WERR_MORE_DATA; + goto done; + } + + /* copy data into the reply */ + + r_u->ctr.size = r_u->needed; + r_u->ctr.size_of_array = r_u->returned; + r_u->ctr.values = enum_values; + + + +done: + free_a_printer(&printer, 2); + + return result; +} + +/**************************************************************************** +****************************************************************************/ + +static void fill_printprocessordirectory_1(PRINTPROCESSOR_DIRECTORY_1 *info, char *name) +{ + init_unistr(&info->name, name); +} + +static WERROR getprintprocessordirectory_level_1(UNISTR2 *name, + UNISTR2 *environment, + NEW_BUFFER *buffer, + uint32 offered, + uint32 *needed) +{ + pstring path; + pstring long_archi; + pstring short_archi; + PRINTPROCESSOR_DIRECTORY_1 *info=NULL; + + unistr2_to_ascii(long_archi, environment, sizeof(long_archi)-1); + + if (get_short_archi(short_archi, long_archi)==FALSE) + return WERR_INVALID_ENVIRONMENT; + + if((info=(PRINTPROCESSOR_DIRECTORY_1 *)malloc(sizeof(PRINTPROCESSOR_DIRECTORY_1))) == NULL) + return WERR_NOMEM; + + /* Not sure what to return here - are UNC names valid here?. + Windows returns the string: C:\WINNT\System32\spool\PRTPROCS\W32X86 + which is pretty bogus for a RPC. */ + + slprintf(path, sizeof(path)-1, "\\\\%s\\print$\\%s", get_called_name(), short_archi); + + DEBUG(4,("print processor directory: [%s]\n", path)); + + fill_printprocessordirectory_1(info, path); + + *needed += spoolss_size_printprocessordirectory_info_1(info); + + if (!alloc_buffer_size(buffer, *needed)) { + safe_free(info); + return WERR_INSUFFICIENT_BUFFER; + } + + smb_io_printprocessordirectory_1("", buffer, info, 0); + + safe_free(info); + + if (*needed > offered) + return WERR_INSUFFICIENT_BUFFER; + else + return WERR_OK; +} + +WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROCESSORDIRECTORY *q_u, SPOOL_R_GETPRINTPROCESSORDIRECTORY *r_u) +{ + uint32 level = q_u->level; + NEW_BUFFER *buffer = NULL; + uint32 offered = q_u->offered; + uint32 *needed = &r_u->needed; + + /* that's an [in out] buffer */ + spoolss_move_buffer(q_u->buffer, &r_u->buffer); + buffer = r_u->buffer; + + DEBUG(5,("_spoolss_getprintprocessordirectory\n")); + + *needed=0; + + switch(level) { + case 1: + return getprintprocessordirectory_level_1 + (&q_u->name, &q_u->environment, buffer, offered, needed); + default: + return WERR_UNKNOWN_LEVEL; + } + + return WERR_ACCESS_DENIED; +} + diff --git a/source3/rpc_server/srv_srvsvc.c b/source3/rpc_server/srv_srvsvc.c new file mode 100644 index 0000000000..ee4ec8aa0a --- /dev/null +++ b/source3/rpc_server/srv_srvsvc.c @@ -0,0 +1,522 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the interface to the srvsvc pipe. */ + +#include "includes.h" + +/******************************************************************* + api_srv_net_srv_get_info +********************************************************************/ + +static BOOL api_srv_net_srv_get_info(pipes_struct *p) +{ + SRV_Q_NET_SRV_GET_INFO q_u; + SRV_R_NET_SRV_GET_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net server get info */ + if (!srv_io_q_net_srv_get_info("", &q_u, data, 0)) + return False; + + r_u.status = _srv_net_srv_get_info(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if (!srv_io_r_net_srv_get_info("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_srv_net_srv_get_info +********************************************************************/ + +static BOOL api_srv_net_srv_set_info(pipes_struct *p) +{ + SRV_Q_NET_SRV_SET_INFO q_u; + SRV_R_NET_SRV_SET_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net server set info */ + if (!srv_io_q_net_srv_set_info("", &q_u, data, 0)) + return False; + + r_u.status = _srv_net_srv_set_info(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if (!srv_io_r_net_srv_set_info("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_srv_net_file_enum +********************************************************************/ + +static BOOL api_srv_net_file_enum(pipes_struct *p) +{ + SRV_Q_NET_FILE_ENUM q_u; + SRV_R_NET_FILE_ENUM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net file enum */ + if (!srv_io_q_net_file_enum("", &q_u, data, 0)) + return False; + + r_u.status = _srv_net_file_enum(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!srv_io_r_net_file_enum("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + api_srv_net_conn_enum +********************************************************************/ + +static BOOL api_srv_net_conn_enum(pipes_struct *p) +{ + SRV_Q_NET_CONN_ENUM q_u; + SRV_R_NET_CONN_ENUM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net server get enum */ + if (!srv_io_q_net_conn_enum("", &q_u, data, 0)) + return False; + + r_u.status = _srv_net_conn_enum(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if (!srv_io_r_net_conn_enum("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + Enumerate sessions. +********************************************************************/ + +static BOOL api_srv_net_sess_enum(pipes_struct *p) +{ + SRV_Q_NET_SESS_ENUM q_u; + SRV_R_NET_SESS_ENUM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net server get enum */ + if (!srv_io_q_net_sess_enum("", &q_u, data, 0)) + return False; + + /* construct reply. always indicate success */ + r_u.status = _srv_net_sess_enum(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if (!srv_io_r_net_sess_enum("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + RPC to enumerate shares. +********************************************************************/ + +static BOOL api_srv_net_share_enum_all(pipes_struct *p) +{ + SRV_Q_NET_SHARE_ENUM q_u; + SRV_R_NET_SHARE_ENUM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server get enum. */ + if(!srv_io_q_net_share_enum("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_share_enum_all: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n")); + return False; + } + + r_u.status = _srv_net_share_enum_all(p, &q_u, &r_u); + + if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_share_enum_all: Failed to marshall SRV_R_NET_SHARE_ENUM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + RPC to enumerate shares. +********************************************************************/ + +static BOOL api_srv_net_share_enum(pipes_struct *p) +{ + SRV_Q_NET_SHARE_ENUM q_u; + SRV_R_NET_SHARE_ENUM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server get enum. */ + if(!srv_io_q_net_share_enum("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_share_enum: Failed to unmarshall SRV_Q_NET_SHARE_ENUM.\n")); + return False; + } + + r_u.status = _srv_net_share_enum(p, &q_u, &r_u); + + if (!srv_io_r_net_share_enum("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_share_enum: Failed to marshall SRV_R_NET_SHARE_ENUM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + RPC to return share information. +********************************************************************/ + +static BOOL api_srv_net_share_get_info(pipes_struct *p) +{ + SRV_Q_NET_SHARE_GET_INFO q_u; + SRV_R_NET_SHARE_GET_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server get info. */ + if(!srv_io_q_net_share_get_info("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_share_get_info: Failed to unmarshall SRV_Q_NET_SHARE_GET_INFO.\n")); + return False; + } + + r_u.status = _srv_net_share_get_info(p, &q_u, &r_u); + + if(!srv_io_r_net_share_get_info("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_share_get_info: Failed to marshall SRV_R_NET_SHARE_GET_INFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + RPC to set share information. +********************************************************************/ + +static BOOL api_srv_net_share_set_info(pipes_struct *p) +{ + SRV_Q_NET_SHARE_SET_INFO q_u; + SRV_R_NET_SHARE_SET_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server set info. */ + if(!srv_io_q_net_share_set_info("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_share_set_info: Failed to unmarshall SRV_Q_NET_SHARE_SET_INFO.\n")); + return False; + } + + r_u.status = _srv_net_share_set_info(p, &q_u, &r_u); + + if(!srv_io_r_net_share_set_info("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_share_set_info: Failed to marshall SRV_R_NET_SHARE_SET_INFO.\n")); + return False; + } + + return True; +} + +/******************************************************************* + RPC to add share information. +********************************************************************/ + +static BOOL api_srv_net_share_add(pipes_struct *p) +{ + SRV_Q_NET_SHARE_ADD q_u; + SRV_R_NET_SHARE_ADD r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server add info. */ + if(!srv_io_q_net_share_add("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_share_add: Failed to unmarshall SRV_Q_NET_SHARE_ADD.\n")); + return False; + } + + r_u.status = _srv_net_share_add(p, &q_u, &r_u); + + if(!srv_io_r_net_share_add("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_share_add: Failed to marshall SRV_R_NET_SHARE_ADD.\n")); + return False; + } + + return True; +} + +/******************************************************************* + RPC to delete share information. +********************************************************************/ + +static BOOL api_srv_net_share_del(pipes_struct *p) +{ + SRV_Q_NET_SHARE_DEL q_u; + SRV_R_NET_SHARE_DEL r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server del info. */ + if(!srv_io_q_net_share_del("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_share_del: Failed to unmarshall SRV_Q_NET_SHARE_DEL.\n")); + return False; + } + + r_u.status = _srv_net_share_del(p, &q_u, &r_u); + + if(!srv_io_r_net_share_del("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_share_del: Failed to marshall SRV_R_NET_SHARE_DEL.\n")); + return False; + } + + return True; +} + +/******************************************************************* + api_srv_net_remote_tod +********************************************************************/ + +static BOOL api_srv_net_remote_tod(pipes_struct *p) +{ + SRV_Q_NET_REMOTE_TOD q_u; + SRV_R_NET_REMOTE_TOD r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net server get enum */ + if(!srv_io_q_net_remote_tod("", &q_u, data, 0)) + return False; + + r_u.status = _srv_net_remote_tod(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!srv_io_r_net_remote_tod("", &r_u, rdata, 0)) + return False; + + return True; +} + +/******************************************************************* + RPC to enumerate disks available on a server e.g. C:, D: ... +*******************************************************************/ + +static BOOL api_srv_net_disk_enum(pipes_struct *p) +{ + SRV_Q_NET_DISK_ENUM q_u; + SRV_R_NET_DISK_ENUM r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server disk enum. */ + if(!srv_io_q_net_disk_enum("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_disk_enum: Failed to unmarshall SRV_Q_NET_DISK_ENUM.\n")); + return False; + } + + r_u.status = _srv_net_disk_enum(p, &q_u, &r_u); + + if(!srv_io_r_net_disk_enum("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_disk_enum: Failed to marshall SRV_R_NET_DISK_ENUM.\n")); + return False; + } + + return True; +} + +/******************************************************************* + NetValidateName (opnum 0x21) +*******************************************************************/ + +static BOOL api_srv_net_name_validate(pipes_struct *p) +{ + SRV_Q_NET_NAME_VALIDATE q_u; + SRV_R_NET_NAME_VALIDATE r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net server disk enum. */ + if(!srv_io_q_net_name_validate("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_name_validate: Failed to unmarshall SRV_Q_NET_NAME_VALIDATE.\n")); + return False; + } + + r_u.status = _srv_net_name_validate(p, &q_u, &r_u); + + if(!srv_io_r_net_name_validate("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_name_validate: Failed to marshall SRV_R_NET_NAME_VALIDATE.\n")); + return False; + } + + return True; +} + +/******************************************************************* + NetFileQuerySecdesc (opnum 0x27) +*******************************************************************/ + +static BOOL api_srv_net_file_query_secdesc(pipes_struct *p) +{ + SRV_Q_NET_FILE_QUERY_SECDESC q_u; + SRV_R_NET_FILE_QUERY_SECDESC r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net file get info from Win9x */ + if(!srv_io_q_net_file_query_secdesc("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_file_query_secdesc: Failed to unmarshall SRV_Q_NET_FILE_QUERY_SECDESC.\n")); + return False; + } + + r_u.status = _srv_net_file_query_secdesc(p, &q_u, &r_u); + + if(!srv_io_r_net_file_query_secdesc("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_file_query_secdesc: Failed to marshall SRV_R_NET_FILE_QUERY_SECDESC.\n")); + return False; + } + + return True; +} + +/******************************************************************* + NetFileSetSecdesc (opnum 0x28) +*******************************************************************/ + +static BOOL api_srv_net_file_set_secdesc(pipes_struct *p) +{ + SRV_Q_NET_FILE_SET_SECDESC q_u; + SRV_R_NET_FILE_SET_SECDESC r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* Unmarshall the net file set info from Win9x */ + if(!srv_io_q_net_file_set_secdesc("", &q_u, data, 0)) { + DEBUG(0,("api_srv_net_file_set_secdesc: Failed to unmarshall SRV_Q_NET_FILE_SET_SECDESC.\n")); + return False; + } + + r_u.status = _srv_net_file_set_secdesc(p, &q_u, &r_u); + + if(!srv_io_r_net_file_set_secdesc("", &r_u, rdata, 0)) { + DEBUG(0,("api_srv_net_file_set_secdesc: Failed to marshall SRV_R_NET_FILE_SET_SECDESC.\n")); + return False; + } + + return True; +} + +/******************************************************************* +\PIPE\srvsvc commands +********************************************************************/ + +struct api_struct api_srv_cmds[] = +{ + { "SRV_NETCONNENUM" , SRV_NETCONNENUM , api_srv_net_conn_enum }, + { "SRV_NETSESSENUM" , SRV_NETSESSENUM , api_srv_net_sess_enum }, + { "SRV_NETSHAREENUM_ALL" , SRV_NETSHAREENUM_ALL , api_srv_net_share_enum_all }, + { "SRV_NETSHAREENUM" , SRV_NETSHAREENUM , api_srv_net_share_enum }, + { "SRV_NET_SHARE_ADD" , SRV_NET_SHARE_ADD , api_srv_net_share_add }, + { "SRV_NET_SHARE_DEL" , SRV_NET_SHARE_DEL , api_srv_net_share_del }, + { "SRV_NET_SHARE_GET_INFO", SRV_NET_SHARE_GET_INFO, api_srv_net_share_get_info }, + { "SRV_NET_SHARE_SET_INFO", SRV_NET_SHARE_SET_INFO, api_srv_net_share_set_info }, + { "SRV_NETFILEENUM" , SRV_NETFILEENUM , api_srv_net_file_enum }, + { "SRV_NET_SRV_GET_INFO" , SRV_NET_SRV_GET_INFO , api_srv_net_srv_get_info }, + { "SRV_NET_SRV_SET_INFO" , SRV_NET_SRV_SET_INFO , api_srv_net_srv_set_info }, + { "SRV_NET_REMOTE_TOD" , SRV_NET_REMOTE_TOD , api_srv_net_remote_tod }, + { "SRV_NET_DISK_ENUM" , SRV_NET_DISK_ENUM , api_srv_net_disk_enum }, + { "SRV_NET_NAME_VALIDATE" , SRV_NET_NAME_VALIDATE , api_srv_net_name_validate}, + { "SRV_NETFILEQUERYSECDESC",SRV_NETFILEQUERYSECDESC,api_srv_net_file_query_secdesc}, + { "SRV_NETFILESETSECDESC" , SRV_NETFILESETSECDESC , api_srv_net_file_set_secdesc}, + { NULL , 0 , NULL } +}; + +/******************************************************************* +receives a srvsvc pipe and responds. +********************************************************************/ +BOOL api_srvsvc_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_srvsvc_rpc", api_srv_cmds); +} diff --git a/source3/rpc_server/srv_srvsvc_nt.c b/source3/rpc_server/srv_srvsvc_nt.c new file mode 100644 index 0000000000..1f3b1945e3 --- /dev/null +++ b/source3/rpc_server/srv_srvsvc_nt.c @@ -0,0 +1,1935 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the implementation of the srvsvc pipe. */ + +#include "includes.h" + +extern pstring global_myname; + +/******************************************************************* + Fill in a share info level 1 structure. + ********************************************************************/ + +static void init_srv_share_info_1(pipes_struct *p, SRV_SHARE_INFO_1 *sh1, int snum) +{ + int len_net_name; + pstring net_name; + pstring remark; + uint32 type; + + pstrcpy(net_name, lp_servicename(snum)); + pstrcpy(remark, lp_comment(snum)); + standard_sub_conn(p->conn, remark); + len_net_name = strlen(net_name); + + /* work out the share type */ + type = STYPE_DISKTREE; + + if (lp_print_ok(snum)) + type = STYPE_PRINTQ; + if (strequal("IPC$", net_name) || strequal("ADMIN$", net_name)) + type = STYPE_IPC; + if (net_name[len_net_name] == '$') + type |= STYPE_HIDDEN; + + init_srv_share_info1(&sh1->info_1, net_name, type, remark); + init_srv_share_info1_str(&sh1->info_1_str, net_name, remark); +} + +/******************************************************************* + Fill in a share info level 2 structure. + ********************************************************************/ + +static void init_srv_share_info_2(pipes_struct *p, SRV_SHARE_INFO_2 *sh2, int snum) +{ + int len_net_name; + pstring net_name; + pstring remark; + pstring path; + pstring passwd; + uint32 type; + + pstrcpy(net_name, lp_servicename(snum)); + pstrcpy(remark, lp_comment(snum)); + standard_sub_conn(p->conn, remark); + pstrcpy(path, "C:"); + pstrcat(path, lp_pathname(snum)); + + /* + * 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, '/', '\\'); + + pstrcpy(passwd, ""); + len_net_name = strlen(net_name); + + /* work out the share type */ + type = STYPE_DISKTREE; + + if (lp_print_ok(snum)) + type = STYPE_PRINTQ; + if (strequal("IPC$", net_name) || strequal("ADMIN$", net_name)) + type = STYPE_IPC; + if (net_name[len_net_name] == '$') + type |= STYPE_HIDDEN; + + init_srv_share_info2(&sh2->info_2, net_name, type, remark, 0, 0xffffffff, 1, path, passwd); + init_srv_share_info2_str(&sh2->info_2_str, net_name, remark, path, passwd); +} + +/******************************************************************* + What to do when smb.conf is updated. + ********************************************************************/ + +static void smb_conf_updated(int msg_type, pid_t src, void *buf, size_t len) +{ + DEBUG(10,("smb_conf_updated: Got message saying smb.conf was updated. Reloading.\n")); + reload_services(False); +} + +/******************************************************************* + Create the share security tdb. + ********************************************************************/ + +static TDB_CONTEXT *share_tdb; /* used for share security descriptors */ +#define SHARE_DATABASE_VERSION_V1 1 +#define SHARE_DATABASE_VERSION_V2 2 /* version id in little endian. */ + +BOOL share_info_db_init(void) +{ + static pid_t local_pid; + char *vstring = "INFO/version"; + int32 vers_id; + + if (share_tdb && local_pid == sys_getpid()) + return True; + share_tdb = tdb_open_log(lock_path("share_info.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!share_tdb) { + DEBUG(0,("Failed to open share info database %s (%s)\n", + lock_path("share_info.tdb"), strerror(errno) )); + return False; + } + + local_pid = sys_getpid(); + + /* handle a Samba upgrade */ + tdb_lock_bystring(share_tdb, vstring); + + /* Cope with byte-reversed older versions of the db. */ + vers_id = tdb_fetch_int32(share_tdb, vstring); + if ((vers_id == SHARE_DATABASE_VERSION_V1) || (IREV(vers_id) == SHARE_DATABASE_VERSION_V1)) { + /* Written on a bigendian machine with old fetch_int code. Save as le. */ + tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2); + vers_id = SHARE_DATABASE_VERSION_V2; + } + + if (vers_id != SHARE_DATABASE_VERSION_V2) { + tdb_traverse(share_tdb, tdb_traverse_delete_fn, NULL); + tdb_store_int32(share_tdb, vstring, SHARE_DATABASE_VERSION_V2); + } + tdb_unlock_bystring(share_tdb, vstring); + + message_register(MSG_SMB_CONF_UPDATED, smb_conf_updated); + + return True; +} + +/******************************************************************* + Fake up a Everyone, full access as a default. + ********************************************************************/ + +static SEC_DESC *get_share_security_default( TALLOC_CTX *ctx, int snum, size_t *psize) +{ + extern DOM_SID global_sid_World; + extern struct generic_mapping file_generic_mapping; + SEC_ACCESS sa; + SEC_ACE ace; + SEC_ACL *psa = NULL; + SEC_DESC *psd = NULL; + uint32 def_access = GENERIC_ALL_ACCESS; + + se_map_generic(&def_access, &file_generic_mapping); + + init_sec_access(&sa, GENERIC_ALL_ACCESS | def_access ); + init_sec_ace(&ace, &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0); + + if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &ace)) != NULL) { + psd = make_sec_desc(ctx, SEC_DESC_REVISION, NULL, NULL, NULL, psa, psize); + } + + if (!psd) { + DEBUG(0,("get_share_security: Failed to make SEC_DESC.\n")); + return NULL; + } + + return psd; +} + +/******************************************************************* + Pull a security descriptor from the share tdb. + ********************************************************************/ + +static SEC_DESC *get_share_security( TALLOC_CTX *ctx, int snum, size_t *psize) +{ + prs_struct ps; + fstring key; + SEC_DESC *psd = NULL; + + *psize = 0; + + /* Fetch security descriptor from tdb */ + + slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum)); + + if (tdb_prs_fetch(share_tdb, key, &ps, ctx)!=0 || + !sec_io_desc("get_share_security", &psd, &ps, 1)) { + + DEBUG(4,("get_share_security: using default secdesc for %s\n", lp_servicename(snum) )); + + return get_share_security_default(ctx, snum, psize); + } + + if (psd) + *psize = sec_desc_size(psd); + + prs_mem_free(&ps); + return psd; +} + +/******************************************************************* + Store a security descriptor in the share db. + ********************************************************************/ + +static BOOL set_share_security(TALLOC_CTX *ctx, const char *share_name, SEC_DESC *psd) +{ + prs_struct ps; + TALLOC_CTX *mem_ctx = NULL; + fstring key; + BOOL ret = False; + + mem_ctx = talloc_init(); + if (mem_ctx == NULL) + return False; + + prs_init(&ps, (uint32)sec_desc_size(psd), mem_ctx, MARSHALL); + + if (!sec_io_desc("share_security", &psd, &ps, 1)) + goto out; + + slprintf(key, sizeof(key)-1, "SECDESC/%s", share_name); + + if (tdb_prs_store(share_tdb, key, &ps)==0) { + ret = True; + DEBUG(5,("set_share_security: stored secdesc for %s\n", share_name )); + } else { + DEBUG(1,("set_share_security: Failed to store secdesc for %s\n", share_name )); + } + + /* Free malloc'ed memory */ + + out: + + prs_mem_free(&ps); + if (mem_ctx) + talloc_destroy(mem_ctx); + return ret; +} + +/******************************************************************* + Delete a security descriptor. +********************************************************************/ + +static BOOL delete_share_security(int snum) +{ + TDB_DATA kbuf; + fstring key; + + slprintf(key, sizeof(key)-1, "SECDESC/%s", lp_servicename(snum)); + kbuf.dptr = key; + kbuf.dsize = strlen(key)+1; + + if (tdb_delete(share_tdb, kbuf) != 0) { + DEBUG(0,("delete_share_security: Failed to delete entry for share %s\n", + lp_servicename(snum) )); + return False; + } + + return True; +} + +/******************************************************************* + Map any generic bits to file specific bits. +********************************************************************/ + +void map_generic_share_sd_bits(SEC_DESC *psd) +{ + extern struct generic_mapping file_generic_mapping; + 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->ace[i]; + uint32 orig_mask = psa->info.mask; + + se_map_generic(&psa->info.mask, &file_generic_mapping); + psa->info.mask |= orig_mask; + } +} + +/******************************************************************* + Can this user access with share with the required permissions ? +********************************************************************/ + +BOOL share_access_check(connection_struct *conn, int snum, uint16 vuid, uint32 desired_access) +{ + uint32 granted; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + SEC_DESC *psd = NULL; + size_t sd_size; + NT_USER_TOKEN *token = NULL; + user_struct *vuser = get_valid_user_struct(vuid); + BOOL ret = True; + + mem_ctx = talloc_init(); + if (mem_ctx == NULL) + return False; + + psd = get_share_security(mem_ctx, snum, &sd_size); + + if (!psd) + goto out; + + if (vuser) + token = vuser->nt_user_token; + else + token = conn->nt_user_token; + + ret = se_access_check(psd, token, desired_access, &granted, &status); + + out: + + talloc_destroy(mem_ctx); + + return ret; +} + +/******************************************************************* + Fill in a share info level 501 structure. +********************************************************************/ + +static void init_srv_share_info_501(pipes_struct *p, SRV_SHARE_INFO_501 *sh501, int snum) +{ + int len_net_name; + pstring net_name; + pstring remark; + uint32 type; + + pstrcpy(net_name, lp_servicename(snum)); + pstrcpy(remark, lp_comment(snum)); + standard_sub_conn(p->conn, remark); + + len_net_name = strlen(net_name); + + /* work out the share type */ + type = STYPE_DISKTREE; + + if (lp_print_ok(snum)) + type = STYPE_PRINTQ; + if (strequal("IPC$", net_name) || strequal("ADMIN$", net_name)) + type = STYPE_IPC; + if (net_name[len_net_name] == '$') + type |= STYPE_HIDDEN; + + init_srv_share_info501(&sh501->info_501, net_name, type, remark, (lp_csc_policy(snum) << 4)); + init_srv_share_info501_str(&sh501->info_501_str, net_name, remark); +} + +/******************************************************************* + Fill in a share info level 502 structure. + ********************************************************************/ + +static void init_srv_share_info_502(pipes_struct *p, SRV_SHARE_INFO_502 *sh502, int snum) +{ + int len_net_name; + pstring net_name; + pstring remark; + pstring path; + pstring passwd; + uint32 type; + SEC_DESC *sd; + size_t sd_size; + TALLOC_CTX *ctx = p->mem_ctx; + + + ZERO_STRUCTP(sh502); + + pstrcpy(net_name, lp_servicename(snum)); + pstrcpy(remark, lp_comment(snum)); + standard_sub_conn(p->conn, remark); + pstrcpy(path, "C:"); + pstrcat(path, lp_pathname(snum)); + + /* + * 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, '/', '\\'); + + pstrcpy(passwd, ""); + len_net_name = strlen(net_name); + + /* work out the share type */ + type = STYPE_DISKTREE; + + if (lp_print_ok(snum)) + type = STYPE_PRINTQ; + if (strequal("IPC$", net_name)) + type = STYPE_IPC; + if (net_name[len_net_name] == '$') + type |= STYPE_HIDDEN; + + sd = get_share_security(ctx, snum, &sd_size); + + init_srv_share_info502(&sh502->info_502, net_name, type, remark, 0, 0xffffffff, 1, path, passwd, sd, sd_size); + init_srv_share_info502_str(&sh502->info_502_str, &sh502->info_502, net_name, remark, path, passwd, sd, sd_size); +} + +/*************************************************************************** + Fill in a share info level 1005 structure. + ***************************************************************************/ + +static void init_srv_share_info_1005(SRV_SHARE_INFO_1005* sh1005, int snum) +{ + sh1005->dfs_root_flag = 0; + + if(lp_host_msdfs() && lp_msdfs_root(snum)) + sh1005->dfs_root_flag = 3; +} + +/******************************************************************* + True if it ends in '$'. + ********************************************************************/ + +static BOOL is_admin_share(int snum) +{ + pstring net_name; + + pstrcpy(net_name, lp_servicename(snum)); + return (net_name[strlen(net_name)] == '$') ? True : False; +} + +/******************************************************************* + Fill in a share info structure. + ********************************************************************/ + +static BOOL init_srv_share_info_ctr(pipes_struct *p, SRV_SHARE_INFO_CTR *ctr, + uint32 info_level, uint32 *resume_hnd, uint32 *total_entries, BOOL all_shares) +{ + int num_entries = 0; + int num_services = lp_numservices(); + int snum; + TALLOC_CTX *ctx = p->mem_ctx; + + DEBUG(5,("init_srv_share_info_ctr\n")); + + ZERO_STRUCTPN(ctr); + + ctr->info_level = ctr->switch_value = info_level; + *resume_hnd = 0; + + /* Count the number of entries. */ + for (snum = 0; snum < num_services; snum++) { + if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_admin_share(snum)) ) + num_entries++; + } + + *total_entries = num_entries; + ctr->num_entries2 = ctr->num_entries = num_entries; + ctr->ptr_share_info = ctr->ptr_entries = 1; + + if (!num_entries) + return True; + + switch (info_level) { + case 1: + { + SRV_SHARE_INFO_1 *info1; + int i = 0; + + info1 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_1)); + + for (snum = *resume_hnd; snum < num_services; snum++) { + if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_admin_share(snum)) ) { + init_srv_share_info_1(p, &info1[i++], snum); + } + } + + ctr->share.info1 = info1; + break; + } + + case 2: + { + SRV_SHARE_INFO_2 *info2; + int i = 0; + + info2 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_2)); + + for (snum = *resume_hnd; snum < num_services; snum++) { + if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_admin_share(snum)) ) { + init_srv_share_info_2(p, &info2[i++], snum); + } + } + + ctr->share.info2 = info2; + break; + } + + case 501: + { + SRV_SHARE_INFO_501 *info501; + int i = 0; + + info501 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_501)); + + for (snum = *resume_hnd; snum < num_services; snum++) { + if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_admin_share(snum)) ) { + init_srv_share_info_501(p, &info501[i++], snum); + } + } + + ctr->share.info501 = info501; + break; + } + + case 502: + { + SRV_SHARE_INFO_502 *info502; + int i = 0; + + info502 = talloc(ctx, num_entries * sizeof(SRV_SHARE_INFO_502)); + + for (snum = *resume_hnd; snum < num_services; snum++) { + if (lp_browseable(snum) && lp_snum_ok(snum) && (all_shares || !is_admin_share(snum)) ) { + init_srv_share_info_502(p, &info502[i++], snum); + } + } + + ctr->share.info502 = info502; + break; + } + + default: + DEBUG(5,("init_srv_share_info_ctr: unsupported switch value %d\n", info_level)); + return False; + } + + return True; +} + +/******************************************************************* + Inits a SRV_R_NET_SHARE_ENUM structure. +********************************************************************/ + +static void init_srv_r_net_share_enum(pipes_struct *p, SRV_R_NET_SHARE_ENUM *r_n, + uint32 info_level, uint32 resume_hnd, BOOL all) +{ + DEBUG(5,("init_srv_r_net_share_enum: %d\n", __LINE__)); + + if (init_srv_share_info_ctr(p, &r_n->ctr, info_level, + &resume_hnd, &r_n->total_entries, all)) { + r_n->status = WERR_OK; + } else { + r_n->status = WERR_UNKNOWN_LEVEL; + } + + init_enum_hnd(&r_n->enum_hnd, resume_hnd); +} + +/******************************************************************* + Inits a SRV_R_NET_SHARE_GET_INFO structure. +********************************************************************/ + +static void init_srv_r_net_share_get_info(pipes_struct *p, SRV_R_NET_SHARE_GET_INFO *r_n, + char *share_name, uint32 info_level) +{ + WERROR status = WERR_OK; + int snum; + + DEBUG(5,("init_srv_r_net_share_get_info: %d\n", __LINE__)); + + r_n->info.switch_value = info_level; + + snum = find_service(share_name); + + if (snum >= 0) { + switch (info_level) { + case 1: + init_srv_share_info_1(p, &r_n->info.share.info1, snum); + break; + case 2: + init_srv_share_info_2(p, &r_n->info.share.info2, snum); + break; + case 501: + init_srv_share_info_501(p, &r_n->info.share.info501, snum); + break; + case 502: + init_srv_share_info_502(p, &r_n->info.share.info502, snum); + break; + case 1005: + init_srv_share_info_1005(&r_n->info.share.info1005, snum); + break; + default: + DEBUG(5,("init_srv_net_share_get_info: unsupported switch value %d\n", info_level)); + status = WERR_UNKNOWN_LEVEL; + break; + } + } else { + status = WERR_INVALID_NAME; + } + + r_n->info.ptr_share_ctr = W_ERROR_IS_OK(status) ? 1 : 0; + r_n->status = status; +} + +/******************************************************************* + fill in a sess info level 1 structure. + ********************************************************************/ + +static void init_srv_sess_0_info(SESS_INFO_0 *se0, SESS_INFO_0_STR *str0, char *name) +{ + init_srv_sess_info0(se0, name); + init_srv_sess_info0_str(str0, name); +} + +/******************************************************************* + fill in a sess info level 0 structure. + ********************************************************************/ + +static void init_srv_sess_info_0(SRV_SESS_INFO_0 *ss0, uint32 *snum, uint32 *stot) +{ + uint32 num_entries = 0; + (*stot) = 1; + + if (ss0 == NULL) { + (*snum) = 0; + return; + } + + DEBUG(5,("init_srv_sess_0_ss0\n")); + + if (snum) { + for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) { + init_srv_sess_0_info(&ss0->info_0[num_entries], + &ss0->info_0_str[num_entries], "MACHINE"); + + /* move on to creating next session */ + /* move on to creating next sess */ + num_entries++; + } + + ss0->num_entries_read = num_entries; + ss0->ptr_sess_info = num_entries > 0 ? 1 : 0; + ss0->num_entries_read2 = num_entries; + + if ((*snum) >= (*stot)) { + (*snum) = 0; + } + + } else { + ss0->num_entries_read = 0; + ss0->ptr_sess_info = 0; + ss0->num_entries_read2 = 0; + } +} + +/******************************************************************* + fill in a sess info level 1 structure. + ********************************************************************/ + +static void init_srv_sess_1_info(SESS_INFO_1 *se1, SESS_INFO_1_STR *str1, + char *name, char *user, + uint32 num_opens, + uint32 open_time, uint32 idle_time, + uint32 usr_flgs) +{ + init_srv_sess_info1(se1 , name, user, num_opens, open_time, idle_time, usr_flgs); + init_srv_sess_info1_str(str1, name, user); +} + +/******************************************************************* + fill in a sess info level 1 structure. + ********************************************************************/ + +static void init_srv_sess_info_1(SRV_SESS_INFO_1 *ss1, uint32 *snum, uint32 *stot) +{ + uint32 num_entries = 0; + (*stot) = 1; + + if (ss1 == NULL) { + (*snum) = 0; + return; + } + + DEBUG(5,("init_srv_sess_1_ss1\n")); + + if (snum) { + for (; (*snum) < (*stot) && num_entries < MAX_SESS_ENTRIES; (*snum)++) { + init_srv_sess_1_info(&ss1->info_1[num_entries], + &ss1->info_1_str[num_entries], + "MACHINE", "dummy_user", 1, 10, 5, 0); + + /* move on to creating next session */ + /* move on to creating next sess */ + num_entries++; + } + + ss1->num_entries_read = num_entries; + ss1->ptr_sess_info = num_entries > 0 ? 1 : 0; + ss1->num_entries_read2 = num_entries; + + if ((*snum) >= (*stot)) { + (*snum) = 0; + } + + } else { + ss1->num_entries_read = 0; + ss1->ptr_sess_info = 0; + ss1->num_entries_read2 = 0; + + (*stot) = 0; + } +} + +/******************************************************************* + makes a SRV_R_NET_SESS_ENUM structure. +********************************************************************/ + +static WERROR init_srv_sess_info_ctr(SRV_SESS_INFO_CTR *ctr, + int switch_value, uint32 *resume_hnd, uint32 *total_entries) +{ + WERROR status = WERR_OK; + DEBUG(5,("init_srv_sess_info_ctr: %d\n", __LINE__)); + + ctr->switch_value = switch_value; + + switch (switch_value) { + case 0: + init_srv_sess_info_0(&(ctr->sess.info0), resume_hnd, total_entries); + ctr->ptr_sess_ctr = 1; + break; + case 1: + init_srv_sess_info_1(&(ctr->sess.info1), resume_hnd, total_entries); + ctr->ptr_sess_ctr = 1; + break; + default: + DEBUG(5,("init_srv_sess_info_ctr: unsupported switch value %d\n", switch_value)); + (*resume_hnd) = 0; + (*total_entries) = 0; + ctr->ptr_sess_ctr = 0; + status = WERR_UNKNOWN_LEVEL; + break; + } + + return status; +} + +/******************************************************************* + makes a SRV_R_NET_SESS_ENUM structure. +********************************************************************/ + +static void init_srv_r_net_sess_enum(SRV_R_NET_SESS_ENUM *r_n, + uint32 resume_hnd, int sess_level, int switch_value) +{ + DEBUG(5,("init_srv_r_net_sess_enum: %d\n", __LINE__)); + + r_n->sess_level = sess_level; + + if (sess_level == -1) + r_n->status = WERR_UNKNOWN_LEVEL; + else + r_n->status = init_srv_sess_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries); + + if (!W_ERROR_IS_OK(r_n->status)) + resume_hnd = 0; + + init_enum_hnd(&r_n->enum_hnd, resume_hnd); +} + +/******************************************************************* + fill in a conn info level 0 structure. + ********************************************************************/ + +static void init_srv_conn_info_0(SRV_CONN_INFO_0 *ss0, uint32 *snum, uint32 *stot) +{ + uint32 num_entries = 0; + (*stot) = 1; + + if (ss0 == NULL) { + (*snum) = 0; + return; + } + + DEBUG(5,("init_srv_conn_0_ss0\n")); + + if (snum) { + for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) { + + init_srv_conn_info0(&ss0->info_0[num_entries], (*stot)); + + /* move on to creating next connection */ + /* move on to creating next conn */ + num_entries++; + } + + ss0->num_entries_read = num_entries; + ss0->ptr_conn_info = num_entries > 0 ? 1 : 0; + ss0->num_entries_read2 = num_entries; + + if ((*snum) >= (*stot)) { + (*snum) = 0; + } + + } else { + ss0->num_entries_read = 0; + ss0->ptr_conn_info = 0; + ss0->num_entries_read2 = 0; + + (*stot) = 0; + } +} + +/******************************************************************* + fill in a conn info level 1 structure. + ********************************************************************/ + +static void init_srv_conn_1_info(CONN_INFO_1 *se1, CONN_INFO_1_STR *str1, + uint32 id, uint32 type, + uint32 num_opens, uint32 num_users, uint32 open_time, + char *usr_name, char *net_name) +{ + init_srv_conn_info1(se1 , id, type, num_opens, num_users, open_time, usr_name, net_name); + init_srv_conn_info1_str(str1, usr_name, net_name); +} + +/******************************************************************* + fill in a conn info level 1 structure. + ********************************************************************/ + +static void init_srv_conn_info_1(SRV_CONN_INFO_1 *ss1, uint32 *snum, uint32 *stot) +{ + uint32 num_entries = 0; + (*stot) = 1; + + if (ss1 == NULL) { + (*snum) = 0; + return; + } + + DEBUG(5,("init_srv_conn_1_ss1\n")); + + if (snum) { + for (; (*snum) < (*stot) && num_entries < MAX_CONN_ENTRIES; (*snum)++) { + init_srv_conn_1_info(&ss1->info_1[num_entries], + &ss1->info_1_str[num_entries], + (*stot), 0x3, 1, 1, 3,"dummy_user", "IPC$"); + + /* move on to creating next connection */ + /* move on to creating next conn */ + num_entries++; + } + + ss1->num_entries_read = num_entries; + ss1->ptr_conn_info = num_entries > 0 ? 1 : 0; + ss1->num_entries_read2 = num_entries; + + + if ((*snum) >= (*stot)) { + (*snum) = 0; + } + + } else { + ss1->num_entries_read = 0; + ss1->ptr_conn_info = 0; + ss1->num_entries_read2 = 0; + + (*stot) = 0; + } +} + +/******************************************************************* + makes a SRV_R_NET_CONN_ENUM structure. +********************************************************************/ + +static WERROR init_srv_conn_info_ctr(SRV_CONN_INFO_CTR *ctr, + int switch_value, uint32 *resume_hnd, uint32 *total_entries) +{ + WERROR status = WERR_OK; + DEBUG(5,("init_srv_conn_info_ctr: %d\n", __LINE__)); + + ctr->switch_value = switch_value; + + switch (switch_value) { + case 0: + init_srv_conn_info_0(&ctr->conn.info0, resume_hnd, total_entries); + ctr->ptr_conn_ctr = 1; + break; + case 1: + init_srv_conn_info_1(&ctr->conn.info1, resume_hnd, total_entries); + ctr->ptr_conn_ctr = 1; + break; + default: + DEBUG(5,("init_srv_conn_info_ctr: unsupported switch value %d\n", switch_value)); + (*resume_hnd = 0); + (*total_entries) = 0; + ctr->ptr_conn_ctr = 0; + status = WERR_UNKNOWN_LEVEL; + break; + } + + return status; +} + +/******************************************************************* + makes a SRV_R_NET_CONN_ENUM structure. +********************************************************************/ + +static void init_srv_r_net_conn_enum(SRV_R_NET_CONN_ENUM *r_n, + uint32 resume_hnd, int conn_level, int switch_value) +{ + DEBUG(5,("init_srv_r_net_conn_enum: %d\n", __LINE__)); + + r_n->conn_level = conn_level; + if (conn_level == -1) + r_n->status = WERR_UNKNOWN_LEVEL; + else + r_n->status = init_srv_conn_info_ctr(r_n->ctr, switch_value, &resume_hnd, &r_n->total_entries); + + if (!W_ERROR_IS_OK(r_n->status)) + resume_hnd = 0; + + init_enum_hnd(&r_n->enum_hnd, resume_hnd); +} + +/******************************************************************* + fill in a file info level 3 structure. + ********************************************************************/ + +static void init_srv_file_3_info(FILE_INFO_3 *fl3, FILE_INFO_3_STR *str3, + uint32 fnum, uint32 perms, uint32 num_locks, + char *path_name, char *user_name) +{ + init_srv_file_info3(fl3 , fnum, perms, num_locks, path_name, user_name); + init_srv_file_info3_str(str3, path_name, user_name); +} + +/******************************************************************* + fill in a file info level 3 structure. + ********************************************************************/ + +static void init_srv_file_info_3(SRV_FILE_INFO_3 *fl3, uint32 *fnum, uint32 *ftot) +{ + uint32 num_entries = 0; + (*ftot) = 1; + + if (fl3 == NULL) { + (*fnum) = 0; + return; + } + + DEBUG(5,("init_srv_file_3_fl3\n")); + + for (; (*fnum) < (*ftot) && num_entries < MAX_FILE_ENTRIES; (*fnum)++) { + init_srv_file_3_info(&fl3->info_3[num_entries], + &fl3->info_3_str[num_entries], + (*fnum), 0x35, 0, "\\PIPE\\samr", "dummy user"); + + /* move on to creating next file */ + num_entries++; + } + + fl3->num_entries_read = num_entries; + fl3->ptr_file_info = num_entries > 0 ? 1 : 0; + fl3->num_entries_read2 = num_entries; + + if ((*fnum) >= (*ftot)) { + (*fnum) = 0; + } +} + +/******************************************************************* + makes a SRV_R_NET_FILE_ENUM structure. +********************************************************************/ + +static WERROR init_srv_file_info_ctr(SRV_FILE_INFO_CTR *ctr, + int switch_value, uint32 *resume_hnd, uint32 *total_entries) +{ + WERROR status = WERR_OK; + DEBUG(5,("init_srv_file_info_ctr: %d\n", __LINE__)); + + ctr->switch_value = switch_value; + + switch (switch_value) { + case 3: + init_srv_file_info_3(&ctr->file.info3, resume_hnd, total_entries); + ctr->ptr_file_ctr = 1; + break; + default: + DEBUG(5,("init_srv_file_info_ctr: unsupported switch value %d\n", switch_value)); + (*resume_hnd = 0); + (*total_entries) = 0; + ctr->ptr_file_ctr = 0; + status = WERR_UNKNOWN_LEVEL; + break; + } + + return status; +} + +/******************************************************************* + makes a SRV_R_NET_FILE_ENUM structure. +********************************************************************/ + +static void init_srv_r_net_file_enum(SRV_R_NET_FILE_ENUM *r_n, + uint32 resume_hnd, int file_level, int switch_value) +{ + DEBUG(5,("init_srv_r_net_file_enum: %d\n", __LINE__)); + + r_n->file_level = file_level; + if (file_level == 0) + r_n->status = WERR_UNKNOWN_LEVEL; + else + r_n->status = init_srv_file_info_ctr(r_n->ctr, switch_value, &resume_hnd, &(r_n->total_entries)); + + if (!W_ERROR_IS_OK(r_n->status)) + resume_hnd = 0; + + init_enum_hnd(&r_n->enum_hnd, resume_hnd); +} + +/******************************************************************* +net server get info +********************************************************************/ + +WERROR _srv_net_srv_get_info(pipes_struct *p, SRV_Q_NET_SRV_GET_INFO *q_u, SRV_R_NET_SRV_GET_INFO *r_u) +{ + WERROR status = WERR_OK; + SRV_INFO_CTR *ctr = (SRV_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_INFO_CTR)); + + if (!ctr) + return WERR_NOMEM; + + ZERO_STRUCTP(ctr); + + DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__)); + + switch (q_u->switch_value) { + case 102: + init_srv_info_102(&ctr->srv.sv102, + 500, global_myname, + string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH), + lp_major_announce_version(), lp_minor_announce_version(), + lp_default_server_announce(), + 0xffffffff, /* users */ + 0xf, /* disc */ + 0, /* hidden */ + 240, /* announce */ + 3000, /* announce delta */ + 100000, /* licenses */ + "c:\\"); /* user path */ + break; + case 101: + init_srv_info_101(&ctr->srv.sv101, + 500, global_myname, + lp_major_announce_version(), lp_minor_announce_version(), + lp_default_server_announce(), + string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH)); + break; + case 100: + init_srv_info_100(&ctr->srv.sv100, 500, global_myname); + break; + default: + status = WERR_UNKNOWN_LEVEL; + break; + } + + /* set up the net server get info structure */ + init_srv_r_net_srv_get_info(r_u, q_u->switch_value, ctr, status); + + DEBUG(5,("srv_net_srv_get_info: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* +net server set info +********************************************************************/ + +WERROR _srv_net_srv_set_info(pipes_struct *p, SRV_Q_NET_SRV_SET_INFO *q_u, SRV_R_NET_SRV_SET_INFO *r_u) +{ + WERROR status = WERR_OK; + + DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__)); + + /* Set up the net server set info structure. */ + + init_srv_r_net_srv_set_info(r_u, 0x0, status); + + DEBUG(5,("srv_net_srv_set_info: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* +net file enum +********************************************************************/ + +WERROR _srv_net_file_enum(pipes_struct *p, SRV_Q_NET_FILE_ENUM *q_u, SRV_R_NET_FILE_ENUM *r_u) +{ + r_u->ctr = (SRV_FILE_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_FILE_INFO_CTR)); + if (!r_u->ctr) + return WERR_NOMEM; + + ZERO_STRUCTP(r_u->ctr); + + DEBUG(5,("srv_net_file_enum: %d\n", __LINE__)); + + /* set up the */ + init_srv_r_net_file_enum(r_u, + get_enum_hnd(&q_u->enum_hnd), + q_u->file_level, + q_u->ctr->switch_value); + + DEBUG(5,("srv_net_file_enum: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* +net conn enum +********************************************************************/ + +WERROR _srv_net_conn_enum(pipes_struct *p, SRV_Q_NET_CONN_ENUM *q_u, SRV_R_NET_CONN_ENUM *r_u) +{ + DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__)); + + r_u->ctr = (SRV_CONN_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_CONN_INFO_CTR)); + if (!r_u->ctr) + return WERR_NOMEM; + + ZERO_STRUCTP(r_u->ctr); + + /* set up the */ + init_srv_r_net_conn_enum(r_u, + get_enum_hnd(&q_u->enum_hnd), + q_u->conn_level, + q_u->ctr->switch_value); + + DEBUG(5,("srv_net_conn_enum: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* +net sess enum +********************************************************************/ + +WERROR _srv_net_sess_enum(pipes_struct *p, SRV_Q_NET_SESS_ENUM *q_u, SRV_R_NET_SESS_ENUM *r_u) +{ + DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__)); + + r_u->ctr = (SRV_SESS_INFO_CTR *)talloc(p->mem_ctx, sizeof(SRV_SESS_INFO_CTR)); + if (!r_u->ctr) + return WERR_NOMEM; + + ZERO_STRUCTP(r_u->ctr); + + /* set up the */ + init_srv_r_net_sess_enum(r_u, + get_enum_hnd(&q_u->enum_hnd), + q_u->sess_level, + q_u->ctr->switch_value); + + DEBUG(5,("_srv_net_sess_enum: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + Net share enum all. +********************************************************************/ + +WERROR _srv_net_share_enum_all(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u) +{ + DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__)); + + /* Create the list of shares for the response. */ + init_srv_r_net_share_enum(p, r_u, + q_u->ctr.info_level, + get_enum_hnd(&q_u->enum_hnd), True); + + DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + Net share enum. +********************************************************************/ + +WERROR _srv_net_share_enum(pipes_struct *p, SRV_Q_NET_SHARE_ENUM *q_u, SRV_R_NET_SHARE_ENUM *r_u) +{ + DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__)); + + /* Create the list of shares for the response. */ + init_srv_r_net_share_enum(p, r_u, + q_u->ctr.info_level, + get_enum_hnd(&q_u->enum_hnd), False); + + DEBUG(5,("_srv_net_share_enum: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + Net share get info. +********************************************************************/ + +WERROR _srv_net_share_get_info(pipes_struct *p, SRV_Q_NET_SHARE_GET_INFO *q_u, SRV_R_NET_SHARE_GET_INFO *r_u) +{ + fstring share_name; + + DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__)); + + /* Create the list of shares for the response. */ + unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name)); + init_srv_r_net_share_get_info(p, r_u, share_name, q_u->info_level); + + DEBUG(5,("_srv_net_share_get_info: %d\n", __LINE__)); + + return r_u->status; +} + +/******************************************************************* + Check a given DOS pathname is valid for a share. +********************************************************************/ + +static char *valid_share_pathname(char *dos_pathname) +{ + pstring saved_pathname; + pstring unix_pathname; + char *ptr; + int ret; + + /* Convert any '\' paths to '/' */ + unix_format(dos_pathname); + unix_clean_name(dos_pathname); + + /* NT is braindead - it wants a C: prefix to a pathname ! So strip it. */ + ptr = dos_pathname; + if (strlen(dos_pathname) > 2 && ptr[1] == ':' && ptr[0] != '/') + ptr += 2; + + /* Only abolute paths allowed. */ + if (*ptr != '/') + return NULL; + + /* Can we cd to it ? */ + + /* First save our current directory. */ + if (getcwd(saved_pathname, sizeof(saved_pathname)) == NULL) + return False; + + pstrcpy(unix_pathname, ptr); + + ret = chdir(unix_pathname); + + /* We *MUST* be able to chdir back. Abort if we can't. */ + if (chdir(saved_pathname) == -1) + smb_panic("valid_share_pathname: Unable to restore current directory.\n"); + + return (ret != -1) ? ptr : NULL; +} + +/******************************************************************* + Net share set info. Modify share details. +********************************************************************/ + +WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, SRV_R_NET_SHARE_SET_INFO *r_u) +{ + struct current_user user; + pstring command; + fstring share_name; + fstring comment; + pstring pathname; + int type; + int snum; + int ret; + char *ptr; + SEC_DESC *psd = NULL; + + DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__)); + + unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name)); + + r_u->switch_value = 0; + + if (strequal(share_name,"IPC$") || 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_INVALID_NAME; + + /* No change to printer shares. */ + if (lp_print_ok(snum)) + return WERR_ACCESS_DENIED; + + get_current_user(&user,p); + + if (user.uid != 0) + return WERR_ACCESS_DENIED; + + switch (q_u->info_level) { + case 1: + /* Not enough info in a level 1 to do anything. */ + return WERR_ACCESS_DENIED; + case 2: + unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(share_name)); + unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(share_name)); + type = q_u->info.share.info2.info_2.type; + psd = NULL; + break; + case 502: + unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(share_name)); + unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(share_name)); + type = q_u->info.share.info502.info_502.type; + psd = q_u->info.share.info502.info_502_str.sd; + map_generic_share_sd_bits(psd); + break; + case 1005: + return WERR_ACCESS_DENIED; + case 1501: + fstrcpy(pathname, lp_pathname(snum)); + fstrcpy(comment, lp_comment(snum)); + psd = q_u->info.share.info1501.sdb->sec; + map_generic_share_sd_bits(psd); + type = STYPE_DISKTREE; + break; + default: + DEBUG(5,("_srv_net_share_set_info: unsupported switch value %d\n", q_u->info_level)); + return WERR_UNKNOWN_LEVEL; + } + + /* We can only modify disk shares. */ + if (type != STYPE_DISKTREE) + return WERR_ACCESS_DENIED; + + /* Check if the pathname is valid. */ + if (!(ptr = valid_share_pathname( pathname ))) + return WERR_OBJECT_PATH_INVALID; + + /* Ensure share name, pathname and comment don't contain '"' characters. */ + string_replace(share_name, '"', ' '); + string_replace(ptr, '"', ' '); + string_replace(comment, '"', ' '); + + DEBUG(10,("_srv_net_share_set_info: change share command = %s\n", + lp_change_share_cmd() ? lp_change_share_cmd() : "NULL" )); + + /* Only call modify function if something changed. */ + + if (strcmp(ptr, lp_pathname(snum)) || strcmp(comment, lp_comment(snum)) ) { + if (!lp_change_share_cmd() || !*lp_change_share_cmd()) + return WERR_ACCESS_DENIED; + + slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"", + lp_change_share_cmd(), dyn_CONFIGFILE, share_name, ptr, comment); + + DEBUG(10,("_srv_net_share_set_info: Running [%s]\n", command )); + if ((ret = smbrun(command, NULL)) != 0) { + DEBUG(0,("_srv_net_share_set_info: Running [%s] returned (%d)\n", command, ret )); + return WERR_ACCESS_DENIED; + } + + /* Tell everyone we updated smb.conf. */ + message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL); + + } else { + DEBUG(10,("_srv_net_share_set_info: 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, snum, &sd_size); + + if (old_sd && !sec_desc_equal(old_sd, psd)) { + if (!set_share_security(p->mem_ctx, share_name, psd)) + DEBUG(0,("_srv_net_share_set_info: Failed to change security info in share %s.\n", + share_name )); + } + } + + DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__)); + + return WERR_OK; +} + +/******************************************************************* + Net share add. Call 'add_share_command "sharename" "pathname" "comment" "read only = xxx"' +********************************************************************/ + +WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_SHARE_ADD *r_u) +{ + struct current_user user; + pstring command; + fstring share_name; + fstring comment; + pstring pathname; + int type; + int snum; + int ret; + char *ptr; + SEC_DESC *psd = NULL; + + DEBUG(5,("_srv_net_share_add: %d\n", __LINE__)); + + r_u->switch_value = 0; + + get_current_user(&user,p); + + if (user.uid != 0) { + DEBUG(10,("_srv_net_share_add: uid != 0. Access denied.\n")); + return WERR_ACCESS_DENIED; + } + + if (!lp_add_share_cmd() || !*lp_add_share_cmd()) { + DEBUG(10,("_srv_net_share_add: No add share command\n")); + return WERR_ACCESS_DENIED; + } + + switch (q_u->info_level) { + case 1: + /* Not enough info in a level 1 to do anything. */ + return WERR_ACCESS_DENIED; + case 2: + unistr2_to_ascii(share_name, &q_u->info.share.info2.info_2_str.uni_netname, sizeof(share_name)); + unistr2_to_ascii(comment, &q_u->info.share.info2.info_2_str.uni_remark, sizeof(share_name)); + unistr2_to_ascii(pathname, &q_u->info.share.info2.info_2_str.uni_path, sizeof(share_name)); + type = q_u->info.share.info2.info_2.type; + break; + case 502: + unistr2_to_ascii(share_name, &q_u->info.share.info502.info_502_str.uni_netname, sizeof(share_name)); + unistr2_to_ascii(comment, &q_u->info.share.info502.info_502_str.uni_remark, sizeof(share_name)); + unistr2_to_ascii(pathname, &q_u->info.share.info502.info_502_str.uni_path, sizeof(share_name)); + type = q_u->info.share.info502.info_502.type; + psd = q_u->info.share.info502.info_502_str.sd; + map_generic_share_sd_bits(psd); + break; + case 1005: + /* DFS only level. */ + return WERR_ACCESS_DENIED; + default: + DEBUG(5,("_srv_net_share_add: unsupported switch value %d\n", q_u->info_level)); + return WERR_UNKNOWN_LEVEL; + } + + if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global")) + 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 (!(ptr = valid_share_pathname( pathname ))) + return WERR_OBJECT_PATH_INVALID; + + /* Ensure share name, pathname and comment don't contain '"' characters. */ + string_replace(share_name, '"', ' '); + string_replace(ptr, '"', ' '); + string_replace(comment, '"', ' '); + + slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"", + lp_add_share_cmd(), dyn_CONFIGFILE, share_name, ptr, comment); + + DEBUG(10,("_srv_net_share_add: Running [%s]\n", command )); + if ((ret = smbrun(command, NULL)) != 0) { + DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret )); + return WERR_ACCESS_DENIED; + } + + if (psd) { + if (!set_share_security(p->mem_ctx, share_name, psd)) + DEBUG(0,("_srv_net_share_add: Failed to add security info to share %s.\n", + share_name )); + } + + /* Tell everyone we updated smb.conf. */ + message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL); + + /* + * 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,("_srv_net_share_add: %d\n", __LINE__)); + + return WERR_OK; +} + +/******************************************************************* + Net share delete. Call "delete share command" with the share name as + a parameter. +********************************************************************/ + +WERROR _srv_net_share_del(pipes_struct *p, SRV_Q_NET_SHARE_DEL *q_u, SRV_R_NET_SHARE_DEL *r_u) +{ + struct current_user user; + pstring command; + fstring share_name; + int ret; + int snum; + + DEBUG(5,("_srv_net_share_del: %d\n", __LINE__)); + + unistr2_to_ascii(share_name, &q_u->uni_share_name, sizeof(share_name)); + + if (strequal(share_name,"IPC$") || strequal(share_name,"ADMIN$") || strequal(share_name,"global")) + return WERR_ACCESS_DENIED; + + snum = find_service(share_name); + + if (snum < 0) + return WERR_NO_SUCH_SHARE; + + /* No change to printer shares. */ + if (lp_print_ok(snum)) + return WERR_ACCESS_DENIED; + + get_current_user(&user,p); + + if (user.uid != 0) + return WERR_ACCESS_DENIED; + + if (!lp_delete_share_cmd() || !*lp_delete_share_cmd()) + return WERR_ACCESS_DENIED; + + slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"", + lp_delete_share_cmd(), dyn_CONFIGFILE, lp_servicename(snum)); + + DEBUG(10,("_srv_net_share_del: Running [%s]\n", command )); + if ((ret = smbrun(command, NULL)) != 0) { + DEBUG(0,("_srv_net_share_del: Running [%s] returned (%d)\n", command, ret )); + return WERR_ACCESS_DENIED; + } + + /* Delete the SD in the database. */ + delete_share_security(snum); + + /* Tell everyone we updated smb.conf. */ + message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL); + + lp_killservice(snum); + + return WERR_OK; +} + +/******************************************************************* +time of day +********************************************************************/ + +WERROR _srv_net_remote_tod(pipes_struct *p, SRV_Q_NET_REMOTE_TOD *q_u, SRV_R_NET_REMOTE_TOD *r_u) +{ + TIME_OF_DAY_INFO *tod; + struct tm *t; + time_t unixdate = time(NULL); + + tod = (TIME_OF_DAY_INFO *)talloc(p->mem_ctx, sizeof(TIME_OF_DAY_INFO)); + if (!tod) + return WERR_NOMEM; + + ZERO_STRUCTP(tod); + + r_u->tod = tod; + r_u->ptr_srv_tod = 0x1; + r_u->status = WERR_OK; + + DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__)); + + t = gmtime(&unixdate); + + /* set up the */ + init_time_of_day_info(tod, + unixdate, + 0, + t->tm_hour, + t->tm_min, + t->tm_sec, + 0, + TimeDiff(unixdate)/60, + 10000, + t->tm_mday, + t->tm_mon + 1, + 1900+t->tm_year, + t->tm_wday); + + DEBUG(5,("_srv_net_remote_tod: %d\n", __LINE__)); + + return r_u->status; +} + +/*********************************************************************************** + Win9x NT tools get security descriptor. +***********************************************************************************/ + +WERROR _srv_net_file_query_secdesc(pipes_struct *p, SRV_Q_NET_FILE_QUERY_SECDESC *q_u, + SRV_R_NET_FILE_QUERY_SECDESC *r_u) +{ + SEC_DESC *psd = NULL; + size_t sd_size; + DATA_BLOB null_pw; + pstring filename; + pstring qualname; + files_struct *fsp = NULL; + SMB_STRUCT_STAT st; + BOOL bad_path; + int access_mode; + int action; + NTSTATUS nt_status; + struct current_user user; + connection_struct *conn = NULL; + BOOL became_user = False; + + ZERO_STRUCT(st); + + r_u->status = WERR_OK; + + unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname)); + + /* Null password is ok - we are already an authenticated user... */ + null_pw = data_blob(NULL, 0); + + get_current_user(&user, p); + + become_root(); + conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status); + unbecome_root(); + + if (conn == NULL) { + DEBUG(3,("_srv_net_file_query_secdesc: Unable to connect to %s\n", qualname)); + r_u->status = ntstatus_to_werror(nt_status); + goto error_exit; + } + + if (!become_user(conn, conn->vuid)) { + DEBUG(0,("_srv_net_file_query_secdesc: Can't become connected user!\n")); + r_u->status = WERR_ACCESS_DENIED; + goto error_exit; + } + became_user = True; + + unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename)); + unix_convert(filename, conn, NULL, &bad_path, &st); + fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &action); + + if (!fsp) { + /* Perhaps it is a directory */ + if (errno == EISDIR) + fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0, + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, &action); + + if (!fsp) { + DEBUG(3,("_srv_net_file_query_secdesc: Unable to open file %s\n", filename)); + r_u->status = WERR_ACCESS_DENIED; + goto error_exit; + } + } + + sd_size = conn->vfs_ops.get_nt_acl(fsp, fsp->fsp_name, &psd); + + if (sd_size == 0) { + DEBUG(3,("_srv_net_file_query_secdesc: Unable to get NT ACL for file %s\n", filename)); + r_u->status = WERR_ACCESS_DENIED; + goto error_exit; + } + + r_u->ptr_response = 1; + r_u->size_response = sd_size; + r_u->ptr_secdesc = 1; + r_u->size_secdesc = sd_size; + r_u->sec_desc = psd; + + psd->dacl->revision = (uint16) NT4_ACL_REVISION; + + close_file(fsp, True); + unbecome_user(); + close_cnum(conn, user.vuid); + return r_u->status; + + error_exit: + + if(fsp) { + close_file(fsp, True); + } + + if (became_user) + unbecome_user(); + + if (conn) + close_cnum(conn, user.vuid); + + return r_u->status; +} + +/*********************************************************************************** + Win9x NT tools set security descriptor. +***********************************************************************************/ + +WERROR _srv_net_file_set_secdesc(pipes_struct *p, SRV_Q_NET_FILE_SET_SECDESC *q_u, + SRV_R_NET_FILE_SET_SECDESC *r_u) +{ + BOOL ret; + pstring filename; + pstring qualname; + DATA_BLOB null_pw; + files_struct *fsp = NULL; + SMB_STRUCT_STAT st; + BOOL bad_path; + int access_mode; + int action; + NTSTATUS nt_status; + struct current_user user; + connection_struct *conn = NULL; + BOOL became_user = False; + + ZERO_STRUCT(st); + + r_u->status = WERR_OK; + + unistr2_to_ascii(qualname, &q_u->uni_qual_name, sizeof(qualname)); + + /* Null password is ok - we are already an authenticated user... */ + null_pw = data_blob(NULL, 0); + + get_current_user(&user, p); + + become_root(); + conn = make_connection(qualname, null_pw, "A:", user.vuid, &nt_status); + unbecome_root(); + + if (conn == NULL) { + DEBUG(3,("_srv_net_file_set_secdesc: Unable to connect to %s\n", qualname)); + r_u->status = ntstatus_to_werror(nt_status); + goto error_exit; + } + + if (!become_user(conn, conn->vuid)) { + DEBUG(0,("_srv_net_file_set_secdesc: Can't become connected user!\n")); + r_u->status = WERR_ACCESS_DENIED; + goto error_exit; + } + became_user = True; + + unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename)); + unix_convert(filename, conn, NULL, &bad_path, &st); + + fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDWR), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, 0, &access_mode, &action); + + if (!fsp) { + /* Perhaps it is a directory */ + if (errno == EISDIR) + fsp = open_directory(conn, filename, &st,FILE_READ_ATTRIBUTES,0, + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), 0, &action); + + if (!fsp) { + DEBUG(3,("_srv_net_file_set_secdesc: Unable to open file %s\n", filename)); + r_u->status = WERR_ACCESS_DENIED; + goto error_exit; + } + } + + ret = conn->vfs_ops.set_nt_acl(fsp, fsp->fsp_name, q_u->sec_info, q_u->sec_desc); + + if (ret == False) { + DEBUG(3,("_srv_net_file_set_secdesc: Unable to set NT ACL on file %s\n", filename)); + r_u->status = WERR_ACCESS_DENIED; + goto error_exit; + } + + close_file(fsp, True); + unbecome_user(); + close_cnum(conn, user.vuid); + return r_u->status; + + error_exit: + + if(fsp) { + close_file(fsp, True); + } + + if (became_user) + unbecome_user(); + + if (conn) + close_cnum(conn, user.vuid); + + return r_u->status; +} + +/*********************************************************************************** + 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>. +***********************************************************************************/ + +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; +} + +WERROR _srv_net_disk_enum(pipes_struct *p, SRV_Q_NET_DISK_ENUM *q_u, SRV_R_NET_DISK_ENUM *r_u) +{ + uint32 i; + const char *disk_name; + uint32 resume=get_enum_hnd(&q_u->enum_hnd); + + r_u->status=WERR_OK; + + r_u->total_entries = init_server_disk_enum(&resume); + + r_u->disk_enum_ctr.unknown = 0; + + r_u->disk_enum_ctr.disk_info_ptr = r_u->disk_enum_ctr.disk_info ? 1 : 0; + + /*allow one DISK_INFO for null terminator*/ + + for(i = 0; i < MAX_SERVER_DISK_ENTRIES -1 && (disk_name = next_server_disk_enum(&resume)); i++) { + + r_u->disk_enum_ctr.entries_read++; + + /*copy disk name into a unicode string*/ + + init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, disk_name); + } + + /*add a terminating null string. Is this there if there is more data to come?*/ + + r_u->disk_enum_ctr.entries_read++; + + init_unistr3(&r_u->disk_enum_ctr.disk_info[i].disk_name, ""); + + init_enum_hnd(&r_u->enum_hnd, resume); + + return r_u->status; +} + +WERROR _srv_net_name_validate(pipes_struct *p, SRV_Q_NET_NAME_VALIDATE *q_u, SRV_R_NET_NAME_VALIDATE *r_u) +{ + int snum; + fstring share_name; + + r_u->status=WERR_OK; + + switch(q_u->type) { + + case 0x9: + + /*check if share name is ok*/ + /*also check if we already have a share with this name*/ + + unistr2_to_ascii(share_name, &q_u->uni_name, sizeof(share_name)); + snum = find_service(share_name); + + /* Share already exists. */ + if (snum >= 0) + r_u->status = WERR_ALREADY_EXISTS; + break; + + default: + /*unsupported type*/ + r_u->status = WERR_UNKNOWN_LEVEL; + break; + } + + return r_u->status; +} diff --git a/source3/rpc_server/srv_util.c b/source3/rpc_server/srv_util.c new file mode 100644 index 0000000000..53bbebb95e --- /dev/null +++ b/source3/rpc_server/srv_util.c @@ -0,0 +1,514 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* 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" + +/* + * A list of the rids of well known BUILTIN and Domain users + * and groups. + */ + +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. */ +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. */ +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 } +}; + +/******************************************************************* + gets a domain user's groups + ********************************************************************/ +NTSTATUS get_alias_user_groups(TALLOC_CTX *ctx, DOM_SID *sid, int *numgroups, uint32 **prids, DOM_SID *q_sid) +{ + SAM_ACCOUNT *sam_pass=NULL; + struct sys_grent *glist; + struct sys_grent *grp; + int i, num, cur_rid=0; + gid_t gid; + GROUP_MAP map; + DOM_SID tmp_sid; + fstring user_name; + fstring str_domsid, str_qsid; + uint32 rid,grid; + uint32 *rids=NULL, *new_rids=NULL; + gid_t winbind_gid_low, winbind_gid_high; + BOOL ret; + + /* + * this code is far from perfect. + * first it enumerates the full /etc/group and that can be slow. + * second, it works only with users' SIDs + * whereas the day we support nested groups, it will have to + * support both users's SIDs and domain groups' SIDs + * + * having our own ldap backend would be so much faster ! + * we're far from that, but hope one day ;-) JFM. + */ + + *prids=NULL; + *numgroups=0; + + lp_winbind_gid(&winbind_gid_low, &winbind_gid_high); + + + DEBUG(10,("get_alias_user_groups: looking if SID %s is a member of groups in the SID domain %s\n", + sid_to_string(str_qsid, q_sid), sid_to_string(str_domsid, sid))); + + sid_peek_rid(q_sid, &rid); + + pdb_init_sam(&sam_pass); + become_root(); + ret = pdb_getsampwrid(sam_pass, rid); + unbecome_root(); + if (ret == False) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + fstrcpy(user_name, pdb_get_username(sam_pass)); + grid=pdb_get_group_rid(sam_pass); + gid=pdb_get_gid(sam_pass); + + grp = glist = getgrent_list(); + if (grp == NULL) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + + for (; grp != NULL; grp = grp->next) { + if(!get_group_from_gid(grp->gr_gid, &map, MAPPING_WITHOUT_PRIV)) { + DEBUG(10,("get_alias_user_groups: gid %d. not found\n", (int)grp->gr_gid)); + continue; + } + + /* if it's not an alias, continue */ + if (map.sid_name_use!=SID_NAME_ALIAS) { + DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name)); + continue; + } + + sid_copy(&tmp_sid, &map.sid); + sid_split_rid(&tmp_sid, &rid); + + /* if the sid is not in the correct domain, continue */ + if (!sid_equal(&tmp_sid, sid)) { + DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name)); + continue; + } + + /* Don't return winbind groups as they are not local! */ + if ((grp->gr_gid >= winbind_gid_low) && (grp->gr_gid <= winbind_gid_high)) { + DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name)); + continue; + } + + /* Don't return user private groups... */ + if (Get_Pwnam(map.nt_name) != 0) { + DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name)); + continue; + } + + /* the group is fine, we can check if there is the user we're looking for */ + DEBUG(10,("get_alias_user_groups: checking if the user is a member of %s.\n", map.nt_name)); + + for(num=0; grp->gr_mem[num]!=NULL; num++) { + if(strcmp(grp->gr_mem[num], user_name)==0) { + /* we found the user, add the group to the list */ + + new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1)); + if (new_rids==NULL) { + DEBUG(10,("get_alias_user_groups: could not realloc memory\n")); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + rids=new_rids; + + sid_peek_rid(&map.sid, &(rids[cur_rid])); + DEBUG(10,("get_alias_user_groups: user found in group %s\n", map.nt_name)); + cur_rid++; + break; + } + } + } + + grent_free(glist); + + /* now check for the user's gid (the primary group rid) */ + for (i=0; i<cur_rid && grid!=rids[i]; i++) + ; + + /* the user's gid is already there */ + if (i!=cur_rid) { + DEBUG(10,("get_alias_user_groups: user is already in the list. good.\n")); + goto done; + } + + DEBUG(10,("get_alias_user_groups: looking for gid %d of user %s\n", (int)gid, user_name)); + + if(!get_group_from_gid(gid, &map, MAPPING_WITHOUT_PRIV)) { + DEBUG(0,("get_alias_user_groups: gid of user %s doesn't exist. Check your /etc/passwd and /etc/group files\n", user_name)); + goto done; + } + + /* the primary group isn't an alias */ + if (map.sid_name_use!=SID_NAME_ALIAS) { + DEBUG(10,("get_alias_user_groups: not returing %s, not an ALIAS group.\n", map.nt_name)); + goto done; + } + + sid_copy(&tmp_sid, &map.sid); + sid_split_rid(&tmp_sid, &rid); + + /* if the sid is not in the correct domain, continue */ + if (!sid_equal(&tmp_sid, sid)) { + DEBUG(10,("get_alias_user_groups: not returing %s, not in the domain SID.\n", map.nt_name)); + goto done; + } + + /* Don't return winbind groups as they are not local! */ + if ((gid >= winbind_gid_low) && (gid <= winbind_gid_high)) { + DEBUG(10,("get_alias_user_groups: not returing %s, not local.\n", map.nt_name )); + goto done; + } + + /* Don't return user private groups... */ + if (Get_Pwnam(map.nt_name) != 0) { + DEBUG(10,("get_alias_user_groups: not returing %s, clashes with user.\n", map.nt_name )); + goto done; + } + + new_rids=(uint32 *)Realloc(rids, sizeof(uint32)*(cur_rid+1)); + if (new_rids==NULL) { + DEBUG(10,("get_alias_user_groups: could not realloc memory\n")); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + rids=new_rids; + + sid_peek_rid(&map.sid, &(rids[cur_rid])); + cur_rid++; + +done: + *prids=rids; + *numgroups=cur_rid; + pdb_free_sam(&sam_pass); + + return NT_STATUS_OK; +} + + +/******************************************************************* + gets a domain user's groups + ********************************************************************/ +BOOL get_domain_user_groups(TALLOC_CTX *ctx, int *numgroups, DOM_GID **pgids, SAM_ACCOUNT *sam_pass) +{ + GROUP_MAP *map=NULL; + int i, num, num_entries, cur_gid=0; + struct group *grp; + DOM_GID *gids; + fstring user_name; + uint32 grid; + uint32 tmp_rid; + + *numgroups= 0; + + fstrcpy(user_name, pdb_get_username(sam_pass)); + grid=pdb_get_group_rid(sam_pass); + + DEBUG(10,("get_domain_user_groups: searching domain groups [%s] is a member of\n", user_name)); + + /* first get the list of the domain groups */ + if (!enum_group_mapping(SID_NAME_DOM_GRP, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) + return False; + DEBUG(10,("get_domain_user_groups: there are %d mapped groups\n", num_entries)); + + /* + * alloc memory. In the worse case, we alloc memory for nothing. + * but I prefer to alloc for nothing + * than reallocing everytime. + */ + gids = (DOM_GID *)talloc(ctx, sizeof(DOM_GID) * num_entries); + + /* for each group, check if the user is a member of*/ + for(i=0; i<num_entries; i++) { + if ((grp=getgrgid(map[i].gid)) == NULL) { + /* very weird !!! */ + DEBUG(5,("get_domain_user_groups: gid %d doesn't exist anymore !\n", (int)map[i].gid)); + continue; + } + + for(num=0; grp->gr_mem[num]!=NULL; num++) { + if(strcmp(grp->gr_mem[num], user_name)==0) { + /* we found the user, add the group to the list */ + sid_peek_rid(&map[i].sid, &(gids[cur_gid].g_rid)); + gids[cur_gid].attr=7; + DEBUG(10,("get_domain_user_groups: user found in group %s\n", map[i].nt_name)); + cur_gid++; + break; + } + } + } + + /* we have checked the groups */ + /* we must now check the gid of the user or the primary group rid, that's the same */ + for (i=0; i<cur_gid && grid!=gids[i].g_rid; i++) + ; + + /* the user's gid is already there */ + if (i!=cur_gid) { + /* + * the primary group of the user but be the first one in the list + * don't ask ! JFM. + */ + gids[i].g_rid=gids[0].g_rid; + gids[0].g_rid=grid; + goto done; + } + + for(i=0; i<num_entries; i++) { + sid_peek_rid(&map[i].sid, &tmp_rid); + if (tmp_rid==grid) { + /* + * the primary group of the user but be the first one in the list + * don't ask ! JFM. + */ + gids[cur_gid].g_rid=gids[0].g_rid; + gids[0].g_rid=tmp_rid; + gids[cur_gid].attr=7; + DEBUG(10,("get_domain_user_groups: primary gid of user found in group %s\n", map[i].nt_name)); + cur_gid++; + goto done; /* leave the loop early */ + } + } + + DEBUG(0,("get_domain_user_groups: primary gid of user [%s] is not a Domain group !\n", user_name)); + DEBUGADD(0,("get_domain_user_groups: You should fix it, NT doesn't like that\n")); + + done: + *pgids=gids; + *numgroups=cur_gid; + safe_free(map); + + return True; +} + +/******************************************************************* + Look up a local (domain) rid and return a name and type. + ********************************************************************/ +NTSTATUS local_lookup_group_name(uint32 rid, char *group_name, uint32 *type) +{ + int i = 0; + (*type) = SID_NAME_DOM_GRP; + + DEBUG(5,("lookup_group_name: rid: %d", rid)); + + while (domain_group_rids[i].rid != rid && domain_group_rids[i].rid != 0) + { + i++; + } + + if (domain_group_rids[i].rid != 0) + { + fstrcpy(group_name, domain_group_rids[i].name); + DEBUG(5,(" = %s\n", group_name)); + return NT_STATUS_OK; + } + + DEBUG(5,(" none mapped\n")); + return NT_STATUS_NONE_MAPPED; +} + +/******************************************************************* + Look up a local alias rid and return a name and type. + ********************************************************************/ +NTSTATUS local_lookup_alias_name(uint32 rid, char *alias_name, uint32 *type) +{ + int i = 0; + (*type) = SID_NAME_WKN_GRP; + + DEBUG(5,("lookup_alias_name: rid: %d", rid)); + + while (builtin_alias_rids[i].rid != rid && builtin_alias_rids[i].rid != 0) + { + i++; + } + + if (builtin_alias_rids[i].rid != 0) + { + fstrcpy(alias_name, builtin_alias_rids[i].name); + DEBUG(5,(" = %s\n", alias_name)); + return NT_STATUS_OK; + } + + DEBUG(5,(" none mapped\n")); + return NT_STATUS_NONE_MAPPED; +} + +/******************************************************************* + Look up a local user rid and return a name and type. + ********************************************************************/ +NTSTATUS local_lookup_user_name(uint32 rid, char *user_name, uint32 *type) +{ + SAM_ACCOUNT *sampwd=NULL; + int i = 0; + BOOL ret; + + (*type) = SID_NAME_USER; + + DEBUG(5,("lookup_user_name: rid: %d", rid)); + + /* look up the well-known domain user rids first */ + while (domain_user_rids[i].rid != rid && domain_user_rids[i].rid != 0) + { + i++; + } + + if (domain_user_rids[i].rid != 0) { + fstrcpy(user_name, domain_user_rids[i].name); + DEBUG(5,(" = %s\n", user_name)); + return NT_STATUS_OK; + } + + pdb_init_sam(&sampwd); + + /* ok, it's a user. find the user account */ + become_root(); + ret = pdb_getsampwrid(sampwd, rid); + unbecome_root(); + + if (ret == True) { + fstrcpy(user_name, pdb_get_username(sampwd) ); + DEBUG(5,(" = %s\n", user_name)); + pdb_free_sam(&sampwd); + return NT_STATUS_OK; + } + + DEBUG(5,(" none mapped\n")); + pdb_free_sam(&sampwd); + return NT_STATUS_NONE_MAPPED; +} + +/******************************************************************* + Look up a local (domain) group name and return a rid + ********************************************************************/ +NTSTATUS local_lookup_group_rid(char *group_name, uint32 *rid) +{ + char *grp_name; + int i = -1; /* start do loop at -1 */ + + do /* find, if it exists, a group rid for the group name*/ + { + i++; + (*rid) = domain_group_rids[i].rid; + grp_name = domain_group_rids[i].name; + + } while (grp_name != NULL && !strequal(grp_name, group_name)); + + return (grp_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED; +} + +/******************************************************************* + Look up a local (BUILTIN) alias name and return a rid + ********************************************************************/ +NTSTATUS local_lookup_alias_rid(char *alias_name, uint32 *rid) +{ + char *als_name; + int i = -1; /* start do loop at -1 */ + + do /* find, if it exists, a alias rid for the alias name*/ + { + i++; + (*rid) = builtin_alias_rids[i].rid; + als_name = builtin_alias_rids[i].name; + + } while (als_name != NULL && !strequal(als_name, alias_name)); + + return (als_name != NULL) ? NT_STATUS_OK : NT_STATUS_NONE_MAPPED; +} + +/******************************************************************* + Look up a local user name and return a rid + ********************************************************************/ +NTSTATUS local_lookup_user_rid(char *user_name, uint32 *rid) +{ + SAM_ACCOUNT *sampass=NULL; + BOOL ret; + + (*rid) = 0; + + pdb_init_sam(&sampass); + + /* find the user account */ + become_root(); + ret = pdb_getsampwnam(sampass, user_name); + unbecome_root(); + + if (ret == True) { + (*rid) = pdb_get_user_rid(sampass); + pdb_free_sam(&sampass); + return NT_STATUS_OK; + } + + pdb_free_sam(&sampass); + return NT_STATUS_NONE_MAPPED; +} diff --git a/source3/rpc_server/srv_wkssvc.c b/source3/rpc_server/srv_wkssvc.c new file mode 100644 index 0000000000..8eb5b3002e --- /dev/null +++ b/source3/rpc_server/srv_wkssvc.c @@ -0,0 +1,70 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the interface to the wks pipe. */ + +#include "includes.h" + +/******************************************************************* + api_wks_query_info + ********************************************************************/ + +static BOOL api_wks_query_info(pipes_struct *p) +{ + WKS_Q_QUERY_INFO q_u; + WKS_R_QUERY_INFO r_u; + prs_struct *data = &p->in_data.data; + prs_struct *rdata = &p->out_data.rdata; + + ZERO_STRUCT(q_u); + ZERO_STRUCT(r_u); + + /* grab the net share enum */ + if(!wks_io_q_query_info("", &q_u, data, 0)) + return False; + + r_u.status = _wks_query_info(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!wks_io_r_query_info("", &r_u, rdata, 0)) + return False; + + return True; +} + + +/******************************************************************* + \PIPE\wkssvc commands + ********************************************************************/ +struct api_struct api_wks_cmds[] = +{ + { "WKS_Q_QUERY_INFO", WKS_QUERY_INFO, api_wks_query_info }, + { NULL , 0 , NULL } +}; + +/******************************************************************* + receives a wkssvc pipe and responds. + ********************************************************************/ +BOOL api_wkssvc_rpc(pipes_struct *p) +{ + return api_rpcTNP(p, "api_wkssvc_rpc", api_wks_cmds); +} diff --git a/source3/rpc_server/srv_wkssvc_nt.c b/source3/rpc_server/srv_wkssvc_nt.c new file mode 100644 index 0000000000..637c95af69 --- /dev/null +++ b/source3/rpc_server/srv_wkssvc_nt.c @@ -0,0 +1,78 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This is the implementation of the wks interface. */ + +#include "includes.h" + +extern pstring global_myname; + +/******************************************************************* + create_wks_info_100 + ********************************************************************/ + +static void create_wks_info_100(WKS_INFO_100 *inf) +{ + pstring my_name; + pstring domain; + + DEBUG(5,("create_wks_info_100: %d\n", __LINE__)); + + pstrcpy (my_name, global_myname); + strupper(my_name); + + pstrcpy (domain, lp_workgroup()); + strupper(domain); + + init_wks_info_100(inf, + 0x000001f4, /* platform id info */ + lp_major_announce_version(), + lp_minor_announce_version(), + my_name, domain); +} + +/******************************************************************* + wks_reply_query_info + + only supports info level 100 at the moment. + + ********************************************************************/ + +NTSTATUS _wks_query_info(pipes_struct *p, WKS_Q_QUERY_INFO *q_u, WKS_R_QUERY_INFO *r_u) +{ + WKS_INFO_100 *wks100 = NULL; + + DEBUG(5,("_wks_query_info: %d\n", __LINE__)); + + wks100 = (WKS_INFO_100 *)talloc_zero(p->mem_ctx, sizeof(WKS_INFO_100)); + + if (!wks100) + return NT_STATUS_NO_MEMORY; + + create_wks_info_100(wks100); + init_wks_r_query_info(r_u, q_u->switch_value, wks100, NT_STATUS_OK); + + DEBUG(5,("_wks_query_info: %d\n", __LINE__)); + + return r_u->status; +} |