diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source3/smbd/smb1_trans2.c | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/smbd/smb1_trans2.c')
-rw-r--r-- | source3/smbd/smb1_trans2.c | 3457 |
1 files changed, 3457 insertions, 0 deletions
diff --git a/source3/smbd/smb1_trans2.c b/source3/smbd/smb1_trans2.c new file mode 100644 index 0000000..4b3c781 --- /dev/null +++ b/source3/smbd/smb1_trans2.c @@ -0,0 +1,3457 @@ +/* + Unix SMB/CIFS implementation. + SMB transaction2 handling + Copyright (C) Jeremy Allison 1994-2007 + Copyright (C) Stefan (metze) Metzmacher 2003 + Copyright (C) Volker Lendecke 2005-2007 + Copyright (C) Steve French 2005 + Copyright (C) James Peach 2006-2007 + + Extensively modified by Andrew Tridgell, 1995 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "ntioctl.h" +#include "system/filesys.h" +#include "lib/util/time_basic.h" +#include "version.h" +#include "smbd/smbd.h" +#include "smbd/globals.h" +#include "../libcli/auth/libcli_auth.h" +#include "../librpc/gen_ndr/xattr.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "trans2.h" +#include "auth.h" +#include "smbprofile.h" +#include "rpc_server/srv_pipe_hnd.h" +#include "printing.h" +#include "lib/util_ea.h" +#include "lib/readdir_attr.h" +#include "messages.h" +#include "libcli/smb/smb2_posix.h" +#include "lib/util/string_wrappers.h" +#include "source3/lib/substitute.h" +#include "source3/lib/adouble.h" + +#define DIR_ENTRY_SAFETY_MARGIN 4096 + +/**************************************************************************** + Send the required number of replies back. + We assume all fields other than the data fields are + set correctly for the type of call. + HACK ! Always assumes smb_setup field is zero. +****************************************************************************/ + +void send_trans2_replies(connection_struct *conn, + struct smb_request *req, + NTSTATUS status, + const char *params, + int paramsize, + const char *pdata, + int datasize, + int max_data_bytes) +{ + /* As we are using a protocol > LANMAN1 then the max_send + variable must have been set in the sessetupX call. + This takes precedence over the max_xmit field in the + global struct. These different max_xmit variables should + be merged as this is now too confusing */ + + int data_to_send = datasize; + int params_to_send = paramsize; + int useable_space; + const char *pp = params; + const char *pd = pdata; + int params_sent_thistime, data_sent_thistime, total_sent_thistime; + int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */ + int data_alignment_offset = 0; + bool overflow = False; + struct smbXsrv_connection *xconn = req->xconn; + int max_send = xconn->smb1.sessions.max_send; + + /* Modify the data_to_send and datasize and set the error if + we're trying to send more than max_data_bytes. We still send + the part of the packet(s) that fit. Strange, but needed + for OS/2. */ + + if (max_data_bytes > 0 && datasize > max_data_bytes) { + DEBUG(5,("send_trans2_replies: max_data_bytes %d exceeded by data %d\n", + max_data_bytes, datasize )); + datasize = data_to_send = max_data_bytes; + overflow = True; + } + + /* If there genuinely are no parameters or data to send just send the empty packet */ + + if(params_to_send == 0 && data_to_send == 0) { + reply_smb1_outbuf(req, 10, 0); + if (NT_STATUS_V(status)) { + uint8_t eclass; + uint32_t ecode; + ntstatus_to_dos(status, &eclass, &ecode); + error_packet_set((char *)req->outbuf, + eclass, ecode, status, + __LINE__,__FILE__); + } + show_msg((char *)req->outbuf); + if (!smb1_srv_send(xconn, + (char *)req->outbuf, + true, req->seqnum+1, + IS_CONN_ENCRYPTED(conn), + &req->pcd)) { + exit_server_cleanly("send_trans2_replies: smb1_srv_send failed."); + } + TALLOC_FREE(req->outbuf); + return; + } + + /* When sending params and data ensure that both are nicely aligned */ + /* Only do this alignment when there is also data to send - else + can cause NT redirector problems. */ + + if (((params_to_send % 4) != 0) && (data_to_send != 0)) + data_alignment_offset = 4 - (params_to_send % 4); + + /* Space is bufsize minus Netbios over TCP header minus SMB header */ + /* The alignment_offset is to align the param bytes on an even byte + boundary. NT 4.0 Beta needs this to work correctly. */ + + useable_space = max_send - (smb_size + + 2 * 10 /* wct */ + + alignment_offset + + data_alignment_offset); + + if (useable_space < 0) { + DEBUG(0, ("send_trans2_replies failed sanity useable_space " + "= %d!!!", useable_space)); + exit_server_cleanly("send_trans2_replies: Not enough space"); + } + + while (params_to_send || data_to_send) { + /* Calculate whether we will totally or partially fill this packet */ + + total_sent_thistime = params_to_send + data_to_send; + + /* We can never send more than useable_space */ + /* + * Note that 'useable_space' does not include the alignment offsets, + * but we must include the alignment offsets in the calculation of + * the length of the data we send over the wire, as the alignment offsets + * are sent here. Fix from Marc_Jacobsen@hp.com. + */ + + total_sent_thistime = MIN(total_sent_thistime, useable_space); + + reply_smb1_outbuf(req, 10, total_sent_thistime + alignment_offset + + data_alignment_offset); + + /* Set total params and data to be sent */ + SSVAL(req->outbuf,smb_tprcnt,paramsize); + SSVAL(req->outbuf,smb_tdrcnt,datasize); + + /* Calculate how many parameters and data we can fit into + * this packet. Parameters get precedence + */ + + params_sent_thistime = MIN(params_to_send,useable_space); + data_sent_thistime = useable_space - params_sent_thistime; + data_sent_thistime = MIN(data_sent_thistime,data_to_send); + + SSVAL(req->outbuf,smb_prcnt, params_sent_thistime); + + /* smb_proff is the offset from the start of the SMB header to the + parameter bytes, however the first 4 bytes of outbuf are + the Netbios over TCP header. Thus use smb_base() to subtract + them from the calculation */ + + SSVAL(req->outbuf,smb_proff, + ((smb_buf(req->outbuf)+alignment_offset) + - smb_base(req->outbuf))); + + if(params_sent_thistime == 0) + SSVAL(req->outbuf,smb_prdisp,0); + else + /* Absolute displacement of param bytes sent in this packet */ + SSVAL(req->outbuf,smb_prdisp,pp - params); + + SSVAL(req->outbuf,smb_drcnt, data_sent_thistime); + if(data_sent_thistime == 0) { + SSVAL(req->outbuf,smb_droff,0); + SSVAL(req->outbuf,smb_drdisp, 0); + } else { + /* The offset of the data bytes is the offset of the + parameter bytes plus the number of parameters being sent this time */ + SSVAL(req->outbuf, smb_droff, + ((smb_buf(req->outbuf)+alignment_offset) + - smb_base(req->outbuf)) + + params_sent_thistime + data_alignment_offset); + SSVAL(req->outbuf,smb_drdisp, pd - pdata); + } + + /* Initialize the padding for alignment */ + + if (alignment_offset != 0) { + memset(smb_buf(req->outbuf), 0, alignment_offset); + } + + /* Copy the param bytes into the packet */ + + if(params_sent_thistime) { + memcpy((smb_buf(req->outbuf)+alignment_offset), pp, + params_sent_thistime); + } + + /* Copy in the data bytes */ + if(data_sent_thistime) { + if (data_alignment_offset != 0) { + memset((smb_buf(req->outbuf)+alignment_offset+ + params_sent_thistime), 0, + data_alignment_offset); + } + memcpy(smb_buf(req->outbuf)+alignment_offset + +params_sent_thistime+data_alignment_offset, + pd,data_sent_thistime); + } + + DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n", + params_sent_thistime, data_sent_thistime, useable_space)); + DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n", + params_to_send, data_to_send, paramsize, datasize)); + + if (overflow) { + error_packet_set((char *)req->outbuf, + ERRDOS,ERRbufferoverflow, + STATUS_BUFFER_OVERFLOW, + __LINE__,__FILE__); + } else if (NT_STATUS_V(status)) { + uint8_t eclass; + uint32_t ecode; + ntstatus_to_dos(status, &eclass, &ecode); + error_packet_set((char *)req->outbuf, + eclass, ecode, status, + __LINE__,__FILE__); + } + + /* Send the packet */ + show_msg((char *)req->outbuf); + if (!smb1_srv_send(xconn, + (char *)req->outbuf, + true, req->seqnum+1, + IS_CONN_ENCRYPTED(conn), + &req->pcd)) + exit_server_cleanly("send_trans2_replies: smb1_srv_send failed."); + + TALLOC_FREE(req->outbuf); + + pp += params_sent_thistime; + pd += data_sent_thistime; + + params_to_send -= params_sent_thistime; + data_to_send -= data_sent_thistime; + + /* Sanity check */ + if(params_to_send < 0 || data_to_send < 0) { + DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!", + params_to_send, data_to_send)); + return; + } + } + + return; +} + +/**************************************************************************** + Deal with SMB_SET_POSIX_LOCK. +****************************************************************************/ + +static void smb_set_posix_lock_done(struct tevent_req *subreq); + +NTSTATUS smb_set_posix_lock(connection_struct *conn, + struct smb_request *req, + const char *pdata, + int total_data, + files_struct *fsp) +{ + struct tevent_req *subreq = NULL; + struct smbd_lock_element *lck = NULL; + uint64_t count; + uint64_t offset; + uint64_t smblctx; + bool blocking_lock = False; + enum brl_type lock_type; + + NTSTATUS status = NT_STATUS_OK; + + if (fsp == NULL || + fsp->fsp_flags.is_pathref || + fsp_get_io_fd(fsp) == -1) + { + return NT_STATUS_INVALID_HANDLE; + } + + if (total_data != POSIX_LOCK_DATA_SIZE) { + return NT_STATUS_INVALID_PARAMETER; + } + + switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) { + case POSIX_LOCK_TYPE_READ: + lock_type = READ_LOCK; + break; + case POSIX_LOCK_TYPE_WRITE: + /* Return the right POSIX-mappable error code for files opened read-only. */ + if (!fsp->fsp_flags.can_write) { + return NT_STATUS_INVALID_HANDLE; + } + lock_type = WRITE_LOCK; + break; + case POSIX_LOCK_TYPE_UNLOCK: + lock_type = UNLOCK_LOCK; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + switch (SVAL(pdata, POSIX_LOCK_FLAGS_OFFSET)) { + case POSIX_LOCK_FLAG_NOWAIT: + blocking_lock = false; + break; + case POSIX_LOCK_FLAG_WAIT: + blocking_lock = true; + break; + default: + return NT_STATUS_INVALID_PARAMETER; + } + + if (!lp_blocking_locks(SNUM(conn))) { + blocking_lock = False; + } + + smblctx = (uint64_t)IVAL(pdata, POSIX_LOCK_PID_OFFSET); + offset = (((uint64_t) IVAL(pdata,(POSIX_LOCK_START_OFFSET+4))) << 32) | + ((uint64_t) IVAL(pdata,POSIX_LOCK_START_OFFSET)); + count = (((uint64_t) IVAL(pdata,(POSIX_LOCK_LEN_OFFSET+4))) << 32) | + ((uint64_t) IVAL(pdata,POSIX_LOCK_LEN_OFFSET)); + + DBG_DEBUG("file %s, lock_type = %u, smblctx = %"PRIu64", " + "count = %"PRIu64", offset = %"PRIu64"\n", + fsp_str_dbg(fsp), + (unsigned int)lock_type, + smblctx, + count, + offset); + + if (lock_type == UNLOCK_LOCK) { + struct smbd_lock_element l = { + .req_guid = smbd_request_guid(req, 0), + .smblctx = smblctx, + .brltype = UNLOCK_LOCK, + .lock_flav = POSIX_LOCK, + .offset = offset, + .count = count, + }; + status = smbd_do_unlocking(req, fsp, 1, &l); + return status; + } + + lck = talloc(req, struct smbd_lock_element); + if (lck == NULL) { + return NT_STATUS_NO_MEMORY; + } + + *lck = (struct smbd_lock_element) { + .req_guid = smbd_request_guid(req, 0), + .smblctx = smblctx, + .brltype = lock_type, + .lock_flav = POSIX_LOCK, + .count = count, + .offset = offset, + }; + + subreq = smbd_smb1_do_locks_send( + fsp, + req->sconn->ev_ctx, + &req, + fsp, + blocking_lock ? UINT32_MAX : 0, + true, /* large_offset */ + 1, + lck); + if (subreq == NULL) { + TALLOC_FREE(lck); + return NT_STATUS_NO_MEMORY; + } + tevent_req_set_callback(subreq, smb_set_posix_lock_done, req); + return NT_STATUS_EVENT_PENDING; +} + +static void smb_set_posix_lock_done(struct tevent_req *subreq) +{ + struct smb_request *req = NULL; + NTSTATUS status; + bool ok; + + ok = smbd_smb1_do_locks_extract_smbreq(subreq, talloc_tos(), &req); + SMB_ASSERT(ok); + + status = smbd_smb1_do_locks_recv(subreq); + TALLOC_FREE(subreq); + + if (NT_STATUS_IS_OK(status)) { + char params[2] = {0}; + /* Fake up max_data_bytes here - we know it fits. */ + send_trans2_replies( + req->conn, + req, + NT_STATUS_OK, + params, + 2, + NULL, + 0, + 0xffff); + } else { + reply_nterror(req, status); + ok = smb1_srv_send( + req->xconn, + (char *)req->outbuf, + true, + req->seqnum+1, + IS_CONN_ENCRYPTED(req->conn), + NULL); + if (!ok) { + exit_server_cleanly("smb_set_posix_lock_done: " + "smb1_srv_send failed."); + } + } + + TALLOC_FREE(req); + return; +} + +/**************************************************************************** + Read a list of EA names from an incoming data buffer. Create an ea_list with them. +****************************************************************************/ + +static struct ea_list *read_ea_name_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) +{ + struct ea_list *ea_list_head = NULL; + size_t converted_size, offset = 0; + + while (offset + 2 < data_size) { + struct ea_list *eal = talloc_zero(ctx, struct ea_list); + unsigned int namelen = CVAL(pdata,offset); + + offset++; /* Go past the namelen byte. */ + + /* integer wrap paranioa. */ + if ((offset + namelen < offset) || (offset + namelen < namelen) || + (offset > data_size) || (namelen > data_size) || + (offset + namelen >= data_size)) { + break; + } + /* Ensure the name is null terminated. */ + if (pdata[offset + namelen] != '\0') { + return NULL; + } + if (!pull_ascii_talloc(ctx, &eal->ea.name, &pdata[offset], + &converted_size)) { + DEBUG(0,("read_ea_name_list: pull_ascii_talloc " + "failed: %s", strerror(errno))); + } + if (!eal->ea.name) { + return NULL; + } + + offset += (namelen + 1); /* Go past the name + terminating zero. */ + DLIST_ADD_END(ea_list_head, eal); + DEBUG(10,("read_ea_name_list: read ea name %s\n", eal->ea.name)); + } + + return ea_list_head; +} + +/**************************************************************************** + Reply to a TRANSACT2_OPEN. +****************************************************************************/ + +static void call_trans2open(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + struct smb_filename *smb_fname = NULL; + char *params = *pparams; + char *pdata = *ppdata; + int deny_mode; + int32_t open_attr; + bool oplock_request; +#if 0 + bool return_additional_info; + int16 open_sattr; + time_t open_time; +#endif + int open_ofun; + uint32_t open_size; + char *pname; + char *fname = NULL; + off_t size=0; + int fattr=0,mtime=0; + SMB_INO_T inode = 0; + int smb_action = 0; + struct files_struct *dirfsp = NULL; + files_struct *fsp; + struct ea_list *ea_list = NULL; + uint16_t flags = 0; + NTSTATUS status; + uint32_t access_mask; + uint32_t share_mode; + uint32_t create_disposition; + uint32_t create_options = 0; + uint32_t private_flags = 0; + NTTIME twrp = 0; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + TALLOC_CTX *ctx = talloc_tos(); + + /* + * Ensure we have enough parameters to perform the operation. + */ + + if (total_params < 29) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + flags = SVAL(params, 0); + deny_mode = SVAL(params, 2); + open_attr = SVAL(params,6); + oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; + if (oplock_request) { + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + } + +#if 0 + return_additional_info = BITSETW(params,0); + open_sattr = SVAL(params, 4); + open_time = make_unix_date3(params+8); +#endif + open_ofun = SVAL(params,12); + open_size = IVAL(params,14); + pname = ¶ms[28]; + + if (IS_IPC(conn)) { + reply_nterror(req, NT_STATUS_NETWORK_ACCESS_DENIED); + goto out; + } + + if (req->posix_pathnames) { + srvstr_get_path_posix(ctx, + params, + req->flags2, + &fname, + pname, + total_params - 28, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(ctx, + params, + req->flags2, + &fname, + pname, + total_params - 28, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + goto out; + } + + DEBUG(3,("call_trans2open %s deny_mode=0x%x attr=%d ofun=0x%x size=%d\n", + fname, (unsigned int)deny_mode, (unsigned int)open_attr, + (unsigned int)open_ofun, open_size)); + + if (ucf_flags & UCF_GMT_PATHNAME) { + extract_snapshot_token(fname, &twrp); + } + status = filename_convert_dirfsp(ctx, + conn, + fname, + ucf_flags, + twrp, + &dirfsp, + &smb_fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + goto out; + } + reply_nterror(req, status); + goto out; + } + + if (open_ofun == 0) { + reply_nterror(req, NT_STATUS_OBJECT_NAME_COLLISION); + goto out; + } + + if (!map_open_params_to_ntcreate(smb_fname->base_name, deny_mode, + open_ofun, + &access_mask, &share_mode, + &create_disposition, + &create_options, + &private_flags)) { + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + goto out; + } + + /* Any data in this call is an EA list. */ + if (total_data && (total_data != 4)) { + if (total_data < 10) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + if (IVAL(pdata,0) > total_data) { + DEBUG(10,("call_trans2open: bad total data size (%u) > %u\n", + IVAL(pdata,0), (unsigned int)total_data)); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + ea_list = read_ea_list(talloc_tos(), pdata + 4, + total_data - 4); + if (!ea_list) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + if (!lp_ea_support(SNUM(conn))) { + reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED); + goto out; + } + + if (!req->posix_pathnames && + ea_list_has_invalid_name(ea_list)) { + int param_len = 30; + *pparams = (char *)SMB_REALLOC(*pparams, param_len); + if(*pparams == NULL ) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + params = *pparams; + memset(params, '\0', param_len); + send_trans2_replies(conn, req, STATUS_INVALID_EA_NAME, + params, param_len, NULL, 0, max_data_bytes); + goto out; + } + } + + status = SMB_VFS_CREATE_FILE( + conn, /* conn */ + req, /* req */ + dirfsp, /* dirfsp */ + smb_fname, /* fname */ + access_mask, /* access_mask */ + share_mode, /* share_access */ + create_disposition, /* create_disposition*/ + create_options, /* create_options */ + open_attr, /* file_attributes */ + oplock_request, /* oplock_request */ + NULL, /* lease */ + open_size, /* allocation_size */ + private_flags, + NULL, /* sd */ + ea_list, /* ea_list */ + &fsp, /* result */ + &smb_action, /* psbuf */ + NULL, NULL); /* create context */ + + if (!NT_STATUS_IS_OK(status)) { + if (open_was_deferred(req->xconn, req->mid)) { + /* We have re-scheduled this call. */ + goto out; + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + reply_openerror(req, status); + goto out; + } + + fsp = fcb_or_dos_open( + req, + smb_fname, + access_mask, + create_options, + private_flags); + if (fsp == NULL) { + bool ok = defer_smb1_sharing_violation(req); + if (ok) { + goto out; + } + reply_openerror(req, status); + goto out; + } + + smb_action = FILE_WAS_OPENED; + } + + size = get_file_size_stat(&smb_fname->st); + fattr = fdos_mode(fsp); + mtime = convert_timespec_to_time_t(smb_fname->st.st_ex_mtime); + inode = smb_fname->st.st_ex_ino; + if (fattr & FILE_ATTRIBUTE_DIRECTORY) { + close_file_free(req, &fsp, ERROR_CLOSE); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + goto out; + } + + /* Realloc the size of parameters and data we will return */ + *pparams = (char *)SMB_REALLOC(*pparams, 30); + if(*pparams == NULL ) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + params = *pparams; + + SSVAL(params,0,fsp->fnum); + SSVAL(params,2,fattr); + srv_put_dos_date2(params,4, mtime); + SIVAL(params,8, (uint32_t)size); + SSVAL(params,12,deny_mode); + SSVAL(params,14,0); /* open_type - file or directory. */ + SSVAL(params,16,0); /* open_state - only valid for IPC device. */ + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + } + + SSVAL(params,18,smb_action); + + /* + * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes. + */ + SIVAL(params,20,inode); + SSVAL(params,24,0); /* Padding. */ + if (flags & 8) { + uint32_t ea_size = estimate_ea_size(smb_fname->fsp); + SIVAL(params, 26, ea_size); + } else { + SIVAL(params, 26, 0); + } + + /* Send the required number of replies */ + send_trans2_replies(conn, req, NT_STATUS_OK, params, 30, *ppdata, 0, max_data_bytes); + out: + TALLOC_FREE(smb_fname); +} + +static NTSTATUS get_lanman2_dir_entry(TALLOC_CTX *ctx, + connection_struct *conn, + struct dptr_struct *dirptr, + uint16_t flags2, + const char *path_mask, + uint32_t dirtype, + int info_level, + bool requires_resume_key, + bool dont_descend, + bool ask_sharemode, + char **ppdata, + char *base_data, + char *end_data, + int space_remaining, + bool *got_exact_match, + int *last_entry_off, + struct ea_list *name_list) +{ + uint8_t align = 4; + const bool do_pad = true; + + if (info_level >= 1 && info_level <= 3) { + /* No alignment on earlier info levels. */ + align = 1; + } + + return smbd_dirptr_lanman2_entry(ctx, conn, dirptr, flags2, + path_mask, dirtype, info_level, + requires_resume_key, dont_descend, ask_sharemode, + true, align, do_pad, + ppdata, base_data, end_data, + space_remaining, + NULL, + got_exact_match, + last_entry_off, name_list, NULL); +} + +/**************************************************************************** + Reply to a TRANS2_FINDFIRST. +****************************************************************************/ + +static void call_trans2findfirst(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + /* We must be careful here that we don't return more than the + allowed number of data bytes. If this means returning fewer than + maxentries then so be it. We assume that the redirector has + enough room for the fixed number of parameter bytes it has + requested. */ + struct smb_filename *smb_dname = NULL; + char *params = *pparams; + char *pdata = *ppdata; + char *data_end; + uint32_t dirtype; + int maxentries; + uint16_t findfirst_flags; + bool close_after_first; + bool close_if_end; + bool requires_resume_key; + int info_level; + char *directory = NULL; + char *mask = NULL; + char *p; + int last_entry_off=0; + int dptr_num = -1; + int numentries = 0; + int i; + bool finished = False; + bool dont_descend = False; + bool out_of_space = False; + int space_remaining; + struct ea_list *ea_list = NULL; + NTSTATUS ntstatus = NT_STATUS_OK; + bool ask_sharemode = lp_smbd_search_ask_sharemode(SNUM(conn)); + struct smbd_server_connection *sconn = req->sconn; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + bool backup_priv = false; + bool as_root = false; + files_struct *fsp = NULL; + struct files_struct *dirfsp = NULL; + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + if (total_params < 13) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + dirtype = SVAL(params,0); + maxentries = SVAL(params,2); + findfirst_flags = SVAL(params,4); + close_after_first = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE); + close_if_end = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE_IF_END); + requires_resume_key = (findfirst_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME); + backup_priv = ((findfirst_flags & FLAG_TRANS2_FIND_BACKUP_INTENT) && + security_token_has_privilege(get_current_nttok(conn), + SEC_PRIV_BACKUP)); + + info_level = SVAL(params,6); + + DBG_NOTICE("dirtype = %"PRIx32", maxentries = %d, " + "close_after_first=%d, close_if_end = %d " + "requires_resume_key = %d backup_priv = %d level = 0x%x, " + "max_data_bytes = %d\n", + dirtype, + maxentries, + close_after_first, + close_if_end, + requires_resume_key, + backup_priv, + info_level, + max_data_bytes); + + if (!maxentries) { + /* W2K3 seems to treat zero as 1. */ + maxentries = 1; + } + + switch (info_level) { + case SMB_FIND_INFO_STANDARD: + case SMB_FIND_EA_SIZE: + case SMB_FIND_EA_LIST: + case SMB_FIND_FILE_DIRECTORY_INFO: + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + case SMB_FIND_FILE_NAMES_INFO: + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + case SMB_FIND_ID_FULL_DIRECTORY_INFO: + case SMB_FIND_ID_BOTH_DIRECTORY_INFO: + break; + case SMB_FIND_FILE_UNIX: + case SMB_FIND_FILE_UNIX_INFO2: + if (!lp_smb1_unix_extensions()) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + goto out; + } + if (!req->posix_pathnames) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + goto out; + } + break; + default: + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + goto out; + } + + if (req->posix_pathnames) { + /* Always use filesystem for UNIX mtime query. */ + ask_sharemode = false; + } + + if (req->posix_pathnames) { + srvstr_get_path_posix(talloc_tos(), + params, + req->flags2, + &directory, + params+12, + total_params - 12, + STR_TERMINATE, + &ntstatus); + } else { + srvstr_get_path(talloc_tos(), + params, + req->flags2, + &directory, + params+12, + total_params - 12, + STR_TERMINATE, + &ntstatus); + } + if (!NT_STATUS_IS_OK(ntstatus)) { + reply_nterror(req, ntstatus); + goto out; + } + + if (backup_priv) { + become_root(); + as_root = true; + } + ntstatus = filename_convert_smb1_search_path(talloc_tos(), + conn, + directory, + ucf_flags, + &dirfsp, + &smb_dname, + &mask); + + if (!NT_STATUS_IS_OK(ntstatus)) { + if (NT_STATUS_EQUAL(ntstatus,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + goto out; + } + reply_nterror(req, ntstatus); + goto out; + } + + TALLOC_FREE(directory); + directory = smb_dname->base_name; + + DEBUG(5,("dir=%s, mask = %s\n",directory, mask)); + + if (info_level == SMB_FIND_EA_LIST) { + uint32_t ea_size; + + if (total_data < 4) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + ea_size = IVAL(pdata,0); + if (ea_size != total_data) { + DEBUG(4,("call_trans2findfirst: Rejecting EA request with incorrect \ +total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) )); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + if (!lp_ea_support(SNUM(conn))) { + reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED); + goto out; + } + + /* Pull out the list of names. */ + ea_list = read_ea_name_list(talloc_tos(), pdata + 4, ea_size - 4); + if (!ea_list) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + } + + if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + *ppdata = (char *)SMB_REALLOC( + *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN); + if(*ppdata == NULL ) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + pdata = *ppdata; + data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1; + /* + * squash valgrind "writev(vector[...]) points to uninitialised byte(s)" + * error. + */ + memset(pdata + total_data, 0, ((max_data_bytes + DIR_ENTRY_SAFETY_MARGIN) - total_data)); + /* Realloc the params space */ + *pparams = (char *)SMB_REALLOC(*pparams, 10); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + params = *pparams; + + /* + * Open an fsp on this directory for the dptr. + */ + ntstatus = SMB_VFS_CREATE_FILE( + conn, /* conn */ + req, /* req */ + dirfsp, /* dirfsp */ + smb_dname, /* dname */ + FILE_LIST_DIRECTORY, /* access_mask */ + FILE_SHARE_READ| + FILE_SHARE_WRITE, /* share_access */ + FILE_OPEN, /* create_disposition*/ + FILE_DIRECTORY_FILE, /* create_options */ + FILE_ATTRIBUTE_DIRECTORY,/* file_attributes */ + NO_OPLOCK, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + NULL, /* in_context */ + NULL);/* out_context */ + + if (!NT_STATUS_IS_OK(ntstatus)) { + DBG_ERR("failed to open directory %s\n", + smb_fname_str_dbg(smb_dname)); + reply_nterror(req, ntstatus); + goto out; + } + + /* Save the wildcard match and attribs we are using on this directory - + needed as lanman2 assumes these are being saved between calls */ + + ntstatus = dptr_create(conn, + req, + fsp, /* fsp */ + False, + True, + req->smbpid, + mask, + dirtype, + &fsp->dptr); + + if (!NT_STATUS_IS_OK(ntstatus)) { + /* + * Use NULL here for the first parameter (req) + * as this is not a client visible handle so + * can'tbe part of an SMB1 chain. + */ + close_file_free(NULL, &fsp, NORMAL_CLOSE); + reply_nterror(req, ntstatus); + goto out; + } + + if (backup_priv) { + /* Remember this in case we have + to do a findnext. */ + dptr_set_priv(fsp->dptr); + } + + dptr_num = dptr_dnum(fsp->dptr); + DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n", dptr_num, mask, dirtype)); + + /* We don't need to check for VOL here as this is returned by + a different TRANS2 call. */ + + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", + directory,lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)))); + if (in_list(directory, + lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)), + dptr_case_sensitive(fsp->dptr))) { + dont_descend = True; + } + + p = pdata; + space_remaining = max_data_bytes; + out_of_space = False; + + for (i=0;(i<maxentries) && !finished && !out_of_space;i++) { + bool got_exact_match = False; + + /* this is a heuristic to avoid seeking the dirptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) { + out_of_space = True; + finished = False; + } else { + ntstatus = get_lanman2_dir_entry(talloc_tos(), + conn, + fsp->dptr, + req->flags2, + mask,dirtype,info_level, + requires_resume_key,dont_descend, + ask_sharemode, + &p,pdata,data_end, + space_remaining, + &got_exact_match, + &last_entry_off, ea_list); + if (NT_STATUS_EQUAL(ntstatus, + NT_STATUS_ILLEGAL_CHARACTER)) { + /* + * Bad character conversion on name. Ignore this + * entry. + */ + continue; + } + if (NT_STATUS_EQUAL(ntstatus, STATUS_MORE_ENTRIES)) { + out_of_space = true; + } else { + finished = !NT_STATUS_IS_OK(ntstatus); + } + } + + if (!finished && !out_of_space) + numentries++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + /* Ensure space_remaining never goes -ve. */ + if (PTR_DIFF(p,pdata) > max_data_bytes) { + space_remaining = 0; + out_of_space = true; + } else { + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } + } + + /* Check if we can close the dirptr */ + if(close_after_first || (finished && close_if_end)) { + DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); + dptr_num = -1; + close_file_free(NULL, &fsp, NORMAL_CLOSE); + } + + /* + * If there are no matching entries we must return ERRDOS/ERRbadfile - + * from observation of NT. NB. This changes to ERRDOS,ERRnofiles if + * the protocol level is less than NT1. Tested with smbclient. JRA. + * This should fix the OS/2 client bug #2335. + */ + + if(numentries == 0) { + dptr_num = -1; + /* + * We may have already closed the file in the + * close_after_first or finished case above. + */ + if (fsp != NULL) { + close_file_free(NULL, &fsp, NORMAL_CLOSE); + } + if (get_Protocol() < PROTOCOL_NT1) { + reply_force_doserror(req, ERRDOS, ERRnofiles); + goto out; + } else { + reply_botherror(req, NT_STATUS_NO_SUCH_FILE, + ERRDOS, ERRbadfile); + goto out; + } + } + + /* At this point pdata points to numentries directory entries. */ + + /* Set up the return parameter block */ + SSVAL(params,0,dptr_num); + SSVAL(params,2,numentries); + SSVAL(params,4,finished); + SSVAL(params,6,0); /* Never an EA error */ + SSVAL(params,8,last_entry_off); + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 10, pdata, PTR_DIFF(p,pdata), + max_data_bytes); + + if ((! *directory) && dptr_path(sconn, dptr_num)) { + directory = talloc_strdup(talloc_tos(),dptr_path(sconn, dptr_num)); + if (!directory) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + } + } + + DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n", + smb_fn_name(req->cmd), + mask, directory, dirtype, numentries ) ); + + /* + * Force a name mangle here to ensure that the + * mask as an 8.3 name is top of the mangled cache. + * The reasons for this are subtle. Don't remove + * this code unless you know what you are doing + * (see PR#13758). JRA. + */ + + if(!mangle_is_8_3_wildcards( mask, False, conn->params)) { + char mangled_name[13]; + name_to_8_3(mask, mangled_name, True, conn->params); + } + out: + + if (as_root) { + unbecome_root(); + } + + TALLOC_FREE(smb_dname); + return; +} + +/**************************************************************************** + Reply to a TRANS2_FINDNEXT. +****************************************************************************/ + +static void call_trans2findnext(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + /* We must be careful here that we don't return more than the + allowed number of data bytes. If this means returning fewer than + maxentries then so be it. We assume that the redirector has + enough room for the fixed number of parameter bytes it has + requested. */ + char *params = *pparams; + char *pdata = *ppdata; + char *data_end; + int dptr_num; + int maxentries; + uint16_t info_level; + uint32_t resume_key; + uint16_t findnext_flags; + bool close_after_request; + bool close_if_end; + bool requires_resume_key; + bool continue_bit; + char *resume_name = NULL; + const char *mask = NULL; + const char *directory = NULL; + char *p = NULL; + uint16_t dirtype; + int numentries = 0; + int i, last_entry_off=0; + bool finished = False; + bool dont_descend = False; + bool out_of_space = False; + int space_remaining; + struct ea_list *ea_list = NULL; + NTSTATUS ntstatus = NT_STATUS_OK; + bool ask_sharemode = lp_smbd_search_ask_sharemode(SNUM(conn)); + TALLOC_CTX *ctx = talloc_tos(); + struct smbd_server_connection *sconn = req->sconn; + bool backup_priv = false; + bool as_root = false; + files_struct *fsp = NULL; + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + if (total_params < 13) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + dptr_num = SVAL(params,0); + maxentries = SVAL(params,2); + info_level = SVAL(params,4); + resume_key = IVAL(params,6); + findnext_flags = SVAL(params,10); + close_after_request = (findnext_flags & FLAG_TRANS2_FIND_CLOSE); + close_if_end = (findnext_flags & FLAG_TRANS2_FIND_CLOSE_IF_END); + requires_resume_key = (findnext_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME); + continue_bit = (findnext_flags & FLAG_TRANS2_FIND_CONTINUE); + + if (!continue_bit) { + /* We only need resume_name if continue_bit is zero. */ + if (req->posix_pathnames) { + srvstr_get_path_posix(ctx, + params, + req->flags2, + &resume_name, + params+12, + total_params - 12, + STR_TERMINATE, + &ntstatus); + } else { + srvstr_get_path(ctx, + params, + req->flags2, + &resume_name, + params+12, + total_params - 12, + STR_TERMINATE, + &ntstatus); + } + if (!NT_STATUS_IS_OK(ntstatus)) { + /* Win9x or OS/2 can send a resume name of ".." or ".". This will cause the parser to + complain (it thinks we're asking for the directory above the shared + path or an invalid name). Catch this as the resume name is only compared, never used in + a file access. JRA. */ + srvstr_pull_talloc(ctx, params, req->flags2, + &resume_name, params+12, + total_params - 12, + STR_TERMINATE); + + if (!resume_name || !(ISDOT(resume_name) || ISDOTDOT(resume_name))) { + reply_nterror(req, ntstatus); + return; + } + } + } + + DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \ +close_after_request=%d, close_if_end = %d requires_resume_key = %d \ +resume_key = %d resume name = %s continue=%d level = %d\n", + dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end, + requires_resume_key, resume_key, + resume_name ? resume_name : "(NULL)", continue_bit, info_level)); + + if (!maxentries) { + /* W2K3 seems to treat zero as 1. */ + maxentries = 1; + } + + switch (info_level) { + case SMB_FIND_INFO_STANDARD: + case SMB_FIND_EA_SIZE: + case SMB_FIND_EA_LIST: + case SMB_FIND_FILE_DIRECTORY_INFO: + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + case SMB_FIND_FILE_NAMES_INFO: + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + case SMB_FIND_ID_FULL_DIRECTORY_INFO: + case SMB_FIND_ID_BOTH_DIRECTORY_INFO: + break; + case SMB_FIND_FILE_UNIX: + case SMB_FIND_FILE_UNIX_INFO2: + /* Always use filesystem for UNIX mtime query. */ + ask_sharemode = false; + if (!lp_smb1_unix_extensions()) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + if (!req->posix_pathnames) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + break; + default: + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + + if (info_level == SMB_FIND_EA_LIST) { + uint32_t ea_size; + + if (total_data < 4) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + ea_size = IVAL(pdata,0); + if (ea_size != total_data) { + DEBUG(4,("call_trans2findnext: Rejecting EA request with incorrect \ +total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) )); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (!lp_ea_support(SNUM(conn))) { + reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED); + return; + } + + /* Pull out the list of names. */ + ea_list = read_ea_name_list(ctx, pdata + 4, ea_size - 4); + if (!ea_list) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + } + + if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + *ppdata = (char *)SMB_REALLOC( + *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN); + if(*ppdata == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + + pdata = *ppdata; + data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1; + + /* + * squash valgrind "writev(vector[...]) points to uninitialised byte(s)" + * error. + */ + memset(pdata + total_data, 0, (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN) - total_data); + /* Realloc the params space */ + *pparams = (char *)SMB_REALLOC(*pparams, 6*SIZEOFWORD); + if(*pparams == NULL ) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + + params = *pparams; + + /* Check that the dptr is valid */ + fsp = dptr_fetch_lanman2_fsp(sconn, dptr_num); + if (fsp == NULL) { + reply_nterror(req, STATUS_NO_MORE_FILES); + return; + } + + directory = dptr_path(sconn, dptr_num); + + /* Get the wildcard mask from the dptr */ + if((mask = dptr_wcard(sconn, dptr_num))== NULL) { + DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num)); + reply_nterror(req, STATUS_NO_MORE_FILES); + return; + } + + /* Get the attr mask from the dptr */ + dirtype = dptr_attr(sconn, dptr_num); + + backup_priv = dptr_get_priv(fsp->dptr); + + DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%ld) " + "backup_priv = %d\n", + dptr_num, mask, dirtype, + (long)fsp->dptr, + dptr_TellDir(fsp->dptr), + (int)backup_priv)); + + /* We don't need to check for VOL here as this is returned by + a different TRANS2 call. */ + + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", + directory,lp_dont_descend(ctx, lp_sub, SNUM(conn)))); + if (in_list(directory,lp_dont_descend(ctx, lp_sub, SNUM(conn)), + dptr_case_sensitive(fsp->dptr))) + dont_descend = True; + + p = pdata; + space_remaining = max_data_bytes; + out_of_space = False; + + if (backup_priv) { + become_root(); + as_root = true; + } + + /* + * Seek to the correct position. We no longer use the resume key but + * depend on the last file name instead. + */ + + if(!continue_bit && resume_name && *resume_name) { + SMB_STRUCT_STAT st; + bool posix_open = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN); + + long current_pos = 0; + /* + * Remember, name_to_8_3 is called by + * get_lanman2_dir_entry(), so the resume name + * could be mangled. Ensure we check the unmangled name. + */ + + if (!posix_open && + mangle_is_mangled(resume_name, conn->params)) { + char *new_resume_name = NULL; + mangle_lookup_name_from_8_3(ctx, + resume_name, + &new_resume_name, + conn->params); + if (new_resume_name) { + resume_name = new_resume_name; + } + } + + /* + * Fix for NT redirector problem triggered by resume key indexes + * changing between directory scans. We now return a resume key of 0 + * and instead look for the filename to continue from (also given + * to us by NT/95/smbfs/smbclient). If no other scans have been done between the + * findfirst/findnext (as is usual) then the directory pointer + * should already be at the correct place. + */ + + finished = !dptr_SearchDir(fsp->dptr, resume_name, ¤t_pos, &st); + } /* end if resume_name && !continue_bit */ + + for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) { + bool got_exact_match = False; + + /* this is a heuristic to avoid seeking the fsp->dptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) { + out_of_space = True; + finished = False; + } else { + ntstatus = get_lanman2_dir_entry(ctx, + conn, + fsp->dptr, + req->flags2, + mask,dirtype,info_level, + requires_resume_key,dont_descend, + ask_sharemode, + &p,pdata,data_end, + space_remaining, + &got_exact_match, + &last_entry_off, ea_list); + if (NT_STATUS_EQUAL(ntstatus, + NT_STATUS_ILLEGAL_CHARACTER)) { + /* + * Bad character conversion on name. Ignore this + * entry. + */ + continue; + } + if (NT_STATUS_EQUAL(ntstatus, STATUS_MORE_ENTRIES)) { + out_of_space = true; + } else { + finished = !NT_STATUS_IS_OK(ntstatus); + } + } + + if (!finished && !out_of_space) + numentries++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } + + DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n", + smb_fn_name(req->cmd), + mask, directory, dirtype, numentries ) ); + + /* Check if we can close the fsp->dptr */ + if(close_after_request || (finished && close_if_end)) { + DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); + dptr_num = -1; + close_file_free(NULL, &fsp, NORMAL_CLOSE); + } + + if (as_root) { + unbecome_root(); + } + + /* Set up the return parameter block */ + SSVAL(params,0,numentries); + SSVAL(params,2,finished); + SSVAL(params,4,0); /* Never an EA error */ + SSVAL(params,6,last_entry_off); + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 8, pdata, PTR_DIFF(p,pdata), + max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_QFSINFO (query filesystem info). +****************************************************************************/ + +static void call_trans2qfsinfo(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + uint16_t info_level; + int data_len = 0; + size_t fixed_portion; + NTSTATUS status; + + if (total_params < 2) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + info_level = SVAL(params,0); + + if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) { + if (info_level != SMB_QUERY_CIFS_UNIX_INFO) { + DEBUG(0,("call_trans2qfsinfo: encryption required " + "and info level 0x%x sent.\n", + (unsigned int)info_level)); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level)); + + status = smbd_do_qfsinfo(req->xconn, conn, req, + info_level, + req->flags2, + max_data_bytes, + &fixed_portion, + NULL, + ppdata, &data_len); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 0, *ppdata, data_len, + max_data_bytes); + + DEBUG( 4, ( "%s info_level = %d\n", + smb_fn_name(req->cmd), info_level) ); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_SETFSINFO (set filesystem info). +****************************************************************************/ + +static void call_trans2setfsinfo(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + struct smbXsrv_connection *xconn = req->xconn; + char *pdata = *ppdata; + char *params = *pparams; + uint16_t info_level; + + DEBUG(10,("call_trans2setfsinfo: for service [%s]\n", + lp_servicename(talloc_tos(), lp_sub, SNUM(conn)))); + + /* */ + if (total_params < 4) { + DEBUG(0,("call_trans2setfsinfo: requires total_params(%d) >= 4 bytes!\n", + total_params)); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + info_level = SVAL(params,2); + + if (IS_IPC(conn)) { + if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION && + info_level != SMB_SET_CIFS_UNIX_INFO) { + DEBUG(0,("call_trans2setfsinfo: not an allowed " + "info level (0x%x) on IPC$.\n", + (unsigned int)info_level)); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) { + if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION) { + DEBUG(0,("call_trans2setfsinfo: encryption required " + "and info level 0x%x sent.\n", + (unsigned int)info_level)); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + switch(info_level) { + case SMB_SET_CIFS_UNIX_INFO: + if (!lp_smb1_unix_extensions()) { + DEBUG(2,("call_trans2setfsinfo: " + "SMB_SET_CIFS_UNIX_INFO is invalid with " + "unix extensions off\n")); + reply_nterror(req, + NT_STATUS_INVALID_LEVEL); + return; + } + + /* There should be 12 bytes of capabilities set. */ + if (total_data < 12) { + reply_nterror( + req, + NT_STATUS_INVALID_PARAMETER); + return; + } + xconn->smb1.unix_info.client_major = SVAL(pdata,0); + xconn->smb1.unix_info.client_minor = SVAL(pdata,2); + xconn->smb1.unix_info.client_cap_low = IVAL(pdata,4); + xconn->smb1.unix_info.client_cap_high = IVAL(pdata,8); + /* Just print these values for now. */ + DEBUG(10, ("call_trans2setfsinfo: set unix_info info. " + "major = %u, minor = %u cap_low = 0x%x, " + "cap_high = 0x%xn", + (unsigned int)xconn-> + smb1.unix_info.client_major, + (unsigned int)xconn-> + smb1.unix_info.client_minor, + (unsigned int)xconn-> + smb1.unix_info.client_cap_low, + (unsigned int)xconn-> + smb1.unix_info.client_cap_high)); + + /* Here is where we must switch to posix pathname processing... */ + if (xconn->smb1.unix_info.client_cap_low & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + lp_set_posix_pathnames(); + mangle_change_to_posix(); + } + + if ((xconn->smb1.unix_info.client_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP) && + !(xconn->smb1.unix_info.client_cap_low & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP)) { + /* Client that knows how to do posix locks, + * but not posix open/mkdir operations. Set a + * default type for read/write checks. */ + + lp_set_posix_default_cifsx_readwrite_locktype(POSIX_LOCK); + + } + break; + + case SMB_REQUEST_TRANSPORT_ENCRYPTION: + { + NTSTATUS status; + size_t param_len = 0; + size_t data_len = total_data; + + if (!lp_smb1_unix_extensions()) { + reply_nterror( + req, + NT_STATUS_INVALID_LEVEL); + return; + } + + if (lp_server_smb_encrypt(SNUM(conn)) == + SMB_ENCRYPTION_OFF) { + reply_nterror( + req, + NT_STATUS_NOT_SUPPORTED); + return; + } + + if (xconn->smb1.echo_handler.trusted_fde) { + DEBUG( 2,("call_trans2setfsinfo: " + "request transport encryption disabled" + "with 'fork echo handler = yes'\n")); + reply_nterror( + req, + NT_STATUS_NOT_SUPPORTED); + return; + } + + DEBUG( 4,("call_trans2setfsinfo: " + "request transport encryption.\n")); + + status = srv_request_encryption_setup(conn, + (unsigned char **)ppdata, + &data_len, + (unsigned char **)pparams, + ¶m_len); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + send_trans2_replies(conn, req, + NT_STATUS_OK, + *pparams, + param_len, + *ppdata, + data_len, + max_data_bytes); + + if (NT_STATUS_IS_OK(status)) { + /* Server-side transport + * encryption is now *on*. */ + status = srv_encryption_start(conn); + if (!NT_STATUS_IS_OK(status)) { + char *reason = talloc_asprintf(talloc_tos(), + "Failure in setting " + "up encrypted transport: %s", + nt_errstr(status)); + exit_server_cleanly(reason); + } + } + return; + } + + case SMB_FS_QUOTA_INFORMATION: + { + NTSTATUS status; + DATA_BLOB qdata = { + .data = (uint8_t *)pdata, + .length = total_data + }; + files_struct *fsp = NULL; + fsp = file_fsp(req, SVAL(params,0)); + + status = smb_set_fsquota(conn, + req, + fsp, + &qdata); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + break; + } + default: + DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n", + info_level)); + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + break; + } + + /* + * sending this reply works fine, + * but I'm not sure it's the same + * like windows do... + * --metze + */ + reply_smb1_outbuf(req, 10, 0); +} + +/**************************************************************************** + Reply to a TRANSACT2_QFILEINFO on a PIPE ! +****************************************************************************/ + +static void call_trans2qpipeinfo(connection_struct *conn, + struct smb_request *req, + files_struct *fsp, + uint16_t info_level, + unsigned int tran_call, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + char *pdata = *ppdata; + unsigned int data_size = 0; + unsigned int param_size = 2; + + if (!fsp_is_np(fsp)) { + reply_nterror(req, NT_STATUS_INVALID_HANDLE); + return; + } + + *pparams = (char *)SMB_REALLOC(*pparams,2); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + params = *pparams; + SSVAL(params,0,0); + if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN; + *ppdata = (char *)SMB_REALLOC(*ppdata, data_size); + if (*ppdata == NULL ) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + pdata = *ppdata; + + switch (info_level) { + case SMB_FILE_STANDARD_INFORMATION: + memset(pdata,0,24); + SOFF_T(pdata,0,4096LL); + SIVAL(pdata,16,1); + SIVAL(pdata,20,1); + data_size = 24; + break; + + default: + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + + send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size, + max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by + file name or file id). +****************************************************************************/ + +static void call_trans2qfilepathinfo(connection_struct *conn, + struct smb_request *req, + unsigned int tran_call, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + char *pdata = *ppdata; + uint16_t info_level; + unsigned int data_size = 0; + unsigned int param_size = 2; + struct smb_filename *smb_fname = NULL; + bool delete_pending = False; + struct timespec write_time_ts; + struct files_struct *dirfsp = NULL; + files_struct *fsp = NULL; + struct file_id fileid; + struct ea_list *ea_list = NULL; + int lock_data_count = 0; + char *lock_data = NULL; + size_t fixed_portion; + NTSTATUS status = NT_STATUS_OK; + int ret; + + if (!params) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + ZERO_STRUCT(write_time_ts); + + if (tran_call == TRANSACT2_QFILEINFO) { + if (total_params < 4) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + fsp = file_fsp(req, SVAL(params,0)); + info_level = SVAL(params,2); + + if (IS_IPC(conn)) { + call_trans2qpipeinfo( + conn, + req, + fsp, + info_level, + tran_call, + pparams, + total_params, + ppdata, + total_data, + max_data_bytes); + return; + } + + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level)); + + if (INFO_LEVEL_IS_UNIX(info_level)) { + if (!lp_smb1_unix_extensions()) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + if (!req->posix_pathnames) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + } + + /* Initial check for valid fsp ptr. */ + if (!check_fsp_open(conn, req, fsp)) { + return; + } + + smb_fname = fsp->fsp_name; + + if(fsp->fake_file_handle) { + /* + * This is actually for the QUOTA_FAKE_FILE --metze + */ + + /* We know this name is ok, it's already passed the checks. */ + + } else if(fsp_get_pathref_fd(fsp) == -1) { + /* + * This is actually a QFILEINFO on a directory + * handle (returned from an NT SMB). NT5.0 seems + * to do this call. JRA. + */ + ret = vfs_stat(conn, smb_fname); + if (ret != 0) { + DBG_NOTICE("vfs_stat of %s failed (%s)\n", + smb_fname_str_dbg(smb_fname), + strerror(errno)); + reply_nterror(req, + map_nt_error_from_unix(errno)); + return; + } + + if (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { + fileid = vfs_file_id_from_sbuf( + conn, &smb_fname->st); + get_file_infos(fileid, fsp->name_hash, + &delete_pending, + &write_time_ts); + } + } else { + /* + * Original code - this is an open file. + */ + status = vfs_stat_fsp(fsp); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("fstat of %s failed (%s)\n", + fsp_fnum_dbg(fsp), nt_errstr(status))); + reply_nterror(req, status); + return; + } + if (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { + fileid = vfs_file_id_from_sbuf( + conn, &smb_fname->st); + get_file_infos(fileid, fsp->name_hash, + &delete_pending, + &write_time_ts); + } + } + + } else { + uint32_t name_hash; + char *fname = NULL; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + NTTIME twrp = 0; + + /* qpathinfo */ + if (total_params < 7) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + info_level = SVAL(params,0); + + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); + + if (INFO_LEVEL_IS_UNIX(info_level)) { + if (!lp_smb1_unix_extensions()) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + if (!req->posix_pathnames) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + } + + if (req->posix_pathnames) { + srvstr_get_path_posix(req, + params, + req->flags2, + &fname, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(req, + params, + req->flags2, + &fname, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + if (ucf_flags & UCF_GMT_PATHNAME) { + extract_snapshot_token(fname, &twrp); + } + status = filename_convert_dirfsp(req, + conn, + fname, + ucf_flags, + twrp, + &dirfsp, + &smb_fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + return; + } + reply_nterror(req, status); + return; + } + + /* + * qpathinfo must operate on an existing file, so we + * can exit early if filename_convert_dirfsp() returned the + * "new file" NT_STATUS_OK, !VALID_STAT case. + */ + + if (!VALID_STAT(smb_fname->st)) { + reply_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + /* + * smb_fname->fsp may be NULL if smb_fname points at a symlink + * and we're in POSIX context, so be careful when using fsp + * below, it can still be NULL. + */ + fsp = smb_fname->fsp; + + /* If this is a stream, check if there is a delete_pending. */ + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) + && is_ntfs_stream_smb_fname(smb_fname)) { + struct smb_filename *smb_fname_base; + + /* Create an smb_filename with stream_name == NULL. */ + smb_fname_base = synthetic_smb_fname( + talloc_tos(), + smb_fname->base_name, + NULL, + NULL, + smb_fname->twrp, + smb_fname->flags); + if (smb_fname_base == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + + ret = vfs_stat(conn, smb_fname_base); + if (ret != 0) { + DBG_NOTICE("vfs_stat of %s failed " + "(%s)\n", + smb_fname_str_dbg(smb_fname_base), + strerror(errno)); + TALLOC_FREE(smb_fname_base); + reply_nterror(req, + map_nt_error_from_unix(errno)); + return; + } + + status = file_name_hash(conn, + smb_fname_str_dbg(smb_fname_base), + &name_hash); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(smb_fname_base); + reply_nterror(req, status); + return; + } + + fileid = vfs_file_id_from_sbuf(conn, + &smb_fname_base->st); + TALLOC_FREE(smb_fname_base); + get_file_infos(fileid, name_hash, &delete_pending, NULL); + if (delete_pending) { + reply_nterror(req, NT_STATUS_DELETE_PENDING); + return; + } + } + + status = file_name_hash(conn, + smb_fname_str_dbg(smb_fname), + &name_hash); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + if (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { + fileid = vfs_file_id_from_sbuf(conn, &smb_fname->st); + get_file_infos(fileid, name_hash, &delete_pending, + &write_time_ts); + } + + if (delete_pending) { + reply_nterror(req, NT_STATUS_DELETE_PENDING); + return; + } + } + + DEBUG(3,("call_trans2qfilepathinfo %s (%s) level=%d call=%d " + "total_data=%d\n", smb_fname_str_dbg(smb_fname), + fsp_fnum_dbg(fsp), + info_level,tran_call,total_data)); + + /* Pull out any data sent here before we realloc. */ + switch (info_level) { + case SMB_INFO_QUERY_EAS_FROM_LIST: + { + /* Pull any EA list from the data portion. */ + uint32_t ea_size; + + if (total_data < 4) { + reply_nterror( + req, NT_STATUS_INVALID_PARAMETER); + return; + } + ea_size = IVAL(pdata,0); + + if (total_data > 0 && ea_size != total_data) { + DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \ +total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) )); + reply_nterror( + req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (!lp_ea_support(SNUM(conn))) { + reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED); + return; + } + + /* Pull out the list of names. */ + ea_list = read_ea_name_list(req, pdata + 4, ea_size - 4); + if (!ea_list) { + reply_nterror( + req, NT_STATUS_INVALID_PARAMETER); + return; + } + break; + } + + case SMB_QUERY_POSIX_LOCK: + { + if (fsp == NULL || + fsp->fsp_flags.is_pathref || + fsp_get_io_fd(fsp) == -1) + { + reply_nterror(req, NT_STATUS_INVALID_HANDLE); + return; + } + + if (total_data != POSIX_LOCK_DATA_SIZE) { + reply_nterror( + req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* Copy the lock range data. */ + lock_data = (char *)talloc_memdup( + req, pdata, total_data); + if (!lock_data) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + lock_data_count = total_data; + break; + } + default: + break; + } + + *pparams = (char *)SMB_REALLOC(*pparams,2); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + params = *pparams; + SSVAL(params,0,0); + + /* + * draft-leach-cifs-v1-spec-02.txt + * 4.2.14 TRANS2_QUERY_PATH_INFORMATION: Get File Attributes given Path + * says: + * + * The requested information is placed in the Data portion of the + * transaction response. For the information levels greater than 0x100, + * the transaction response has 1 parameter word which should be + * ignored by the client. + * + * However Windows only follows this rule for the IS_NAME_VALID call. + */ + switch (info_level) { + case SMB_INFO_IS_NAME_VALID: + param_size = 0; + break; + } + + if ((info_level & SMB2_INFO_SPECIAL) == SMB2_INFO_SPECIAL) { + /* + * We use levels that start with 0xFF00 + * internally to represent SMB2 specific levels + */ + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + + status = smbd_do_qfilepathinfo(conn, req, req, info_level, + fsp, smb_fname, + delete_pending, write_time_ts, + ea_list, + lock_data_count, lock_data, + req->flags2, max_data_bytes, + &fixed_portion, + ppdata, &data_size); + if (!NT_STATUS_IS_OK(status)) { + if (open_was_deferred(req->xconn, req->mid)) { + /* We have re-scheduled this call. */ + return; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + bool ok = defer_smb1_sharing_violation(req); + if (ok) { + return; + } + } + reply_nterror(req, status); + return; + } + if (fixed_portion > max_data_bytes) { + reply_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH); + return; + } + + send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size, + max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname). +****************************************************************************/ + +static void call_trans2setfilepathinfo(connection_struct *conn, + struct smb_request *req, + unsigned int tran_call, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + char *pdata = *ppdata; + uint16_t info_level; + struct smb_filename *smb_fname = NULL; + struct files_struct *dirfsp = NULL; + files_struct *fsp = NULL; + NTSTATUS status = NT_STATUS_OK; + int data_return_size = 0; + int ret; + + if (!params) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (tran_call == TRANSACT2_SETFILEINFO) { + if (total_params < 4) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + fsp = file_fsp(req, SVAL(params,0)); + /* Basic check for non-null fsp. */ + if (!check_fsp_open(conn, req, fsp)) { + return; + } + info_level = SVAL(params,2); + + if (INFO_LEVEL_IS_UNIX(info_level)) { + if (!lp_smb1_unix_extensions()) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + if (!req->posix_pathnames) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + } + + smb_fname = fsp->fsp_name; + + if (fsp_get_pathref_fd(fsp) == -1) { + /* + * This is actually a SETFILEINFO on a directory + * handle (returned from an NT SMB). NT5.0 seems + * to do this call. JRA. + */ + ret = vfs_stat(conn, smb_fname); + if (ret != 0) { + DBG_NOTICE("vfs_stat of %s failed (%s)\n", + smb_fname_str_dbg(smb_fname), + strerror(errno)); + reply_nterror(req, + map_nt_error_from_unix(errno)); + return; + } + } else if (fsp->print_file) { + /* + * Doing a DELETE_ON_CLOSE should cancel a print job. + */ + if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) && CVAL(pdata,0)) { + + fsp->fsp_flags.delete_on_close = true; + + DEBUG(3,("call_trans2setfilepathinfo: " + "Cancelling print job (%s)\n", + fsp_str_dbg(fsp))); + + SSVAL(params,0,0); + send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, + *ppdata, 0, + max_data_bytes); + return; + } else { + reply_nterror(req, + NT_STATUS_OBJECT_PATH_NOT_FOUND); + return; + } + } else { + /* + * Original code - this is an open file. + */ + status = vfs_stat_fsp(fsp); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3,("call_trans2setfilepathinfo: fstat " + "of %s failed (%s)\n", fsp_fnum_dbg(fsp), + nt_errstr(status))); + reply_nterror(req, status); + return; + } + } + } else { + char *fname = NULL; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + bool require_existing_object = true; + NTTIME twrp = 0; + + /* set path info */ + if (total_params < 7) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + info_level = SVAL(params,0); + + if (INFO_LEVEL_IS_UNIX(info_level)) { + if (!lp_smb1_unix_extensions()) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + if (!req->posix_pathnames) { + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + } + + if (req->posix_pathnames) { + srvstr_get_path_posix(req, + params, + req->flags2, + &fname, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(req, + params, + req->flags2, + &fname, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + if (ucf_flags & UCF_GMT_PATHNAME) { + extract_snapshot_token(fname, &twrp); + } + status = filename_convert_dirfsp(req, + conn, + fname, + ucf_flags, + twrp, + &dirfsp, + &smb_fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + return; + } + reply_nterror(req, status); + return; + } + + /* + * smb_fname->fsp may be NULL if smb_fname points at a symlink + * and we're in POSIX context, so be careful when using fsp + * below, it can still be NULL. + */ + fsp = smb_fname->fsp; + + /* + * There are 4 info levels which can + * create a new object in the filesystem. + * They are: + * SMB_SET_FILE_UNIX_LINK -> creates POSIX symlink. + * SMB_POSIX_PATH_OPEN -> creates POSIX file or directory. + * SMB_SET_FILE_UNIX_BASIC: + * SMB_SET_FILE_UNIX_INFO2: can create a POSIX special file. + * + * These info levels do not require an existing object. + */ + switch (info_level) { + case SMB_SET_FILE_UNIX_LINK: + case SMB_POSIX_PATH_OPEN: + case SMB_SET_FILE_UNIX_BASIC: + case SMB_SET_FILE_UNIX_INFO2: + require_existing_object = false; + break; + default: + break; + } + + if (!VALID_STAT(smb_fname->st) && require_existing_object) { + reply_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } + } + + DEBUG(3,("call_trans2setfilepathinfo(%d) %s (%s) info_level=%d " + "totdata=%d\n", tran_call, smb_fname_str_dbg(smb_fname), + fsp_fnum_dbg(fsp), + info_level,total_data)); + + /* Realloc the parameter size */ + *pparams = (char *)SMB_REALLOC(*pparams,2); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + params = *pparams; + + SSVAL(params,0,0); + + status = smbd_do_setfilepathinfo(conn, req, req, + info_level, + fsp, + smb_fname, + ppdata, total_data, + &data_return_size); + if (!NT_STATUS_IS_OK(status)) { + if (open_was_deferred(req->xconn, req->mid)) { + /* We have re-scheduled this call. */ + return; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + bool ok = defer_smb1_sharing_violation(req); + if (ok) { + return; + } + } + if (NT_STATUS_EQUAL(status, NT_STATUS_EVENT_PENDING)) { + /* We have re-scheduled this call. */ + return; + } + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + return; + } + if (info_level == SMB_POSIX_PATH_OPEN) { + reply_openerror(req, status); + return; + } + + /* + * Invalid EA name needs to return 2 param bytes, + * not a zero-length error packet. + */ + if (NT_STATUS_EQUAL(status, STATUS_INVALID_EA_NAME)) { + send_trans2_replies(conn, req, status, params, 2, NULL, 0, + max_data_bytes); + } else { + reply_nterror(req, status); + } + return; + } + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, data_return_size, + max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_MKDIR (make directory with extended attributes). +****************************************************************************/ + +static void call_trans2mkdir(connection_struct *conn, struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + struct files_struct *dirfsp = NULL; + struct files_struct *fsp = NULL; + struct smb_filename *smb_dname = NULL; + char *params = *pparams; + char *pdata = *ppdata; + char *directory = NULL; + NTSTATUS status = NT_STATUS_OK; + struct ea_list *ea_list = NULL; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + NTTIME twrp = 0; + TALLOC_CTX *ctx = talloc_tos(); + + if (!CAN_WRITE(conn)) { + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + + if (total_params < 5) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (req->posix_pathnames) { + srvstr_get_path_posix(ctx, + params, + req->flags2, + &directory, + ¶ms[4], + total_params - 4, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(ctx, + params, + req->flags2, + &directory, + ¶ms[4], + total_params - 4, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); + + if (ucf_flags & UCF_GMT_PATHNAME) { + extract_snapshot_token(directory, &twrp); + } + status = filename_convert_dirfsp(ctx, + conn, + directory, + ucf_flags, + twrp, + &dirfsp, + &smb_dname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + return; + } + reply_nterror(req, status); + return; + } + + /* + * OS/2 workplace shell seems to send SET_EA requests of "null" + * length (4 bytes containing IVAL 4). + * They seem to have no effect. Bug #3212. JRA. + */ + + if (total_data && (total_data != 4)) { + /* Any data in this call is an EA list. */ + if (total_data < 10) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + if (IVAL(pdata,0) > total_data) { + DEBUG(10,("call_trans2mkdir: bad total data size (%u) > %u\n", + IVAL(pdata,0), (unsigned int)total_data)); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + ea_list = read_ea_list(talloc_tos(), pdata + 4, + total_data - 4); + if (!ea_list) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto out; + } + + if (!lp_ea_support(SNUM(conn))) { + reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED); + goto out; + } + } + /* If total_data == 4 Windows doesn't care what values + * are placed in that field, it just ignores them. + * The System i QNTC IBM SMB client puts bad values here, + * so ignore them. */ + + status = SMB_VFS_CREATE_FILE( + conn, /* conn */ + req, /* req */ + dirfsp, /* dirfsp */ + smb_dname, /* fname */ + MAXIMUM_ALLOWED_ACCESS, /* access_mask */ + FILE_SHARE_NONE, /* share_access */ + FILE_CREATE, /* create_disposition*/ + FILE_DIRECTORY_FILE, /* create_options */ + FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */ + 0, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + NULL, NULL); /* create context */ + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + goto out; + } + + /* Try and set any given EA. */ + if (ea_list) { + status = set_ea(conn, fsp, ea_list); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + goto out; + } + } + + /* Realloc the parameter and data sizes */ + *pparams = (char *)SMB_REALLOC(*pparams,2); + if(*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + params = *pparams; + + SSVAL(params,0,0); + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, 0, max_data_bytes); + + out: + if (fsp != NULL) { + close_file_free(NULL, &fsp, NORMAL_CLOSE); + } + TALLOC_FREE(smb_dname); + return; +} + +/**************************************************************************** + Reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes). + We don't actually do this - we just send a null response. +****************************************************************************/ + +static void call_trans2findnotifyfirst(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + uint16_t info_level; + + if (total_params < 6) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + info_level = SVAL(params,4); + DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level)); + + switch (info_level) { + case 1: + case 2: + break; + default: + reply_nterror(req, NT_STATUS_INVALID_LEVEL); + return; + } + + /* Realloc the parameter and data sizes */ + *pparams = (char *)SMB_REALLOC(*pparams,6); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + params = *pparams; + + SSVAL(params,0,fnf_handle); + SSVAL(params,2,0); /* No changes */ + SSVAL(params,4,0); /* No EA errors */ + + fnf_handle++; + + if(fnf_handle == 0) + fnf_handle = 257; + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 6, *ppdata, 0, max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for + changes). Currently this does nothing. +****************************************************************************/ + +static void call_trans2findnotifynext(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + + DEBUG(3,("call_trans2findnotifynext\n")); + + /* Realloc the parameter and data sizes */ + *pparams = (char *)SMB_REALLOC(*pparams,4); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + params = *pparams; + + SSVAL(params,0,0); /* No changes */ + SSVAL(params,2,0); /* No EA errors */ + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 4, *ppdata, 0, max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>. +****************************************************************************/ + +static void call_trans2getdfsreferral(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + char *pathname = NULL; + int reply_size = 0; + int max_referral_level; + NTSTATUS status = NT_STATUS_OK; + TALLOC_CTX *ctx = talloc_tos(); + + DEBUG(10,("call_trans2getdfsreferral\n")); + + if (total_params < 3) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + max_referral_level = SVAL(params,0); + + if(!lp_host_msdfs()) { + reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED); + return; + } + + srvstr_pull_talloc(ctx, params, req->flags2, &pathname, ¶ms[2], + total_params - 2, STR_TERMINATE); + if (!pathname) { + reply_nterror(req, NT_STATUS_NOT_FOUND); + return; + } + if((reply_size = setup_dfs_referral(conn, pathname, max_referral_level, + ppdata,&status)) < 0) { + reply_nterror(req, status); + return; + } + + SSVAL((discard_const_p(uint8_t, req->inbuf)), smb_flg2, + SVAL(req->inbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES); + send_trans2_replies(conn, req, NT_STATUS_OK, 0,0,*ppdata,reply_size, max_data_bytes); + + return; +} + +#define LMCAT_SPL 0x53 +#define LMFUNC_GETJOBID 0x60 + +/**************************************************************************** + Reply to a TRANS2_IOCTL - used for OS/2 printing. +****************************************************************************/ + +static void call_trans2ioctl(connection_struct *conn, + struct smb_request *req, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *pdata = *ppdata; + files_struct *fsp = file_fsp(req, SVAL(req->vwv+15, 0)); + NTSTATUS status; + size_t len = 0; + + /* check for an invalid fid before proceeding */ + + if (!fsp) { + reply_nterror(req, NT_STATUS_INVALID_HANDLE); + return; + } + + if ((SVAL(req->vwv+16, 0) == LMCAT_SPL) + && (SVAL(req->vwv+17, 0) == LMFUNC_GETJOBID)) { + *ppdata = (char *)SMB_REALLOC(*ppdata, 32); + if (*ppdata == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + pdata = *ppdata; + + /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2 + CAN ACCEPT THIS IN UNICODE. JRA. */ + + /* Job number */ + SSVAL(pdata, 0, print_spool_rap_jobid(fsp->print_file)); + + status = srvstr_push(pdata, req->flags2, pdata + 2, + lp_netbios_name(), 15, + STR_ASCII|STR_TERMINATE, &len); /* Our NetBIOS name */ + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + status = srvstr_push(pdata, req->flags2, pdata+18, + lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), 13, + STR_ASCII|STR_TERMINATE, &len); /* Service name */ + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + send_trans2_replies(conn, req, NT_STATUS_OK, *pparams, 0, *ppdata, 32, + max_data_bytes); + return; + } + + DEBUG(2,("Unknown TRANS2_IOCTL\n")); + reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED); +} + +static void handle_trans2(connection_struct *conn, struct smb_request *req, + struct trans_state *state) +{ + if (get_Protocol() >= PROTOCOL_NT1) { + req->flags2 |= 0x40; /* IS_LONG_NAME */ + SSVAL((discard_const_p(uint8_t, req->inbuf)),smb_flg2,req->flags2); + } + + if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) { + if (state->call != TRANSACT2_QFSINFO && + state->call != TRANSACT2_SETFSINFO) { + DEBUG(0,("handle_trans2: encryption required " + "with call 0x%x\n", + (unsigned int)state->call)); + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + SMB_PERFCOUNT_SET_SUBOP(&req->pcd, state->call); + + /* Now we must call the relevant TRANS2 function */ + switch(state->call) { + case TRANSACT2_OPEN: + { + START_PROFILE(Trans2_open); + call_trans2open(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_open); + break; + } + + case TRANSACT2_FINDFIRST: + { + START_PROFILE(Trans2_findfirst); + call_trans2findfirst(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_findfirst); + break; + } + + case TRANSACT2_FINDNEXT: + { + START_PROFILE(Trans2_findnext); + call_trans2findnext(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_findnext); + break; + } + + case TRANSACT2_QFSINFO: + { + START_PROFILE(Trans2_qfsinfo); + call_trans2qfsinfo(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_qfsinfo); + break; + } + + case TRANSACT2_SETFSINFO: + { + START_PROFILE(Trans2_setfsinfo); + call_trans2setfsinfo(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_setfsinfo); + break; + } + + case TRANSACT2_QPATHINFO: + case TRANSACT2_QFILEINFO: + { + START_PROFILE(Trans2_qpathinfo); + call_trans2qfilepathinfo(conn, req, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_qpathinfo); + break; + } + + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + { + START_PROFILE(Trans2_setpathinfo); + call_trans2setfilepathinfo(conn, req, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_setpathinfo); + break; + } + + case TRANSACT2_FINDNOTIFYFIRST: + { + START_PROFILE(Trans2_findnotifyfirst); + call_trans2findnotifyfirst(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_findnotifyfirst); + break; + } + + case TRANSACT2_FINDNOTIFYNEXT: + { + START_PROFILE(Trans2_findnotifynext); + call_trans2findnotifynext(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_findnotifynext); + break; + } + + case TRANSACT2_MKDIR: + { + START_PROFILE(Trans2_mkdir); + call_trans2mkdir(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_mkdir); + break; + } + + case TRANSACT2_GET_DFS_REFERRAL: + { + START_PROFILE(Trans2_get_dfs_referral); + call_trans2getdfsreferral(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_get_dfs_referral); + break; + } + + case TRANSACT2_IOCTL: + { + START_PROFILE(Trans2_ioctl); + call_trans2ioctl(conn, req, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_ioctl); + break; + } + + default: + /* Error in request */ + DEBUG(2,("Unknown request %d in trans2 call\n", state->call)); + reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED); + } +} + +/**************************************************************************** + Reply to a SMBtrans2. + ****************************************************************************/ + +void reply_trans2(struct smb_request *req) +{ + connection_struct *conn = req->conn; + unsigned int dsoff; + unsigned int dscnt; + unsigned int psoff; + unsigned int pscnt; + unsigned int tran_call; + struct trans_state *state; + NTSTATUS result; + + START_PROFILE(SMBtrans2); + + if (req->wct < 14) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBtrans2); + return; + } + + dsoff = SVAL(req->vwv+12, 0); + dscnt = SVAL(req->vwv+11, 0); + psoff = SVAL(req->vwv+10, 0); + pscnt = SVAL(req->vwv+9, 0); + tran_call = SVAL(req->vwv+14, 0); + + result = allow_new_trans(conn->pending_trans, req->mid); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(2, ("Got invalid trans2 request: %s\n", + nt_errstr(result))); + reply_nterror(req, result); + END_PROFILE(SMBtrans2); + return; + } + + if (IS_IPC(conn)) { + switch (tran_call) { + /* List the allowed trans2 calls on IPC$ */ + case TRANSACT2_OPEN: + case TRANSACT2_GET_DFS_REFERRAL: + case TRANSACT2_QFILEINFO: + case TRANSACT2_QFSINFO: + case TRANSACT2_SETFSINFO: + break; + default: + reply_nterror(req, NT_STATUS_ACCESS_DENIED); + END_PROFILE(SMBtrans2); + return; + } + } + + if ((state = talloc(conn, struct trans_state)) == NULL) { + DEBUG(0, ("talloc failed\n")); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBtrans2); + return; + } + + state->cmd = SMBtrans2; + + state->mid = req->mid; + state->vuid = req->vuid; + state->setup_count = SVAL(req->vwv+13, 0); + state->setup = NULL; + state->total_param = SVAL(req->vwv+0, 0); + state->param = NULL; + state->total_data = SVAL(req->vwv+1, 0); + state->data = NULL; + state->max_param_return = SVAL(req->vwv+2, 0); + state->max_data_return = SVAL(req->vwv+3, 0); + state->max_setup_return = SVAL(req->vwv+4, 0); + state->close_on_completion = BITSETW(req->vwv+5, 0); + state->one_way = BITSETW(req->vwv+5, 1); + + state->call = tran_call; + + /* All trans2 messages we handle have smb_sucnt == 1 - ensure this + is so as a sanity check */ + if (state->setup_count != 1) { + /* + * Need to have rc=0 for ioctl to get job id for OS/2. + * Network printing will fail if function is not successful. + * Similar function in reply.c will be used if protocol + * is LANMAN1.0 instead of LM1.2X002. + * Until DosPrintSetJobInfo with PRJINFO3 is supported, + * outbuf doesn't have to be set(only job id is used). + */ + if ( (state->setup_count == 4) + && (tran_call == TRANSACT2_IOCTL) + && (SVAL(req->vwv+16, 0) == LMCAT_SPL) + && (SVAL(req->vwv+17, 0) == LMFUNC_GETJOBID)) { + DEBUG(2,("Got Trans2 DevIOctl jobid\n")); + } else { + DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",state->setup_count)); + DEBUG(2,("Transaction is %d\n",tran_call)); + TALLOC_FREE(state); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBtrans2); + return; + } + } + + if ((dscnt > state->total_data) || (pscnt > state->total_param)) + goto bad_param; + + if (state->total_data) { + + if (smb_buffer_oob(state->total_data, 0, dscnt) + || smb_buffer_oob(smb_len(req->inbuf), dsoff, dscnt)) { + goto bad_param; + } + + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->data = (char *)SMB_MALLOC(state->total_data); + if (state->data == NULL) { + DEBUG(0,("reply_trans2: data malloc fail for %u " + "bytes !\n", (unsigned int)state->total_data)); + TALLOC_FREE(state); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBtrans2); + return; + } + + memcpy(state->data,smb_base(req->inbuf)+dsoff,dscnt); + } + + if (state->total_param) { + + if (smb_buffer_oob(state->total_param, 0, pscnt) + || smb_buffer_oob(smb_len(req->inbuf), psoff, pscnt)) { + goto bad_param; + } + + /* Can't use talloc here, the core routines do realloc on the + * params and data. */ + state->param = (char *)SMB_MALLOC(state->total_param); + if (state->param == NULL) { + DEBUG(0,("reply_trans: param malloc fail for %u " + "bytes !\n", (unsigned int)state->total_param)); + SAFE_FREE(state->data); + TALLOC_FREE(state); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBtrans2); + return; + } + + memcpy(state->param,smb_base(req->inbuf)+psoff,pscnt); + } + + state->received_data = dscnt; + state->received_param = pscnt; + + if ((state->received_param == state->total_param) && + (state->received_data == state->total_data)) { + + handle_trans2(conn, req, state); + + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtrans2); + return; + } + + DLIST_ADD(conn->pending_trans, state); + + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + reply_smb1_outbuf(req, 0, 0); + show_msg((char *)req->outbuf); + END_PROFILE(SMBtrans2); + return; + + bad_param: + + DEBUG(0,("reply_trans2: invalid trans parameters\n")); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + END_PROFILE(SMBtrans2); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); +} + +/**************************************************************************** + Reply to a SMBtranss2 + ****************************************************************************/ + +void reply_transs2(struct smb_request *req) +{ + connection_struct *conn = req->conn; + unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp; + struct trans_state *state; + + START_PROFILE(SMBtranss2); + + show_msg((const char *)req->inbuf); + + /* Windows clients expect all replies to + a transact secondary (SMBtranss2 0x33) + to have a command code of transact + (SMBtrans2 0x32). See bug #8989 + and also [MS-CIFS] section 2.2.4.47.2 + for details. + */ + req->cmd = SMBtrans2; + + if (req->wct < 8) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBtranss2); + return; + } + + for (state = conn->pending_trans; state != NULL; + state = state->next) { + if (state->mid == req->mid) { + break; + } + } + + if ((state == NULL) || (state->cmd != SMBtrans2)) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBtranss2); + return; + } + + /* Revise state->total_param and state->total_data in case they have + changed downwards */ + + if (SVAL(req->vwv+0, 0) < state->total_param) + state->total_param = SVAL(req->vwv+0, 0); + if (SVAL(req->vwv+1, 0) < state->total_data) + state->total_data = SVAL(req->vwv+1, 0); + + pcnt = SVAL(req->vwv+2, 0); + poff = SVAL(req->vwv+3, 0); + pdisp = SVAL(req->vwv+4, 0); + + dcnt = SVAL(req->vwv+5, 0); + doff = SVAL(req->vwv+6, 0); + ddisp = SVAL(req->vwv+7, 0); + + state->received_param += pcnt; + state->received_data += dcnt; + + if ((state->received_data > state->total_data) || + (state->received_param > state->total_param)) + goto bad_param; + + if (pcnt) { + if (smb_buffer_oob(state->total_param, pdisp, pcnt) + || smb_buffer_oob(smb_len(req->inbuf), poff, pcnt)) { + goto bad_param; + } + memcpy(state->param+pdisp,smb_base(req->inbuf)+poff,pcnt); + } + + if (dcnt) { + if (smb_buffer_oob(state->total_data, ddisp, dcnt) + || smb_buffer_oob(smb_len(req->inbuf), doff, dcnt)) { + goto bad_param; + } + memcpy(state->data+ddisp, smb_base(req->inbuf)+doff,dcnt); + } + + if ((state->received_param < state->total_param) || + (state->received_data < state->total_data)) { + END_PROFILE(SMBtranss2); + return; + } + + handle_trans2(conn, req, state); + + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + + END_PROFILE(SMBtranss2); + return; + + bad_param: + + DEBUG(0,("reply_transs2: invalid trans parameters\n")); + DLIST_REMOVE(conn->pending_trans, state); + SAFE_FREE(state->data); + SAFE_FREE(state->param); + TALLOC_FREE(state); + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + END_PROFILE(SMBtranss2); + return; +} |