/* Unix SMB/CIFS implementation. Main SMB reply routines Copyright (C) Andrew Tridgell 1992-2003 Copyright (C) James J Myers 2003 <myersjj@samba.org> Copyright (C) Stefan Metzmacher 2006 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* This file handles most of the reply_ calls that the server makes to handle specific SMB commands */ #include "includes.h" #include "smb_server/smb_server.h" #include "ntvfs/ntvfs.h" #include "librpc/gen_ndr/ndr_nbt.h" /**************************************************************************** Reply to a simple request (async send) ****************************************************************************/ static void reply_simple_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; SMBSRV_CHECK_ASYNC_STATUS_SIMPLE; smbsrv_setup_reply(req, 0, 0); smbsrv_send_reply(req); } /**************************************************************************** Reply to a tcon. ****************************************************************************/ void smbsrv_reply_tcon(struct smbsrv_request *req) { union smb_tcon con; NTSTATUS status; uint8_t *p; /* parse request */ SMBSRV_CHECK_WCT(req, 0); con.tcon.level = RAW_TCON_TCON; p = req->in.data; p += req_pull_ascii4(&req->in.bufinfo, &con.tcon.in.service, p, STR_TERMINATE); p += req_pull_ascii4(&req->in.bufinfo, &con.tcon.in.password, p, STR_TERMINATE); p += req_pull_ascii4(&req->in.bufinfo, &con.tcon.in.dev, p, STR_TERMINATE); if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) { smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER); return; } /* call backend */ status = smbsrv_tcon_backend(req, &con); if (!NT_STATUS_IS_OK(status)) { smbsrv_send_error(req, status); return; } /* construct reply */ smbsrv_setup_reply(req, 2, 0); SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit); SSVAL(req->out.vwv, VWV(1), con.tcon.out.tid); SSVAL(req->out.hdr, HDR_TID, req->tcon->tid); smbsrv_send_reply(req); } /**************************************************************************** Reply to a tcon and X. ****************************************************************************/ void smbsrv_reply_tcon_and_X(struct smbsrv_request *req) { NTSTATUS status; union smb_tcon con; uint8_t *p; uint16_t passlen; con.tconx.level = RAW_TCON_TCONX; /* parse request */ SMBSRV_CHECK_WCT(req, 4); con.tconx.in.flags = SVAL(req->in.vwv, VWV(2)); passlen = SVAL(req->in.vwv, VWV(3)); p = req->in.data; if (!req_pull_blob(&req->in.bufinfo, p, passlen, &con.tconx.in.password)) { smbsrv_send_error(req, NT_STATUS_ILL_FORMED_PASSWORD); return; } p += passlen; p += req_pull_string(&req->in.bufinfo, &con.tconx.in.path, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &con.tconx.in.device, p, -1, STR_ASCII); if (!con.tconx.in.path || !con.tconx.in.device) { smbsrv_send_error(req, NT_STATUS_BAD_DEVICE_TYPE); return; } /* call backend */ status = smbsrv_tcon_backend(req, &con); if (!NT_STATUS_IS_OK(status)) { smbsrv_send_error(req, status); return; } /* construct reply - two variants */ if (req->smb_conn->negotiate.protocol < PROTOCOL_NT1) { smbsrv_setup_reply(req, 2, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); } else { smbsrv_setup_reply(req, 3, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SSVAL(req->out.vwv, VWV(2), con.tconx.out.options); req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE); } /* set the incoming and outgoing tid to the just created one */ SSVAL(req->in.hdr, HDR_TID, con.tconx.out.tid); SSVAL(req->out.hdr,HDR_TID, con.tconx.out.tid); smbsrv_chain_reply(req); } /**************************************************************************** Reply to an unknown request ****************************************************************************/ void smbsrv_reply_unknown(struct smbsrv_request *req) { int type; type = CVAL(req->in.hdr, HDR_COM); DEBUG(0,("unknown command type %d (0x%X)\n", type, type)); smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRunknownsmb)); } /**************************************************************************** Reply to an ioctl (async reply) ****************************************************************************/ static void reply_ioctl_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_ioctl *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_ioctl); /* the +1 is for nicer alignment */ smbsrv_setup_reply(req, 8, io->ioctl.out.blob.length+1); SSVAL(req->out.vwv, VWV(1), io->ioctl.out.blob.length); SSVAL(req->out.vwv, VWV(5), io->ioctl.out.blob.length); SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1); memcpy(req->out.data+1, io->ioctl.out.blob.data, io->ioctl.out.blob.length); smbsrv_send_reply(req); } /**************************************************************************** Reply to an ioctl. ****************************************************************************/ void smbsrv_reply_ioctl(struct smbsrv_request *req) { union smb_ioctl *io; /* parse request */ SMBSRV_CHECK_WCT(req, 3); SMBSRV_TALLOC_IO_PTR(io, union smb_ioctl); SMBSRV_SETUP_NTVFS_REQUEST(reply_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->ioctl.level = RAW_IOCTL_IOCTL; io->ioctl.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->ioctl.in.request = IVAL(req->in.vwv, VWV(1)); SMBSRV_CHECK_FILE_HANDLE_ERROR(io->ioctl.in.file.ntvfs, NT_STATUS_DOS(ERRSRV, ERRerror)); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io)); } /**************************************************************************** Reply to a chkpth. ****************************************************************************/ void smbsrv_reply_chkpth(struct smbsrv_request *req) { union smb_chkpath *io; SMBSRV_TALLOC_IO_PTR(io, union smb_chkpath); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); req_pull_ascii4(&req->in.bufinfo, &io->chkpath.in.path, req->in.data, STR_TERMINATE); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_chkpath(req->ntvfs, io)); } /**************************************************************************** Reply to a getatr (async reply) ****************************************************************************/ static void reply_getatr_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_fileinfo *st; SMBSRV_CHECK_ASYNC_STATUS(st, union smb_fileinfo); /* construct reply */ smbsrv_setup_reply(req, 10, 0); SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib); srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(1), st->getattr.out.write_time); SIVAL(req->out.vwv, VWV(3), st->getattr.out.size); SMBSRV_VWV_RESERVED(5, 5); smbsrv_send_reply(req); } /**************************************************************************** Reply to a getatr. ****************************************************************************/ void smbsrv_reply_getatr(struct smbsrv_request *req) { union smb_fileinfo *st; SMBSRV_TALLOC_IO_PTR(st, union smb_fileinfo); SMBSRV_SETUP_NTVFS_REQUEST(reply_getatr_send, NTVFS_ASYNC_STATE_MAY_ASYNC); st->getattr.level = RAW_FILEINFO_GETATTR; /* parse request */ req_pull_ascii4(&req->in.bufinfo, &st->getattr.in.file.path, req->in.data, STR_TERMINATE); if (!st->getattr.in.file.path) { smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_qpathinfo(req->ntvfs, st)); } /**************************************************************************** Reply to a setatr. ****************************************************************************/ void smbsrv_reply_setatr(struct smbsrv_request *req) { union smb_setfileinfo *st; /* parse request */ SMBSRV_CHECK_WCT(req, 8); SMBSRV_TALLOC_IO_PTR(st, union smb_setfileinfo); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); st->setattr.level = RAW_SFILEINFO_SETATTR; st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0)); st->setattr.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); req_pull_ascii4(&req->in.bufinfo, &st->setattr.in.file.path, req->in.data, STR_TERMINATE); if (!st->setattr.in.file.path) { smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_setpathinfo(req->ntvfs, st)); } /**************************************************************************** Reply to a dskattr (async reply) ****************************************************************************/ static void reply_dskattr_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_fsinfo *fs; SMBSRV_CHECK_ASYNC_STATUS(fs, union smb_fsinfo); /* construct reply */ smbsrv_setup_reply(req, 5, 0); SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total); SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit); SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size); SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free); SMBSRV_VWV_RESERVED(4, 1); smbsrv_send_reply(req); } /**************************************************************************** Reply to a dskattr. ****************************************************************************/ void smbsrv_reply_dskattr(struct smbsrv_request *req) { union smb_fsinfo *fs; SMBSRV_TALLOC_IO_PTR(fs, union smb_fsinfo); SMBSRV_SETUP_NTVFS_REQUEST(reply_dskattr_send, NTVFS_ASYNC_STATE_MAY_ASYNC); fs->dskattr.level = RAW_QFS_DSKATTR; SMBSRV_CALL_NTVFS_BACKEND(ntvfs_fsinfo(req->ntvfs, fs)); } /**************************************************************************** Reply to an open (async reply) ****************************************************************************/ static void reply_open_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_open *oi; SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open); /* construct reply */ smbsrv_setup_reply(req, 7, 0); smbsrv_push_fnum(req->out.vwv, VWV(0), oi->openold.out.file.ntvfs); SSVAL(req->out.vwv, VWV(1), oi->openold.out.attrib); srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(2), oi->openold.out.write_time); SIVAL(req->out.vwv, VWV(4), oi->openold.out.size); SSVAL(req->out.vwv, VWV(6), oi->openold.out.rmode); smbsrv_send_reply(req); } /**************************************************************************** Reply to an open. ****************************************************************************/ void smbsrv_reply_open(struct smbsrv_request *req) { union smb_open *oi; /* parse request */ SMBSRV_CHECK_WCT(req, 2); SMBSRV_TALLOC_IO_PTR(oi, union smb_open); SMBSRV_SETUP_NTVFS_REQUEST(reply_open_send, NTVFS_ASYNC_STATE_MAY_ASYNC); oi->openold.level = RAW_OPEN_OPEN; oi->openold.in.open_mode = SVAL(req->in.vwv, VWV(0)); oi->openold.in.search_attrs = SVAL(req->in.vwv, VWV(1)); req_pull_ascii4(&req->in.bufinfo, &oi->openold.in.fname, req->in.data, STR_TERMINATE); if (!oi->openold.in.fname) { smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi)); } /**************************************************************************** Reply to an open and X (async reply) ****************************************************************************/ static void reply_open_and_X_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_open *oi; SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open); /* build the reply */ if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { smbsrv_setup_reply(req, 19, 0); } else { smbsrv_setup_reply(req, 15, 0); } SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); smbsrv_push_fnum(req->out.vwv, VWV(2), oi->openx.out.file.ntvfs); SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib); srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(4), oi->openx.out.write_time); SIVAL(req->out.vwv, VWV(6), oi->openx.out.size); SSVAL(req->out.vwv, VWV(8), oi->openx.out.access); SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype); SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate); SSVAL(req->out.vwv, VWV(11),oi->openx.out.action); SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid); SSVAL(req->out.vwv, VWV(14),0); /* reserved */ if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask); SMBSRV_VWV_RESERVED(17, 2); } req->chained_fnum = SVAL(req->out.vwv, VWV(2)); smbsrv_chain_reply(req); } /**************************************************************************** Reply to an open and X. ****************************************************************************/ void smbsrv_reply_open_and_X(struct smbsrv_request *req) { union smb_open *oi; /* parse the request */ SMBSRV_CHECK_WCT(req, 15); SMBSRV_TALLOC_IO_PTR(oi, union smb_open); SMBSRV_SETUP_NTVFS_REQUEST(reply_open_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC); oi->openx.level = RAW_OPEN_OPENX; oi->openx.in.flags = SVAL(req->in.vwv, VWV(2)); oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3)); oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4)); oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5)); oi->openx.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(6)); oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8)); oi->openx.in.size = IVAL(req->in.vwv, VWV(9)); oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11)); req_pull_ascii4(&req->in.bufinfo, &oi->openx.in.fname, req->in.data, STR_TERMINATE); if (!oi->openx.in.fname) { smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi)); } /**************************************************************************** Reply to a mknew or a create. ****************************************************************************/ static void reply_mknew_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_open *oi; SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open); /* build the reply */ smbsrv_setup_reply(req, 1, 0); smbsrv_push_fnum(req->out.vwv, VWV(0), oi->mknew.out.file.ntvfs); smbsrv_send_reply(req); } /**************************************************************************** Reply to a mknew or a create. ****************************************************************************/ void smbsrv_reply_mknew(struct smbsrv_request *req) { union smb_open *oi; /* parse the request */ SMBSRV_CHECK_WCT(req, 3); SMBSRV_TALLOC_IO_PTR(oi, union smb_open); SMBSRV_SETUP_NTVFS_REQUEST(reply_mknew_send, NTVFS_ASYNC_STATE_MAY_ASYNC); if (CVAL(req->in.hdr, HDR_COM) == SMBmknew) { oi->mknew.level = RAW_OPEN_MKNEW; } else { oi->mknew.level = RAW_OPEN_CREATE; } oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0)); oi->mknew.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); req_pull_ascii4(&req->in.bufinfo, &oi->mknew.in.fname, req->in.data, STR_TERMINATE); if (!oi->mknew.in.fname) { smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi)); } /**************************************************************************** Reply to a create temporary file (async reply) ****************************************************************************/ static void reply_ctemp_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_open *oi; SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open); /* build the reply */ smbsrv_setup_reply(req, 1, 0); smbsrv_push_fnum(req->out.vwv, VWV(0), oi->ctemp.out.file.ntvfs); /* the returned filename is relative to the directory */ req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE | STR_ASCII); smbsrv_send_reply(req); } /**************************************************************************** Reply to a create temporary file. ****************************************************************************/ void smbsrv_reply_ctemp(struct smbsrv_request *req) { union smb_open *oi; /* parse the request */ SMBSRV_CHECK_WCT(req, 3); SMBSRV_TALLOC_IO_PTR(oi, union smb_open); SMBSRV_SETUP_NTVFS_REQUEST(reply_ctemp_send, NTVFS_ASYNC_STATE_MAY_ASYNC); oi->ctemp.level = RAW_OPEN_CTEMP; oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0)); oi->ctemp.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); /* the filename is actually a directory name, the server provides a filename in that directory */ req_pull_ascii4(&req->in.bufinfo, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE); if (!oi->ctemp.in.directory) { smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi)); } /**************************************************************************** Reply to a unlink ****************************************************************************/ void smbsrv_reply_unlink(struct smbsrv_request *req) { union smb_unlink *unl; /* parse the request */ SMBSRV_CHECK_WCT(req, 1); SMBSRV_TALLOC_IO_PTR(unl, union smb_unlink); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); unl->unlink.in.attrib = SVAL(req->in.vwv, VWV(0)); req_pull_ascii4(&req->in.bufinfo, &unl->unlink.in.pattern, req->in.data, STR_TERMINATE); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_unlink(req->ntvfs, unl)); } /**************************************************************************** Reply to a readbraw (core+ protocol). this is a strange packet because it doesn't use a standard SMB header in the reply, only the 4 byte NBT header This command must be replied to synchronously ****************************************************************************/ void smbsrv_reply_readbraw(struct smbsrv_request *req) { NTSTATUS status; union smb_read io; io.readbraw.level = RAW_READ_READBRAW; /* there are two variants, one with 10 and one with 8 command words */ if (req->in.wct < 8) { goto failed; } io.readbraw.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1)); io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(3)); io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(4)); io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5)); if (!io.readbraw.in.file.ntvfs) { goto failed; } /* the 64 bit variant */ if (req->in.wct == 10) { uint32_t offset_high = IVAL(req->in.vwv, VWV(8)); io.readbraw.in.offset |= (((off_t)offset_high) << 32); } /* before calling the backend we setup the raw buffer. This * saves a copy later */ req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE; req->out.buffer = talloc_size(req, req->out.size); if (req->out.buffer == NULL) { goto failed; } SIVAL(req->out.buffer, 0, 0); /* init NBT header */ /* tell the backend where to put the data */ io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE; /* prepare the ntvfs request */ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, req->session->session_info, SVAL(req->in.hdr,HDR_PID), req->request_time, req, NULL, 0); if (!req->ntvfs) { goto failed; } /* call the backend */ status = ntvfs_read(req->ntvfs, &io); if (!NT_STATUS_IS_OK(status)) { goto failed; } req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE; smbsrv_send_reply_nosign(req); return; failed: /* any failure in readbraw is equivalent to reading zero bytes */ req->out.size = 4; req->out.buffer = talloc_size(req, req->out.size); SIVAL(req->out.buffer, 0, 0); /* init NBT header */ smbsrv_send_reply_nosign(req); } /**************************************************************************** Reply to a lockread (async reply) ****************************************************************************/ static void reply_lockread_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_read *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read); /* trim packet */ io->lockread.out.nread = MIN(io->lockread.out.nread, req_max_data(req) - 3); req_grow_data(req, 3 + io->lockread.out.nread); /* construct reply */ SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread); SMBSRV_VWV_RESERVED(1, 4); SCVAL(req->out.data, 0, SMB_DATA_BLOCK); SSVAL(req->out.data, 1, io->lockread.out.nread); smbsrv_send_reply(req); } /**************************************************************************** Reply to a lockread (core+ protocol). note that the lock is a write lock, not a read lock! ****************************************************************************/ void smbsrv_reply_lockread(struct smbsrv_request *req) { union smb_read *io; /* parse request */ SMBSRV_CHECK_WCT(req, 5); SMBSRV_TALLOC_IO_PTR(io, union smb_read); SMBSRV_SETUP_NTVFS_REQUEST(reply_lockread_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->lockread.level = RAW_READ_LOCKREAD; io->lockread.in.file.ntvfs= smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->lockread.in.count = SVAL(req->in.vwv, VWV(1)); io->lockread.in.offset = IVAL(req->in.vwv, VWV(2)); io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4)); /* setup the reply packet assuming the maximum possible read */ smbsrv_setup_reply(req, 5, 3 + io->lockread.in.count); /* tell the backend where to put the data */ io->lockread.out.data = req->out.data + 3; SMBSRV_CHECK_FILE_HANDLE(io->lockread.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io)); } /**************************************************************************** Reply to a read (async reply) ****************************************************************************/ static void reply_read_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_read *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read); /* trim packet */ io->read.out.nread = MIN(io->read.out.nread, req_max_data(req) - 3); req_grow_data(req, 3 + io->read.out.nread); /* construct reply */ SSVAL(req->out.vwv, VWV(0), io->read.out.nread); SMBSRV_VWV_RESERVED(1, 4); SCVAL(req->out.data, 0, SMB_DATA_BLOCK); SSVAL(req->out.data, 1, io->read.out.nread); smbsrv_send_reply(req); } /**************************************************************************** Reply to a read. ****************************************************************************/ void smbsrv_reply_read(struct smbsrv_request *req) { union smb_read *io; /* parse request */ SMBSRV_CHECK_WCT(req, 5); SMBSRV_TALLOC_IO_PTR(io, union smb_read); SMBSRV_SETUP_NTVFS_REQUEST(reply_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->read.level = RAW_READ_READ; io->read.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->read.in.count = SVAL(req->in.vwv, VWV(1)); io->read.in.offset = IVAL(req->in.vwv, VWV(2)); io->read.in.remaining = SVAL(req->in.vwv, VWV(4)); /* setup the reply packet assuming the maximum possible read */ smbsrv_setup_reply(req, 5, 3 + io->read.in.count); /* tell the backend where to put the data */ io->read.out.data = req->out.data + 3; SMBSRV_CHECK_FILE_HANDLE(io->read.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io)); } /**************************************************************************** Reply to a read and X (async reply) ****************************************************************************/ static void reply_read_and_X_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_read *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read); /* readx reply packets can be over-sized */ req->control_flags |= SMBSRV_REQ_CONTROL_LARGE; if (io->readx.in.maxcnt != 0xFFFF && io->readx.in.mincnt != 0xFFFF) { req_grow_data(req, 1 + io->readx.out.nread); SCVAL(req->out.data, 0, 0); /* padding */ } else { req_grow_data(req, io->readx.out.nread); } /* construct reply */ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining); SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode); SMBSRV_VWV_RESERVED(4, 1); SSVAL(req->out.vwv, VWV(5), io->readx.out.nread); SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr)); SMBSRV_VWV_RESERVED(7, 5); smbsrv_chain_reply(req); } /**************************************************************************** Reply to a read and X. ****************************************************************************/ void smbsrv_reply_read_and_X(struct smbsrv_request *req) { union smb_read *io; /* parse request */ if (req->in.wct != 12) { SMBSRV_CHECK_WCT(req, 10); } SMBSRV_TALLOC_IO_PTR(io, union smb_read); SMBSRV_SETUP_NTVFS_REQUEST(reply_read_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->readx.level = RAW_READ_READX; io->readx.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(2)); io->readx.in.offset = IVAL(req->in.vwv, VWV(3)); io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5)); io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6)); io->readx.in.remaining = SVAL(req->in.vwv, VWV(9)); if (req->flags2 & FLAGS2_READ_PERMIT_EXECUTE) { io->readx.in.read_for_execute = true; } else { io->readx.in.read_for_execute = false; } if (req->smb_conn->negotiate.client_caps & CAP_LARGE_READX) { uint32_t high_part = IVAL(req->in.vwv, VWV(7)); if (high_part == 1) { io->readx.in.maxcnt |= high_part << 16; } } /* the 64 bit variant */ if (req->in.wct == 12) { uint32_t offset_high = IVAL(req->in.vwv, VWV(10)); io->readx.in.offset |= (((uint64_t)offset_high) << 32); } /* setup the reply packet assuming the maximum possible read */ smbsrv_setup_reply(req, 12, 1 + io->readx.in.maxcnt); /* tell the backend where to put the data. Notice the pad byte. */ if (io->readx.in.maxcnt != 0xFFFF && io->readx.in.mincnt != 0xFFFF) { io->readx.out.data = req->out.data + 1; } else { io->readx.out.data = req->out.data; } SMBSRV_CHECK_FILE_HANDLE(io->readx.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io)); } /**************************************************************************** Reply to a writebraw (core+ or LANMAN1.0 protocol). ****************************************************************************/ void smbsrv_reply_writebraw(struct smbsrv_request *req) { smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD)); } /**************************************************************************** Reply to a writeunlock (async reply) ****************************************************************************/ static void reply_writeunlock_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_write *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write); /* construct reply */ smbsrv_setup_reply(req, 1, 0); SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten); smbsrv_send_reply(req); } /**************************************************************************** Reply to a writeunlock (core+). ****************************************************************************/ void smbsrv_reply_writeunlock(struct smbsrv_request *req) { union smb_write *io; SMBSRV_CHECK_WCT(req, 5); SMBSRV_TALLOC_IO_PTR(io, union smb_write); SMBSRV_SETUP_NTVFS_REQUEST(reply_writeunlock_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->writeunlock.level = RAW_WRITE_WRITEUNLOCK; io->writeunlock.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1)); io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2)); io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4)); io->writeunlock.in.data = req->in.data + 3; /* make sure they gave us the data they promised */ if (io->writeunlock.in.count+3 > req->in.data_size) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } /* make sure the data block is big enough */ if (SVAL(req->in.data, 1) < io->writeunlock.in.count) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CHECK_FILE_HANDLE(io->writeunlock.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io)); } /**************************************************************************** Reply to a write (async reply) ****************************************************************************/ static void reply_write_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_write *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write); /* construct reply */ smbsrv_setup_reply(req, 1, 0); SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); smbsrv_send_reply(req); } /**************************************************************************** Reply to a write ****************************************************************************/ void smbsrv_reply_write(struct smbsrv_request *req) { union smb_write *io; SMBSRV_CHECK_WCT(req, 5); SMBSRV_TALLOC_IO_PTR(io, union smb_write); SMBSRV_SETUP_NTVFS_REQUEST(reply_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->write.level = RAW_WRITE_WRITE; io->write.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->write.in.count = SVAL(req->in.vwv, VWV(1)); io->write.in.offset = IVAL(req->in.vwv, VWV(2)); io->write.in.remaining = SVAL(req->in.vwv, VWV(4)); io->write.in.data = req->in.data + 3; /* make sure they gave us the data they promised */ if (req_data_oob(&req->in.bufinfo, io->write.in.data, io->write.in.count)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } /* make sure the data block is big enough */ if (SVAL(req->in.data, 1) < io->write.in.count) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CHECK_FILE_HANDLE(io->write.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io)); } /**************************************************************************** Reply to a write and X (async reply) ****************************************************************************/ static void reply_write_and_X_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_write *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write); /* construct reply */ smbsrv_setup_reply(req, 6, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF); SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining); SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16); SMBSRV_VWV_RESERVED(5, 1); smbsrv_chain_reply(req); } /**************************************************************************** Reply to a write and X. ****************************************************************************/ void smbsrv_reply_write_and_X(struct smbsrv_request *req) { union smb_write *io; if (req->in.wct != 14) { SMBSRV_CHECK_WCT(req, 12); } SMBSRV_TALLOC_IO_PTR(io, union smb_write); SMBSRV_SETUP_NTVFS_REQUEST(reply_write_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->writex.level = RAW_WRITE_WRITEX; io->writex.in.file.ntvfs= smbsrv_pull_fnum(req, req->in.vwv, VWV(2)); io->writex.in.offset = IVAL(req->in.vwv, VWV(3)); io->writex.in.wmode = SVAL(req->in.vwv, VWV(7)); io->writex.in.remaining = SVAL(req->in.vwv, VWV(8)); io->writex.in.count = SVAL(req->in.vwv, VWV(10)); io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11)); if (req->in.wct == 14) { uint32_t offset_high = IVAL(req->in.vwv, VWV(12)); uint16_t count_high = SVAL(req->in.vwv, VWV(9)); io->writex.in.offset |= (((uint64_t)offset_high) << 32); io->writex.in.count |= ((uint32_t)count_high) << 16; } /* make sure the data is in bounds */ if (req_data_oob(&req->in.bufinfo, io->writex.in.data, io->writex.in.count)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CHECK_FILE_HANDLE(io->writex.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io)); } /**************************************************************************** Reply to a lseek (async reply) ****************************************************************************/ static void reply_lseek_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_seek *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_seek); /* construct reply */ smbsrv_setup_reply(req, 2, 0); SIVALS(req->out.vwv, VWV(0), io->lseek.out.offset); smbsrv_send_reply(req); } /**************************************************************************** Reply to a lseek. ****************************************************************************/ void smbsrv_reply_lseek(struct smbsrv_request *req) { union smb_seek *io; SMBSRV_CHECK_WCT(req, 4); SMBSRV_TALLOC_IO_PTR(io, union smb_seek); SMBSRV_SETUP_NTVFS_REQUEST(reply_lseek_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->lseek.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->lseek.in.mode = SVAL(req->in.vwv, VWV(1)); io->lseek.in.offset = IVALS(req->in.vwv, VWV(2)); SMBSRV_CHECK_FILE_HANDLE(io->lseek.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_seek(req->ntvfs, io)); } /**************************************************************************** Reply to a flush. ****************************************************************************/ void smbsrv_reply_flush(struct smbsrv_request *req) { union smb_flush *io; uint16_t fnum; /* parse request */ SMBSRV_CHECK_WCT(req, 1); SMBSRV_TALLOC_IO_PTR(io, union smb_flush); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); fnum = SVAL(req->in.vwv, VWV(0)); if (fnum == 0xFFFF) { io->flush_all.level = RAW_FLUSH_ALL; } else { io->flush.level = RAW_FLUSH_FLUSH; io->flush.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); SMBSRV_CHECK_FILE_HANDLE(io->flush.in.file.ntvfs); } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io)); } /**************************************************************************** Reply to a close Note that this has to deal with closing a directory opened by NT SMB's. ****************************************************************************/ void smbsrv_reply_close(struct smbsrv_request *req) { union smb_close *io; /* parse request */ SMBSRV_CHECK_WCT(req, 3); SMBSRV_TALLOC_IO_PTR(io, union smb_close); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->close.level = RAW_CLOSE_CLOSE; io->close.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->close.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); SMBSRV_CHECK_FILE_HANDLE(io->close.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io)); } /**************************************************************************** Reply to a writeclose (async reply) ****************************************************************************/ static void reply_writeclose_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_write *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write); /* construct reply */ smbsrv_setup_reply(req, 1, 0); SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); smbsrv_send_reply(req); } /**************************************************************************** Reply to a writeclose (Core+ protocol). ****************************************************************************/ void smbsrv_reply_writeclose(struct smbsrv_request *req) { union smb_write *io; /* this one is pretty weird - the wct can be 6 or 12 */ if (req->in.wct != 12) { SMBSRV_CHECK_WCT(req, 6); } SMBSRV_TALLOC_IO_PTR(io, union smb_write); SMBSRV_SETUP_NTVFS_REQUEST(reply_writeclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->writeclose.level = RAW_WRITE_WRITECLOSE; io->writeclose.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->writeclose.in.count = SVAL(req->in.vwv, VWV(1)); io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2)); io->writeclose.in.mtime = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(4)); io->writeclose.in.data = req->in.data + 1; /* make sure they gave us the data they promised */ if (req_data_oob(&req->in.bufinfo, io->writeclose.in.data, io->writeclose.in.count)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CHECK_FILE_HANDLE(io->writeclose.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io)); } /**************************************************************************** Reply to a lock. ****************************************************************************/ void smbsrv_reply_lock(struct smbsrv_request *req) { union smb_lock *lck; /* parse request */ SMBSRV_CHECK_WCT(req, 5); SMBSRV_TALLOC_IO_PTR(lck, union smb_lock); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); lck->lock.level = RAW_LOCK_LOCK; lck->lock.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); lck->lock.in.count = IVAL(req->in.vwv, VWV(1)); lck->lock.in.offset = IVAL(req->in.vwv, VWV(3)); SMBSRV_CHECK_FILE_HANDLE(lck->lock.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck)); } /**************************************************************************** Reply to a unlock. ****************************************************************************/ void smbsrv_reply_unlock(struct smbsrv_request *req) { union smb_lock *lck; /* parse request */ SMBSRV_CHECK_WCT(req, 5); SMBSRV_TALLOC_IO_PTR(lck, union smb_lock); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); lck->unlock.level = RAW_LOCK_UNLOCK; lck->unlock.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); lck->unlock.in.count = IVAL(req->in.vwv, VWV(1)); lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3)); SMBSRV_CHECK_FILE_HANDLE(lck->unlock.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck)); } /**************************************************************************** Reply to a tdis. ****************************************************************************/ void smbsrv_reply_tdis(struct smbsrv_request *req) { struct smbsrv_handle *h, *nh; SMBSRV_CHECK_WCT(req, 0); /* * TODO: cancel all pending requests on this tcon */ /* * close all handles on this tcon */ for (h=req->tcon->handles.list; h; h=nh) { nh = h->next; talloc_free(h); } /* finally destroy the tcon */ talloc_free(req->tcon); req->tcon = NULL; smbsrv_setup_reply(req, 0, 0); smbsrv_send_reply(req); } /**************************************************************************** Reply to a echo. This is one of the few calls that is handled directly (the backends don't see it at all) ****************************************************************************/ void smbsrv_reply_echo(struct smbsrv_request *req) { uint16_t count; int i; SMBSRV_CHECK_WCT(req, 1); count = SVAL(req->in.vwv, VWV(0)); smbsrv_setup_reply(req, 1, req->in.data_size); memcpy(req->out.data, req->in.data, req->in.data_size); for (i=1; i <= count;i++) { struct smbsrv_request *this_req; if (i != count) { this_req = smbsrv_setup_secondary_request(req); } else { this_req = req; } SSVAL(this_req->out.vwv, VWV(0), i); smbsrv_send_reply(this_req); } } /**************************************************************************** Reply to a printopen (async reply) ****************************************************************************/ static void reply_printopen_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_open *oi; SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open); /* construct reply */ smbsrv_setup_reply(req, 1, 0); smbsrv_push_fnum(req->out.vwv, VWV(0), oi->openold.out.file.ntvfs); smbsrv_send_reply(req); } /**************************************************************************** Reply to a printopen. ****************************************************************************/ void smbsrv_reply_printopen(struct smbsrv_request *req) { union smb_open *oi; /* parse request */ SMBSRV_CHECK_WCT(req, 2); SMBSRV_TALLOC_IO_PTR(oi, union smb_open); SMBSRV_SETUP_NTVFS_REQUEST(reply_printopen_send, NTVFS_ASYNC_STATE_MAY_ASYNC); oi->splopen.level = RAW_OPEN_SPLOPEN; oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0)); oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1)); req_pull_ascii4(&req->in.bufinfo, &oi->splopen.in.ident, req->in.data, STR_TERMINATE); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi)); } /**************************************************************************** Reply to a printclose. ****************************************************************************/ void smbsrv_reply_printclose(struct smbsrv_request *req) { union smb_close *io; /* parse request */ SMBSRV_CHECK_WCT(req, 3); SMBSRV_TALLOC_IO_PTR(io, union smb_close); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->splclose.level = RAW_CLOSE_SPLCLOSE; io->splclose.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); SMBSRV_CHECK_FILE_HANDLE(io->splclose.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io)); } /**************************************************************************** Reply to a printqueue. ****************************************************************************/ static void reply_printqueue_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_lpq *lpq; int i, maxcount; const uint_t el_size = 28; SMBSRV_CHECK_ASYNC_STATUS(lpq,union smb_lpq); /* construct reply */ smbsrv_setup_reply(req, 2, 0); /* truncate the returned list to fit in the negotiated buffer size */ maxcount = (req_max_data(req) - 3) / el_size; if (maxcount < lpq->retq.out.count) { lpq->retq.out.count = maxcount; } /* setup enough space in the reply */ req_grow_data(req, 3 + el_size*lpq->retq.out.count); /* and fill it in */ SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count); SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx); SCVAL(req->out.data, 0, SMB_DATA_BLOCK); SSVAL(req->out.data, 1, el_size*lpq->retq.out.count); req->out.ptr = req->out.data + 3; for (i=0;i<lpq->retq.out.count;i++) { srv_push_dos_date2(req->smb_conn, req->out.ptr, 0 , lpq->retq.out.queue[i].time); SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status); SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job); SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size); SCVAL(req->out.ptr, 11, 0); /* reserved */ req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII); req->out.ptr += el_size; } smbsrv_send_reply(req); } /**************************************************************************** Reply to a printqueue. ****************************************************************************/ void smbsrv_reply_printqueue(struct smbsrv_request *req) { union smb_lpq *lpq; /* parse request */ SMBSRV_CHECK_WCT(req, 2); SMBSRV_TALLOC_IO_PTR(lpq, union smb_lpq); SMBSRV_SETUP_NTVFS_REQUEST(reply_printqueue_send, NTVFS_ASYNC_STATE_MAY_ASYNC); lpq->retq.level = RAW_LPQ_RETQ; lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0)); lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1)); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lpq(req->ntvfs, lpq)); } /**************************************************************************** Reply to a printwrite. ****************************************************************************/ void smbsrv_reply_printwrite(struct smbsrv_request *req) { union smb_write *io; /* parse request */ SMBSRV_CHECK_WCT(req, 1); SMBSRV_TALLOC_IO_PTR(io, union smb_write); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); if (req->in.data_size < 3) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } io->splwrite.level = RAW_WRITE_SPLWRITE; io->splwrite.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); io->splwrite.in.count = SVAL(req->in.data, 1); io->splwrite.in.data = req->in.data + 3; /* make sure they gave us the data they promised */ if (req_data_oob(&req->in.bufinfo, io->splwrite.in.data, io->splwrite.in.count)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CHECK_FILE_HANDLE(io->splwrite.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io)); } /**************************************************************************** Reply to a mkdir. ****************************************************************************/ void smbsrv_reply_mkdir(struct smbsrv_request *req) { union smb_mkdir *io; /* parse the request */ SMBSRV_CHECK_WCT(req, 0); SMBSRV_TALLOC_IO_PTR(io, union smb_mkdir); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->generic.level = RAW_MKDIR_MKDIR; req_pull_ascii4(&req->in.bufinfo, &io->mkdir.in.path, req->in.data, STR_TERMINATE); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_mkdir(req->ntvfs, io)); } /**************************************************************************** Reply to a rmdir. ****************************************************************************/ void smbsrv_reply_rmdir(struct smbsrv_request *req) { struct smb_rmdir *io; /* parse the request */ SMBSRV_CHECK_WCT(req, 0); SMBSRV_TALLOC_IO_PTR(io, struct smb_rmdir); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); req_pull_ascii4(&req->in.bufinfo, &io->in.path, req->in.data, STR_TERMINATE); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rmdir(req->ntvfs, io)); } /**************************************************************************** Reply to a mv. ****************************************************************************/ void smbsrv_reply_mv(struct smbsrv_request *req) { union smb_rename *io; uint8_t *p; /* parse the request */ SMBSRV_CHECK_WCT(req, 1); SMBSRV_TALLOC_IO_PTR(io, union smb_rename); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->generic.level = RAW_RENAME_RENAME; io->rename.in.attrib = SVAL(req->in.vwv, VWV(0)); p = req->in.data; p += req_pull_ascii4(&req->in.bufinfo, &io->rename.in.pattern1, p, STR_TERMINATE); p += req_pull_ascii4(&req->in.bufinfo, &io->rename.in.pattern2, p, STR_TERMINATE); if (!io->rename.in.pattern1 || !io->rename.in.pattern2) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rename(req->ntvfs, io)); } /**************************************************************************** Reply to an NT rename. ****************************************************************************/ void smbsrv_reply_ntrename(struct smbsrv_request *req) { union smb_rename *io; uint8_t *p; /* parse the request */ SMBSRV_CHECK_WCT(req, 4); SMBSRV_TALLOC_IO_PTR(io, union smb_rename); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->generic.level = RAW_RENAME_NTRENAME; io->ntrename.in.attrib = SVAL(req->in.vwv, VWV(0)); io->ntrename.in.flags = SVAL(req->in.vwv, VWV(1)); io->ntrename.in.cluster_size = IVAL(req->in.vwv, VWV(2)); p = req->in.data; p += req_pull_ascii4(&req->in.bufinfo, &io->ntrename.in.old_name, p, STR_TERMINATE); p += req_pull_ascii4(&req->in.bufinfo, &io->ntrename.in.new_name, p, STR_TERMINATE); if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rename(req->ntvfs, io)); } /**************************************************************************** Reply to a file copy (async reply) ****************************************************************************/ static void reply_copy_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; struct smb_copy *cp; SMBSRV_CHECK_ASYNC_STATUS(cp, struct smb_copy); /* build the reply */ smbsrv_setup_reply(req, 1, 0); SSVAL(req->out.vwv, VWV(0), cp->out.count); smbsrv_send_reply(req); } /**************************************************************************** Reply to a file copy. ****************************************************************************/ void smbsrv_reply_copy(struct smbsrv_request *req) { struct smb_copy *cp; uint8_t *p; /* parse request */ SMBSRV_CHECK_WCT(req, 3); SMBSRV_TALLOC_IO_PTR(cp, struct smb_copy); SMBSRV_SETUP_NTVFS_REQUEST(reply_copy_send, NTVFS_ASYNC_STATE_MAY_ASYNC); cp->in.tid2 = SVAL(req->in.vwv, VWV(0)); cp->in.ofun = SVAL(req->in.vwv, VWV(1)); cp->in.flags = SVAL(req->in.vwv, VWV(2)); p = req->in.data; p += req_pull_ascii4(&req->in.bufinfo, &cp->in.path1, p, STR_TERMINATE); p += req_pull_ascii4(&req->in.bufinfo, &cp->in.path2, p, STR_TERMINATE); if (!cp->in.path1 || !cp->in.path2) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_copy(req->ntvfs, cp)); } /**************************************************************************** Reply to a lockingX request (async send) ****************************************************************************/ static void reply_lockingX_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_lock *lck; SMBSRV_CHECK_ASYNC_STATUS(lck, union smb_lock); /* if it was an oplock break ack then we only send a reply if there was an error */ if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) { talloc_free(req); return; } /* construct reply */ smbsrv_setup_reply(req, 2, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); smbsrv_chain_reply(req); } /**************************************************************************** Reply to a lockingX request. ****************************************************************************/ void smbsrv_reply_lockingX(struct smbsrv_request *req) { union smb_lock *lck; uint_t total_locks, i; uint_t lck_size; uint8_t *p; /* parse request */ SMBSRV_CHECK_WCT(req, 8); SMBSRV_TALLOC_IO_PTR(lck, union smb_lock); SMBSRV_SETUP_NTVFS_REQUEST(reply_lockingX_send, NTVFS_ASYNC_STATE_MAY_ASYNC); lck->lockx.level = RAW_LOCK_LOCKX; lck->lockx.in.file.ntvfs= smbsrv_pull_fnum(req, req->in.vwv, VWV(2)); lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3)); lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4)); lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6)); lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7)); total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt; /* there are two variants, one with 64 bit offsets and counts */ if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { lck_size = 20; } else { lck_size = 10; } /* make sure we got the promised data */ if (req_data_oob(&req->in.bufinfo, req->in.data, total_locks * lck_size)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } /* allocate the locks array */ if (total_locks) { lck->lockx.in.locks = talloc_array(req, struct smb_lock_entry, total_locks); if (lck->lockx.in.locks == NULL) { smbsrv_send_error(req, NT_STATUS_NO_MEMORY); return; } } p = req->in.data; /* construct the locks array */ for (i=0;i<total_locks;i++) { uint32_t ofs_high=0, count_high=0; lck->lockx.in.locks[i].pid = SVAL(p, 0); if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { ofs_high = IVAL(p, 4); lck->lockx.in.locks[i].offset = IVAL(p, 8); count_high = IVAL(p, 12); lck->lockx.in.locks[i].count = IVAL(p, 16); } else { lck->lockx.in.locks[i].offset = IVAL(p, 2); lck->lockx.in.locks[i].count = IVAL(p, 6); } if (ofs_high != 0 || count_high != 0) { lck->lockx.in.locks[i].count |= ((uint64_t)count_high) << 32; lck->lockx.in.locks[i].offset |= ((uint64_t)ofs_high) << 32; } p += lck_size; } SMBSRV_CHECK_FILE_HANDLE(lck->lockx.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck)); } /**************************************************************************** Reply to a SMBreadbmpx (read block multiplex) request. ****************************************************************************/ void smbsrv_reply_readbmpx(struct smbsrv_request *req) { /* tell the client to not use a multiplexed read - its too broken to use */ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD)); } /**************************************************************************** Reply to a SMBsetattrE. ****************************************************************************/ void smbsrv_reply_setattrE(struct smbsrv_request *req) { union smb_setfileinfo *info; /* parse request */ SMBSRV_CHECK_WCT(req, 7); SMBSRV_TALLOC_IO_PTR(info, union smb_setfileinfo); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); info->setattre.level = RAW_SFILEINFO_SETATTRE; info->setattre.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); info->setattre.in.create_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(1)); info->setattre.in.access_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(3)); info->setattre.in.write_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(5)); SMBSRV_CHECK_FILE_HANDLE(info->setattre.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_setfileinfo(req->ntvfs, info)); } /**************************************************************************** Reply to a SMBwritebmpx (write block multiplex primary) request. ****************************************************************************/ void smbsrv_reply_writebmpx(struct smbsrv_request *req) { smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD)); } /**************************************************************************** Reply to a SMBwritebs (write block multiplex secondary) request. ****************************************************************************/ void smbsrv_reply_writebs(struct smbsrv_request *req) { smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD)); } /**************************************************************************** Reply to a SMBgetattrE (async reply) ****************************************************************************/ static void reply_getattrE_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_fileinfo *info; SMBSRV_CHECK_ASYNC_STATUS(info, union smb_fileinfo); /* setup reply */ smbsrv_setup_reply(req, 11, 0); srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(0), info->getattre.out.create_time); srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(2), info->getattre.out.access_time); srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(4), info->getattre.out.write_time); SIVAL(req->out.vwv, VWV(6), info->getattre.out.size); SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size); SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib); smbsrv_send_reply(req); } /**************************************************************************** Reply to a SMBgetattrE. ****************************************************************************/ void smbsrv_reply_getattrE(struct smbsrv_request *req) { union smb_fileinfo *info; /* parse request */ SMBSRV_CHECK_WCT(req, 1); SMBSRV_TALLOC_IO_PTR(info, union smb_fileinfo); SMBSRV_SETUP_NTVFS_REQUEST(reply_getattrE_send, NTVFS_ASYNC_STATE_MAY_ASYNC); info->getattr.level = RAW_FILEINFO_GETATTRE; info->getattr.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0)); SMBSRV_CHECK_FILE_HANDLE(info->getattr.in.file.ntvfs); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_qfileinfo(req->ntvfs, info)); } void smbsrv_reply_sesssetup_send(struct smbsrv_request *req, union smb_sesssetup *io, NTSTATUS status) { switch (io->old.level) { case RAW_SESSSETUP_OLD: if (!NT_STATUS_IS_OK(status)) { smbsrv_send_error(req, status); return; } /* construct reply */ smbsrv_setup_reply(req, 3, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SSVAL(req->out.vwv, VWV(2), io->old.out.action); SSVAL(req->out.hdr, HDR_UID, io->old.out.vuid); smbsrv_chain_reply(req); return; case RAW_SESSSETUP_NT1: if (!NT_STATUS_IS_OK(status)) { smbsrv_send_error(req, status); return; } /* construct reply */ smbsrv_setup_reply(req, 3, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SSVAL(req->out.vwv, VWV(2), io->nt1.out.action); SSVAL(req->out.hdr, HDR_UID, io->nt1.out.vuid); req_push_str(req, NULL, io->nt1.out.os, -1, STR_TERMINATE); req_push_str(req, NULL, io->nt1.out.lanman, -1, STR_TERMINATE); req_push_str(req, NULL, io->nt1.out.domain, -1, STR_TERMINATE); smbsrv_chain_reply(req); return; case RAW_SESSSETUP_SPNEGO: if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { smbsrv_send_error(req, status); return; } /* construct reply */ smbsrv_setup_reply(req, 4, io->spnego.out.secblob.length); if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { smbsrv_setup_error(req, status); } SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SSVAL(req->out.vwv, VWV(2), io->spnego.out.action); SSVAL(req->out.vwv, VWV(3), io->spnego.out.secblob.length); SSVAL(req->out.hdr, HDR_UID, io->spnego.out.vuid); memcpy(req->out.data, io->spnego.out.secblob.data, io->spnego.out.secblob.length); req_push_str(req, NULL, io->spnego.out.os, -1, STR_TERMINATE); req_push_str(req, NULL, io->spnego.out.lanman, -1, STR_TERMINATE); req_push_str(req, NULL, io->spnego.out.workgroup, -1, STR_TERMINATE); smbsrv_chain_reply(req); return; case RAW_SESSSETUP_SMB2: break; } smbsrv_send_error(req, NT_STATUS_INTERNAL_ERROR); } /**************************************************************************** reply to an old style session setup command ****************************************************************************/ static void reply_sesssetup_old(struct smbsrv_request *req) { uint8_t *p; uint16_t passlen; union smb_sesssetup *io; SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup); io->old.level = RAW_SESSSETUP_OLD; /* parse request */ io->old.in.bufsize = SVAL(req->in.vwv, VWV(2)); io->old.in.mpx_max = SVAL(req->in.vwv, VWV(3)); io->old.in.vc_num = SVAL(req->in.vwv, VWV(4)); io->old.in.sesskey = IVAL(req->in.vwv, VWV(5)); passlen = SVAL(req->in.vwv, VWV(7)); /* check the request isn't malformed */ if (req_data_oob(&req->in.bufinfo, req->in.data, passlen)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } p = req->in.data; if (!req_pull_blob(&req->in.bufinfo, p, passlen, &io->old.in.password)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } p += passlen; p += req_pull_string(&req->in.bufinfo, &io->old.in.user, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->old.in.domain, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->old.in.os, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->old.in.lanman, p, -1, STR_TERMINATE); /* call the generic handler */ smbsrv_sesssetup_backend(req, io); } /**************************************************************************** reply to an NT1 style session setup command ****************************************************************************/ static void reply_sesssetup_nt1(struct smbsrv_request *req) { uint8_t *p; uint16_t passlen1, passlen2; union smb_sesssetup *io; SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup); io->nt1.level = RAW_SESSSETUP_NT1; /* parse request */ io->nt1.in.bufsize = SVAL(req->in.vwv, VWV(2)); io->nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3)); io->nt1.in.vc_num = SVAL(req->in.vwv, VWV(4)); io->nt1.in.sesskey = IVAL(req->in.vwv, VWV(5)); passlen1 = SVAL(req->in.vwv, VWV(7)); passlen2 = SVAL(req->in.vwv, VWV(8)); io->nt1.in.capabilities = IVAL(req->in.vwv, VWV(11)); /* check the request isn't malformed */ if (req_data_oob(&req->in.bufinfo, req->in.data, passlen1) || req_data_oob(&req->in.bufinfo, req->in.data + passlen1, passlen2)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } p = req->in.data; if (!req_pull_blob(&req->in.bufinfo, p, passlen1, &io->nt1.in.password1)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } p += passlen1; if (!req_pull_blob(&req->in.bufinfo, p, passlen2, &io->nt1.in.password2)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } p += passlen2; p += req_pull_string(&req->in.bufinfo, &io->nt1.in.user, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->nt1.in.domain, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->nt1.in.os, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->nt1.in.lanman, p, -1, STR_TERMINATE); /* call the generic handler */ smbsrv_sesssetup_backend(req, io); } /**************************************************************************** reply to an SPNEGO style session setup command ****************************************************************************/ static void reply_sesssetup_spnego(struct smbsrv_request *req) { uint8_t *p; uint16_t blob_len; union smb_sesssetup *io; SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup); io->spnego.level = RAW_SESSSETUP_SPNEGO; /* parse request */ io->spnego.in.bufsize = SVAL(req->in.vwv, VWV(2)); io->spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3)); io->spnego.in.vc_num = SVAL(req->in.vwv, VWV(4)); io->spnego.in.sesskey = IVAL(req->in.vwv, VWV(5)); blob_len = SVAL(req->in.vwv, VWV(7)); io->spnego.in.capabilities = IVAL(req->in.vwv, VWV(10)); p = req->in.data; if (!req_pull_blob(&req->in.bufinfo, p, blob_len, &io->spnego.in.secblob)) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } p += blob_len; p += req_pull_string(&req->in.bufinfo, &io->spnego.in.os, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->spnego.in.lanman, p, -1, STR_TERMINATE); p += req_pull_string(&req->in.bufinfo, &io->spnego.in.workgroup, p, -1, STR_TERMINATE); /* call the generic handler */ smbsrv_sesssetup_backend(req, io); } /**************************************************************************** reply to a session setup command ****************************************************************************/ void smbsrv_reply_sesssetup(struct smbsrv_request *req) { switch (req->in.wct) { case 10: /* a pre-NT1 call */ reply_sesssetup_old(req); return; case 13: /* a NT1 call */ reply_sesssetup_nt1(req); return; case 12: /* a SPNEGO call */ reply_sesssetup_spnego(req); return; } /* unsupported variant */ smbsrv_send_error(req, NT_STATUS_FOOBAR); } /**************************************************************************** Reply to a exit. This closes all files open by a smbpid ****************************************************************************/ void smbsrv_reply_exit(struct smbsrv_request *req) { struct smbsrv_handle_session_item *i, *ni; struct smbsrv_handle *h; struct smbsrv_tcon *tcon; uint16_t smbpid; SMBSRV_CHECK_WCT(req, 0); smbpid = SVAL(req->in.hdr,HDR_PID); /* first destroy all handles, which have the same PID as the request */ for (i=req->session->handles; i; i=ni) { ni = i->next; h = i->handle; if (h->smbpid != smbpid) continue; talloc_free(h); } /* * then let the ntvfs backends proxy the call if they want to, * but we didn't check the return value of the backends, * as for the SMB client the call succeed */ for (tcon=req->smb_conn->smb_tcons.list;tcon;tcon=tcon->next) { req->tcon = tcon; SMBSRV_SETUP_NTVFS_REQUEST(NULL,0); ntvfs_exit(req->ntvfs); talloc_free(req->ntvfs); req->ntvfs = NULL; req->tcon = NULL; } smbsrv_setup_reply(req, 0, 0); smbsrv_send_reply(req); } /**************************************************************************** Reply to a SMBulogoffX. ****************************************************************************/ void smbsrv_reply_ulogoffX(struct smbsrv_request *req) { struct smbsrv_handle_session_item *i, *ni; struct smbsrv_handle *h; struct smbsrv_tcon *tcon; SMBSRV_CHECK_WCT(req, 2); /* * TODO: cancel all pending requests */ /* destroy all handles */ for (i=req->session->handles; i; i=ni) { ni = i->next; h = i->handle; talloc_free(h); } /* * then let the ntvfs backends proxy the call if they want to, * but we didn't check the return value of the backends, * as for the SMB client the call succeed */ for (tcon=req->smb_conn->smb_tcons.list;tcon;tcon=tcon->next) { req->tcon = tcon; SMBSRV_SETUP_NTVFS_REQUEST(NULL,0); ntvfs_logoff(req->ntvfs); talloc_free(req->ntvfs); req->ntvfs = NULL; req->tcon = NULL; } talloc_free(req->session); req->session = NULL; /* it is now invalid, don't use on any chained packets */ smbsrv_setup_reply(req, 2, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); smbsrv_chain_reply(req); } /**************************************************************************** Reply to an SMBfindclose request ****************************************************************************/ void smbsrv_reply_findclose(struct smbsrv_request *req) { union smb_search_close *io; /* parse request */ SMBSRV_CHECK_WCT(req, 1); SMBSRV_TALLOC_IO_PTR(io, union smb_search_close); SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->findclose.level = RAW_FINDCLOSE_FINDCLOSE; io->findclose.in.handle = SVAL(req->in.vwv, VWV(0)); SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, io)); } /**************************************************************************** Reply to an SMBfindnclose request ****************************************************************************/ void smbsrv_reply_findnclose(struct smbsrv_request *req) { smbsrv_send_error(req, NT_STATUS_FOOBAR); } /**************************************************************************** Reply to an SMBntcreateX request (async send) ****************************************************************************/ static void reply_ntcreate_and_X_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; union smb_open *io; SMBSRV_CHECK_ASYNC_STATUS(io, union smb_open); /* construct reply */ smbsrv_setup_reply(req, 34, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); SSVAL(req->out.vwv, VWV(1), 0); SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level); /* the rest of the parameters are not aligned! */ smbsrv_push_fnum(req->out.vwv, 5, io->ntcreatex.out.file.ntvfs); SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action); push_nttime(req->out.vwv, 11, io->ntcreatex.out.create_time); push_nttime(req->out.vwv, 19, io->ntcreatex.out.access_time); push_nttime(req->out.vwv, 27, io->ntcreatex.out.write_time); push_nttime(req->out.vwv, 35, io->ntcreatex.out.change_time); SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib); SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size); SBVAL(req->out.vwv, 55, io->ntcreatex.out.size); SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type); SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state); SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory); req->chained_fnum = SVAL(req->out.vwv, 5); smbsrv_chain_reply(req); } /**************************************************************************** Reply to an SMBntcreateX request ****************************************************************************/ void smbsrv_reply_ntcreate_and_X(struct smbsrv_request *req) { union smb_open *io; uint16_t fname_len; /* parse the request */ SMBSRV_CHECK_WCT(req, 24); SMBSRV_TALLOC_IO_PTR(io, union smb_open); SMBSRV_SETUP_NTVFS_REQUEST(reply_ntcreate_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC); io->ntcreatex.level = RAW_OPEN_NTCREATEX; /* notice that the word parameters are not word aligned, so we don't use VWV() */ fname_len = SVAL(req->in.vwv, 5); io->ntcreatex.in.flags = IVAL(req->in.vwv, 7); io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11); io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15); io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19); io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27); io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31); io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35); io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39); io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43); io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47); io->ntcreatex.in.ea_list = NULL; io->ntcreatex.in.sec_desc = NULL; io->ntcreatex.in.query_maximal_access = false; /* we use a couple of bits of the create options internally */ if (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_PRIVATE_MASK) { smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER); return; } /* we need a neater way to handle this alignment */ if ((req->flags2 & FLAGS2_UNICODE_STRINGS) && ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) { fname_len++; } req_pull_string(&req->in.bufinfo, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE); if (!io->ntcreatex.in.fname) { smbsrv_send_error(req, NT_STATUS_FOOBAR); return; } SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io)); } /**************************************************************************** Reply to an SMBntcancel request ****************************************************************************/ void smbsrv_reply_ntcancel(struct smbsrv_request *req) { struct smbsrv_request *r; uint16_t tid = SVAL(req->in.hdr,HDR_TID); uint16_t uid = SVAL(req->in.hdr,HDR_UID); uint16_t mid = SVAL(req->in.hdr,HDR_MID); uint16_t pid = SVAL(req->in.hdr,HDR_PID); for (r = req->smb_conn->requests; r; r = r->next) { if (tid != SVAL(r->in.hdr,HDR_TID)) continue; if (uid != SVAL(r->in.hdr,HDR_UID)) continue; if (mid != SVAL(r->in.hdr,HDR_MID)) continue; if (pid != SVAL(r->in.hdr,HDR_PID)) continue; SMBSRV_CHECK(ntvfs_cancel(r->ntvfs)); /* NOTE: this request does not generate a reply */ talloc_free(req); return; } /* TODO: workout the correct error code, * until we know how the smb signing works * for ntcancel replies, don't send an error */ /*smbsrv_send_error(req, NT_STATUS_FOOBAR);*/ talloc_free(req); } /* parse the called/calling names from session request */ static NTSTATUS parse_session_request(struct smbsrv_request *req) { DATA_BLOB blob; NTSTATUS status; blob.data = req->in.buffer + 4; blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer)); if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME; req->smb_conn->negotiate.called_name = talloc(req->smb_conn, struct nbt_name); req->smb_conn->negotiate.calling_name = talloc(req->smb_conn, struct nbt_name); if (req->smb_conn->negotiate.called_name == NULL || req->smb_conn->negotiate.calling_name == NULL) { return NT_STATUS_NO_MEMORY; } status = nbt_name_from_blob(req->smb_conn, &blob, req->smb_conn->negotiate.called_name); NT_STATUS_NOT_OK_RETURN(status); blob.data += blob.length; blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer)); if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME; status = nbt_name_from_blob(req->smb_conn, &blob, req->smb_conn->negotiate.calling_name); NT_STATUS_NOT_OK_RETURN(status); req->smb_conn->negotiate.done_nbt_session = true; return NT_STATUS_OK; } /**************************************************************************** Reply to a special message - a SMB packet with non zero NBT message type ****************************************************************************/ void smbsrv_reply_special(struct smbsrv_request *req) { uint8_t msg_type; uint8_t *buf = talloc_zero_array(req, uint8_t, 4); msg_type = CVAL(req->in.buffer,0); SIVAL(buf, 0, 0); switch (msg_type) { case 0x81: /* session request */ if (req->smb_conn->negotiate.done_nbt_session) { DEBUG(0,("Warning: ignoring secondary session request\n")); return; } SCVAL(buf,0,0x82); SCVAL(buf,3,0); /* we don't check the status - samba always accepts session requests for any name */ parse_session_request(req); req->out.buffer = buf; req->out.size = 4; smbsrv_send_reply_nosign(req); return; case 0x89: /* session keepalive request (some old clients produce this?) */ SCVAL(buf, 0, SMBkeepalive); SCVAL(buf, 3, 0); req->out.buffer = buf; req->out.size = 4; smbsrv_send_reply_nosign(req); return; case SMBkeepalive: /* session keepalive - swallow it */ talloc_free(req); return; } DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type)); talloc_free(req); }