mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
.externalToolBuilders
config
source
cpp
java
org
alfresco
email
filesys
alfresco
avm
ftp
locking
netbios
server
smb
dcerpc
mailslot
server
notify
ntfs
repo
win32
AdminSharedDevice.java
CoreProtocolHandler.java
CoreResumeKey.java
DCERPCHandler.java
DiskInfoPacker.java
Find.java
FindInfoPacker.java
IPCHandler.java
LanManProtocolHandler.java
NTParameterPacker.java
NTProtocolHandler.java
NTTransPacket.java
NamedPipeTransaction.java
NetBIOSPacketHandler.java
NetBIOSSessionSocketHandler.java
OpenAndX.java
PacketHandler.java
PipeDevice.java
PipeLanmanHandler.java
ProtocolFactory.java
ProtocolHandler.java
QueryInfoPacker.java
SMBPacket.java
SMBServer.java
SMBSrvException.java
SMBSrvPacket.java
SMBSrvSession.java
SMBSrvSessionState.java
SMBSrvTransPacket.java
SMBTransPacket.java
SecurityMode.java
SessionSocketHandler.java
SrvSessionFactory.java
SrvTransactBuffer.java
TcpipSMBPacketHandler.java
TcpipSMBSessionSocketHandler.java
VirtualCircuit.java
VirtualCircuitList.java
Capability.java
DataType.java
Dialect.java
DialectSelector.java
DirectoryWatcher.java
FileInfoLevel.java
FindFirstNext.java
InvalidUNCPathException.java
LockingAndX.java
NTIOCtl.java
NTTime.java
NetworkSession.java
PCShare.java
PacketType.java
Protocol.java
SMBDate.java
SMBDeviceType.java
SMBErrorText.java
SMBException.java
SMBStatus.java
SeekType.java
ServerType.java
SharingMode.java
TcpipSMB.java
TransactBuffer.java
TransactionNames.java
WinNT.java
util
CIFSServer.java
FTPServer.java
NFSServer.java
jcr
linkvalidation
model
repo
sandbox
service
tools
util
apache
queryRegister.dtd
meta-inf
test-resources
web
.classpath
.project
build.xml
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5186 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
3004 lines
92 KiB
Java
3004 lines
92 KiB
Java
/*
|
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
* As a special exception to the terms and conditions of version 2.0 of
|
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
* FLOSS exception. You should have recieved a copy of the text describing
|
|
* the FLOSS exception, and it is also available here:
|
|
* http://www.alfresco.com/legal/licensing"
|
|
*/
|
|
package org.alfresco.filesys.smb.server;
|
|
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
|
|
import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
|
|
import org.alfresco.filesys.server.auth.CifsAuthenticator;
|
|
import org.alfresco.filesys.server.auth.ClientInfo;
|
|
import org.alfresco.filesys.server.auth.InvalidUserException;
|
|
import org.alfresco.filesys.server.core.InvalidDeviceInterfaceException;
|
|
import org.alfresco.filesys.server.core.ShareType;
|
|
import org.alfresco.filesys.server.core.SharedDevice;
|
|
import org.alfresco.filesys.server.filesys.AccessDeniedException;
|
|
import org.alfresco.filesys.server.filesys.DiskDeviceContext;
|
|
import org.alfresco.filesys.server.filesys.DiskInterface;
|
|
import org.alfresco.filesys.server.filesys.FileAccess;
|
|
import org.alfresco.filesys.server.filesys.FileAction;
|
|
import org.alfresco.filesys.server.filesys.FileInfo;
|
|
import org.alfresco.filesys.server.filesys.FileOfflineException;
|
|
import org.alfresco.filesys.server.filesys.FileOpenParams;
|
|
import org.alfresco.filesys.server.filesys.FileSharingException;
|
|
import org.alfresco.filesys.server.filesys.FileStatus;
|
|
import org.alfresco.filesys.server.filesys.NetworkFile;
|
|
import org.alfresco.filesys.server.filesys.SearchContext;
|
|
import org.alfresco.filesys.server.filesys.SrvDiskInfo;
|
|
import org.alfresco.filesys.server.filesys.TooManyConnectionsException;
|
|
import org.alfresco.filesys.server.filesys.TooManyFilesException;
|
|
import org.alfresco.filesys.server.filesys.TreeConnection;
|
|
import org.alfresco.filesys.server.filesys.UnsupportedInfoLevelException;
|
|
import org.alfresco.filesys.server.filesys.VolumeInfo;
|
|
import org.alfresco.filesys.smb.DataType;
|
|
import org.alfresco.filesys.smb.FindFirstNext;
|
|
import org.alfresco.filesys.smb.InvalidUNCPathException;
|
|
import org.alfresco.filesys.smb.PCShare;
|
|
import org.alfresco.filesys.smb.PacketType;
|
|
import org.alfresco.filesys.smb.SMBDate;
|
|
import org.alfresco.filesys.smb.SMBStatus;
|
|
import org.alfresco.filesys.util.DataBuffer;
|
|
import org.alfresco.filesys.util.DataPacker;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* LanMan SMB Protocol Handler Class.
|
|
* <p>
|
|
* The LanMan protocol handler processes the additional SMBs that were added to the protocol in the
|
|
* LanMan1 and LanMan2 SMB dialects.
|
|
*/
|
|
class LanManProtocolHandler extends CoreProtocolHandler
|
|
{
|
|
|
|
// Debug logging
|
|
|
|
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
|
|
|
|
// Locking type flags
|
|
|
|
protected static final int LockShared = 0x01;
|
|
protected static final int LockOplockRelease = 0x02;
|
|
protected static final int LockChangeType = 0x04;
|
|
protected static final int LockCancel = 0x08;
|
|
protected static final int LockLargeFiles = 0x10;
|
|
|
|
/**
|
|
* LanManProtocolHandler constructor.
|
|
*/
|
|
protected LanManProtocolHandler()
|
|
{
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* LanManProtocolHandler constructor.
|
|
*
|
|
* @param sess org.alfresco.filesys.smbsrv.SMBSrvSession
|
|
*/
|
|
protected LanManProtocolHandler(SMBSrvSession sess)
|
|
{
|
|
super(sess);
|
|
}
|
|
|
|
/**
|
|
* Return the protocol name
|
|
*
|
|
* @return String
|
|
*/
|
|
public String getName()
|
|
{
|
|
return "LanMan";
|
|
}
|
|
|
|
/**
|
|
* Process the chained SMB commands (AndX).
|
|
*
|
|
* @return New offset to the end of the reply packet
|
|
* @param outPkt Reply packet.
|
|
*/
|
|
protected final int procAndXCommands(SMBSrvPacket outPkt)
|
|
{
|
|
|
|
// Get the chained command and command block offset
|
|
|
|
int andxCmd = m_smbPkt.getAndXCommand();
|
|
int andxOff = m_smbPkt.getParameter(1) + RFCNetBIOSProtocol.HEADER_LEN;
|
|
|
|
// Set the initial chained command and offset
|
|
|
|
outPkt.setAndXCommand(andxCmd);
|
|
outPkt.setParameter(1, andxOff - RFCNetBIOSProtocol.HEADER_LEN);
|
|
|
|
// Pointer to the last parameter block, starts with the main command parameter block
|
|
|
|
int paramBlk = SMBSrvPacket.WORDCNT;
|
|
|
|
// Get the current end of the reply packet offset
|
|
|
|
int endOfPkt = outPkt.getByteOffset() + outPkt.getByteCount();
|
|
boolean andxErr = false;
|
|
|
|
while (andxCmd != SMBSrvPacket.NO_ANDX_CMD && andxErr == false)
|
|
{
|
|
|
|
// Determine the chained command type
|
|
|
|
int prevEndOfPkt = endOfPkt;
|
|
|
|
switch (andxCmd)
|
|
{
|
|
|
|
// Tree connect
|
|
|
|
case PacketType.TreeConnectAndX:
|
|
endOfPkt = procChainedTreeConnectAndX(andxOff, outPkt, endOfPkt);
|
|
break;
|
|
}
|
|
|
|
// Advance to the next chained command block
|
|
|
|
andxCmd = m_smbPkt.getAndXParameter(andxOff, 0) & 0x00FF;
|
|
andxOff = m_smbPkt.getAndXParameter(andxOff, 1);
|
|
|
|
// Set the next chained command details in the current parameter block
|
|
|
|
outPkt.setAndXCommand(prevEndOfPkt, andxCmd);
|
|
outPkt.setAndXParameter(paramBlk, 1, prevEndOfPkt - RFCNetBIOSProtocol.HEADER_LEN);
|
|
|
|
// Advance the current parameter block
|
|
|
|
paramBlk = prevEndOfPkt;
|
|
|
|
// Check if the chained command has generated an error status
|
|
|
|
if (outPkt.getErrorCode() != SMBStatus.Success)
|
|
andxErr = true;
|
|
}
|
|
|
|
// Return the offset to the end of the reply packet
|
|
|
|
return endOfPkt;
|
|
}
|
|
|
|
/**
|
|
* Process a chained tree connect request.
|
|
*
|
|
* @return New end of reply offset.
|
|
* @param cmdOff int Offset to the chained command within the request packet.
|
|
* @param outPkt SMBSrvPacket Reply packet.
|
|
* @param endOff int Offset to the current end of the reply packet.
|
|
*/
|
|
protected final int procChainedTreeConnectAndX(int cmdOff, SMBSrvPacket outPkt, int endOff)
|
|
{
|
|
|
|
// Extract the parameters
|
|
|
|
int flags = m_smbPkt.getAndXParameter(cmdOff, 2);
|
|
int pwdLen = m_smbPkt.getAndXParameter(cmdOff, 3);
|
|
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( outPkt.getUserId());
|
|
|
|
if (vc == null)
|
|
{
|
|
outPkt.setError(m_smbPkt.isLongErrorCode(), SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return endOff;
|
|
}
|
|
|
|
// Get the data bytes position and length
|
|
|
|
int dataPos = m_smbPkt.getAndXByteOffset(cmdOff);
|
|
int dataLen = m_smbPkt.getAndXByteCount(cmdOff);
|
|
byte[] buf = m_smbPkt.getBuffer();
|
|
|
|
// Extract the password string
|
|
|
|
String pwd = null;
|
|
|
|
if (pwdLen > 0)
|
|
{
|
|
pwd = new String(buf, dataPos, pwdLen);
|
|
dataPos += pwdLen;
|
|
dataLen -= pwdLen;
|
|
}
|
|
|
|
// Extract the requested share name, as a UNC path
|
|
|
|
String uncPath = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (uncPath == null)
|
|
{
|
|
outPkt.setError(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
|
|
// Extract the service type string
|
|
|
|
dataPos += uncPath.length() + 1; // null terminated
|
|
dataLen -= uncPath.length() + 1; // null terminated
|
|
|
|
String service = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (service == null)
|
|
{
|
|
outPkt.setError(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
|
|
// Convert the service type to a shared device type, client may specify '?????' in which
|
|
// case we ignore the error.
|
|
|
|
int servType = ShareType.ServiceAsType(service);
|
|
if (servType == ShareType.UNKNOWN && service.compareTo("?????") != 0)
|
|
{
|
|
outPkt.setError(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TREE))
|
|
logger.debug("ANDX Tree Connect AndX - " + uncPath + ", " + service);
|
|
|
|
// Parse the requested share name
|
|
|
|
PCShare share = null;
|
|
|
|
try
|
|
{
|
|
share = new PCShare(uncPath);
|
|
}
|
|
catch (org.alfresco.filesys.smb.InvalidUNCPathException ex)
|
|
{
|
|
outPkt.setError(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
|
|
// Map the IPC$ share to the admin pipe type
|
|
|
|
if (servType == ShareType.NAMEDPIPE && share.getShareName().compareTo("IPC$") == 0)
|
|
servType = ShareType.ADMINPIPE;
|
|
|
|
// Find the requested shared device
|
|
|
|
SharedDevice shareDev = null;
|
|
|
|
try
|
|
{
|
|
|
|
// Get/create the shared device
|
|
|
|
shareDev = m_sess.getSMBServer().findShare(share.getNodeName(), share.getShareName(), servType,
|
|
getSession(), true);
|
|
}
|
|
catch (InvalidUserException ex)
|
|
{
|
|
|
|
// Return a logon failure status
|
|
|
|
outPkt.setError(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
// Return a general status, bad network name
|
|
|
|
outPkt.setError(SMBStatus.SRVInvalidNetworkName, SMBStatus.ErrSrv);
|
|
return endOff;
|
|
}
|
|
|
|
// Check if the share is valid
|
|
|
|
if (shareDev == null || (servType != ShareType.UNKNOWN && shareDev.getType() != servType))
|
|
{
|
|
outPkt.setError(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
|
|
// Authenticate the share connect, if the server is using share mode security
|
|
|
|
CifsAuthenticator auth = getSession().getSMBServer().getAuthenticator();
|
|
int filePerm = FileAccess.Writeable;
|
|
|
|
if (auth != null)
|
|
{
|
|
|
|
// Validate the share connection
|
|
|
|
filePerm = auth.authenticateShareConnect(m_sess.getClientInformation(), shareDev, pwd, m_sess);
|
|
if (filePerm < 0)
|
|
{
|
|
|
|
// Invalid share connection request
|
|
|
|
outPkt.setError(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return endOff;
|
|
}
|
|
}
|
|
|
|
// Allocate a tree id for the new connection
|
|
|
|
try
|
|
{
|
|
|
|
// Allocate the tree id for this connection
|
|
|
|
int treeId = vc.addConnection(shareDev);
|
|
outPkt.setTreeId(treeId);
|
|
|
|
// Set the file permission that this user has been granted for this share
|
|
|
|
TreeConnection tree = vc.findConnection(treeId);
|
|
tree.setPermission(filePerm);
|
|
|
|
// Inform the driver that a connection has been opened
|
|
|
|
if (tree.getInterface() != null)
|
|
tree.getInterface().treeOpened(m_sess, tree);
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TREE))
|
|
logger.debug("ANDX Tree Connect AndX - Allocated Tree Id = " + treeId);
|
|
}
|
|
catch (TooManyConnectionsException ex)
|
|
{
|
|
|
|
// Too many connections open at the moment
|
|
|
|
outPkt.setError(SMBStatus.SRVNoResourcesAvailable, SMBStatus.ErrSrv);
|
|
return endOff;
|
|
}
|
|
|
|
// Build the tree connect response
|
|
|
|
outPkt.setAndXParameterCount(endOff, 2);
|
|
outPkt.setAndXParameter(endOff, 0, SMBSrvPacket.NO_ANDX_CMD);
|
|
outPkt.setAndXParameter(endOff, 1, 0);
|
|
|
|
// Pack the service type
|
|
|
|
int pos = outPkt.getAndXByteOffset(endOff);
|
|
byte[] outBuf = outPkt.getBuffer();
|
|
pos = DataPacker.putString(ShareType.TypeAsService(shareDev.getType()), outBuf, pos, true);
|
|
int bytLen = pos - outPkt.getAndXByteOffset(endOff);
|
|
outPkt.setAndXByteCount(endOff, bytLen);
|
|
|
|
// Return the new end of packet offset
|
|
|
|
return pos;
|
|
}
|
|
|
|
/**
|
|
* Close a search started via the transact2 find first/next command.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected final void procFindClose(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid find close request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(1, 0) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( m_smbPkt.getUserId());
|
|
if ( vc == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
int treeId = m_smbPkt.getTreeId();
|
|
TreeConnection conn = vc.findConnection(treeId);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTAccessDenied, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Get the search id
|
|
|
|
int searchId = m_smbPkt.getParameter(0);
|
|
|
|
// Get the search context
|
|
|
|
SearchContext ctx = vc.getSearchContext(searchId);
|
|
|
|
if (ctx == null)
|
|
{
|
|
|
|
// Invalid search handle
|
|
|
|
m_sess.sendSuccessResponseSMB();
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("Close trans search [" + searchId + "]");
|
|
|
|
// Deallocate the search slot, close the search.
|
|
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Return a success status SMB
|
|
|
|
m_sess.sendSuccessResponseSMB();
|
|
}
|
|
|
|
/**
|
|
* Process the file lock/unlock request.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
*/
|
|
protected final void procLockingAndX(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid locking andX request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(8, 0) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTAccessDenied, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Extract the file lock/unlock parameters
|
|
|
|
int fid = m_smbPkt.getParameter(2);
|
|
int lockType = m_smbPkt.getParameter(3);
|
|
long lockTmo = m_smbPkt.getParameterLong(4);
|
|
int lockCnt = m_smbPkt.getParameter(6);
|
|
int unlockCnt = m_smbPkt.getParameter(7);
|
|
|
|
NetworkFile netFile = conn.findFile(fid);
|
|
|
|
if (netFile == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_LOCK))
|
|
logger.debug("File Lock [" + netFile.getFileId() + "] : type=0x" + Integer.toHexString(lockType) + ", tmo="
|
|
+ lockTmo + ", locks=" + lockCnt + ", unlocks=" + unlockCnt);
|
|
|
|
// Return a success status for now
|
|
|
|
outPkt.setParameterCount(2);
|
|
outPkt.setAndXCommand(0xFF);
|
|
outPkt.setParameter(1, 0);
|
|
outPkt.setByteCount(0);
|
|
|
|
// Send the lock request response
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
|
|
/**
|
|
* Process the logoff request.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
*/
|
|
protected final void procLogoffAndX(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
// Check that the received packet looks like a valid logoff andX request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(2, 0) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the virtual circuit for the request
|
|
|
|
int uid = m_smbPkt.getUserId();
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( uid);
|
|
|
|
if (vc == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
if ( logger.isDebugEnabled() && m_sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
|
|
logger.debug("Logoff vc=" + vc);
|
|
|
|
// Close the virtual circuit
|
|
|
|
m_sess.removeVirtualCircuit( uid);
|
|
|
|
// Return a success status SMB
|
|
|
|
m_sess.sendSuccessResponseSMB();
|
|
}
|
|
|
|
/**
|
|
* Process the file open request.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
*/
|
|
protected final void procOpenAndX(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid open andX request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(15, 1) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// If the connection is to the IPC$ remote admin named pipe pass the request to the IPC
|
|
// handler. If the device is
|
|
// not a disk type device then return an error.
|
|
|
|
if (conn.getSharedDevice().getType() == ShareType.ADMINPIPE)
|
|
{
|
|
|
|
// Use the IPC$ handler to process the request
|
|
|
|
IPCHandler.processIPCRequest(m_sess, outPkt);
|
|
return;
|
|
}
|
|
else if (conn.getSharedDevice().getType() != ShareType.DISK)
|
|
{
|
|
|
|
// Return an access denied error
|
|
|
|
// m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
// m_sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNoAccessRights, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Extract the open file parameters
|
|
|
|
int flags = m_smbPkt.getParameter(2);
|
|
int access = m_smbPkt.getParameter(3);
|
|
int srchAttr = m_smbPkt.getParameter(4);
|
|
int fileAttr = m_smbPkt.getParameter(5);
|
|
int crTime = m_smbPkt.getParameter(6);
|
|
int crDate = m_smbPkt.getParameter(7);
|
|
int openFunc = m_smbPkt.getParameter(8);
|
|
int allocSiz = m_smbPkt.getParameterLong(9);
|
|
|
|
// Extract the filename string
|
|
|
|
String fileName = m_smbPkt.unpackString(m_smbPkt.isUnicode());
|
|
if (fileName == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Create the file open parameters
|
|
|
|
SMBDate crDateTime = null;
|
|
if (crTime > 0 && crDate > 0)
|
|
crDateTime = new SMBDate(crDate, crTime);
|
|
|
|
FileOpenParams params = new FileOpenParams(fileName, openFunc, access, srchAttr, fileAttr, allocSiz, crDateTime
|
|
.getTime());
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_FILE))
|
|
logger.debug("File Open AndX [" + m_smbPkt.getTreeId() + "] params=" + params);
|
|
|
|
// Access the disk interface and open the requested file
|
|
|
|
int fid;
|
|
NetworkFile netFile = null;
|
|
int respAction = 0;
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface that is associated with the shared device
|
|
|
|
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Check if the requested file already exists
|
|
|
|
int fileSts = disk.fileExists(m_sess, conn, fileName);
|
|
|
|
if (fileSts == FileStatus.NotExist)
|
|
{
|
|
|
|
// Check if the file should be created if it does not exist
|
|
|
|
if (FileAction.createNotExists(openFunc))
|
|
{
|
|
|
|
// Check if the session has write access to the filesystem
|
|
|
|
if (conn.hasWriteAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Create a new file
|
|
|
|
netFile = disk.createFile(m_sess, conn, params);
|
|
|
|
// Indicate that the file did not exist and was created
|
|
|
|
respAction = FileAction.FileCreated;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Check if the path is a directory
|
|
|
|
if (fileSts == FileStatus.DirectoryExists)
|
|
{
|
|
|
|
// Return an access denied error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Return a file not found error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSFileNotFound, SMBStatus.ErrDos);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Open the requested file
|
|
|
|
netFile = disk.openFile(m_sess, conn, params);
|
|
|
|
// Set the file action response
|
|
|
|
if (FileAction.truncateExistingFile(openFunc))
|
|
respAction = FileAction.FileTruncated;
|
|
else
|
|
respAction = FileAction.FileExisted;
|
|
}
|
|
|
|
// Add the file to the list of open files for this tree connection
|
|
|
|
fid = conn.addFile(netFile, getSession());
|
|
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (TooManyFilesException ex)
|
|
{
|
|
|
|
// Too many files are open on this connection, cannot open any more files.
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSTooManyOpenFiles, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (AccessDeniedException ex)
|
|
{
|
|
|
|
// Return an access denied error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (FileSharingException ex)
|
|
{
|
|
|
|
// Return a sharing violation error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSFileSharingConflict, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (FileOfflineException ex)
|
|
{
|
|
|
|
// File data is unavailable
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTFileOffline, SMBStatus.HRDDriveNotReady, SMBStatus.ErrHrd);
|
|
return;
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
|
|
// Failed to open the file
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSFileNotFound, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Build the open file response
|
|
|
|
outPkt.setParameterCount(15);
|
|
|
|
outPkt.setAndXCommand(0xFF);
|
|
outPkt.setParameter(1, 0); // AndX offset
|
|
|
|
outPkt.setParameter(2, fid);
|
|
outPkt.setParameter(3, netFile.getFileAttributes()); // file attributes
|
|
|
|
SMBDate modDate = null;
|
|
|
|
if (netFile.hasModifyDate())
|
|
modDate = new SMBDate(netFile.getModifyDate());
|
|
|
|
outPkt.setParameter(4, modDate != null ? modDate.asSMBTime() : 0); // last write time
|
|
outPkt.setParameter(5, modDate != null ? modDate.asSMBDate() : 0); // last write date
|
|
outPkt.setParameterLong(6, netFile.getFileSizeInt()); // file size
|
|
outPkt.setParameter(8, netFile.getGrantedAccess());
|
|
outPkt.setParameter(9, OpenAndX.FileTypeDisk);
|
|
outPkt.setParameter(10, 0); // named pipe state
|
|
outPkt.setParameter(11, respAction);
|
|
outPkt.setParameter(12, 0); // server FID (long)
|
|
outPkt.setParameter(13, 0);
|
|
outPkt.setParameter(14, 0);
|
|
|
|
outPkt.setByteCount(0);
|
|
|
|
// Send the response packet
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
|
|
/**
|
|
* Process the file read request.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
*/
|
|
protected final void procReadAndX(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid read andX request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(10, 0) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// If the connection is to the IPC$ remote admin named pipe pass the request to the IPC
|
|
// handler.
|
|
|
|
if (conn.getSharedDevice().getType() == ShareType.ADMINPIPE)
|
|
{
|
|
|
|
// Use the IPC$ handler to process the request
|
|
|
|
IPCHandler.processIPCRequest(m_sess, outPkt);
|
|
return;
|
|
}
|
|
|
|
// Extract the read file parameters
|
|
|
|
int fid = m_smbPkt.getParameter(2);
|
|
int offset = m_smbPkt.getParameterLong(3);
|
|
int maxCount = m_smbPkt.getParameter(5);
|
|
|
|
NetworkFile netFile = conn.findFile(fid);
|
|
|
|
if (netFile == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_FILEIO))
|
|
logger.debug("File Read AndX [" + netFile.getFileId() + "] : Size=" + maxCount + " ,Pos=" + offset);
|
|
|
|
// Read data from the file
|
|
|
|
byte[] buf = outPkt.getBuffer();
|
|
int dataPos = 0;
|
|
int rdlen = 0;
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface that is associated with the shared device
|
|
|
|
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Set the returned parameter count so that the byte offset can be calculated
|
|
|
|
outPkt.setParameterCount(12);
|
|
dataPos = outPkt.getByteOffset();
|
|
// dataPos = ( dataPos + 3) & 0xFFFFFFFC; // longword align the data
|
|
|
|
// Check if the requested data length will fit into the buffer
|
|
|
|
int dataLen = buf.length - dataPos;
|
|
if (dataLen < maxCount)
|
|
maxCount = dataLen;
|
|
|
|
// Read from the file
|
|
|
|
rdlen = disk.readFile(m_sess, conn, netFile, buf, dataPos, maxCount, offset);
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (AccessDeniedException ex)
|
|
{
|
|
|
|
// No access to file, or file is a directory
|
|
//
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_FILEIO))
|
|
logger.debug("File Read Error [" + netFile.getFileId() + "] : " + ex.toString());
|
|
|
|
// Failed to read the file
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
|
|
// Debug
|
|
|
|
logger.error("File Read Error [" + netFile.getFileId() + "] : ", ex);
|
|
|
|
// Failed to read the file
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.HRDReadFault, SMBStatus.ErrHrd);
|
|
return;
|
|
}
|
|
|
|
// Return the data block
|
|
|
|
outPkt.setAndXCommand(0xFF); // no chained command
|
|
outPkt.setParameter(1, 0);
|
|
outPkt.setParameter(2, 0); // bytes remaining, for pipes only
|
|
outPkt.setParameter(3, 0); // data compaction mode
|
|
outPkt.setParameter(4, 0); // reserved
|
|
outPkt.setParameter(5, rdlen); // data length
|
|
outPkt.setParameter(6, dataPos - RFCNetBIOSProtocol.HEADER_LEN);
|
|
// offset to data
|
|
|
|
// Clear the reserved parameters
|
|
|
|
for (int i = 7; i < 12; i++)
|
|
outPkt.setParameter(i, 0);
|
|
|
|
// Set the byte count
|
|
|
|
outPkt.setByteCount((dataPos + rdlen) - outPkt.getByteOffset());
|
|
|
|
// Send the read andX response
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
|
|
/**
|
|
* Rename a file.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected void procRenameFile(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid rename file request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(1, 4) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree id from the received packet and validate that it is a valid
|
|
// connection id.
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasWriteAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Get the Unicode flag
|
|
|
|
boolean isUni = m_smbPkt.isUnicode();
|
|
|
|
// Read the data block
|
|
|
|
m_smbPkt.resetBytePointer();
|
|
|
|
// Extract the old file name
|
|
|
|
if (m_smbPkt.unpackByte() != DataType.ASCII)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
String oldName = m_smbPkt.unpackString(isUni);
|
|
if (oldName == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Extract the new file name
|
|
|
|
if (m_smbPkt.unpackByte() != DataType.ASCII)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
String newName = m_smbPkt.unpackString(isUni);
|
|
if (oldName == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_FILE))
|
|
logger.debug("File Rename [" + m_smbPkt.getTreeId() + "] old name=" + oldName + ", new name=" + newName);
|
|
|
|
// Access the disk interface and rename the requested file
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface that is associated with the shared device
|
|
|
|
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Rename the requested file
|
|
|
|
disk.renameFile(m_sess, conn, oldName, newName);
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
|
|
// Failed to open the file
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSFileNotFound, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Build the rename file response
|
|
|
|
outPkt.setParameterCount(0);
|
|
outPkt.setByteCount(0);
|
|
|
|
// Send the response packet
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
|
|
/**
|
|
* Process the SMB session setup request.
|
|
*
|
|
* @param outPkt Response SMB packet.
|
|
*/
|
|
protected void procSessionSetup(SMBSrvPacket outPkt) throws SMBSrvException, IOException,
|
|
TooManyConnectionsException
|
|
{
|
|
|
|
// Extract the client details from the session setup request
|
|
|
|
int dataPos = m_smbPkt.getByteOffset();
|
|
int dataLen = m_smbPkt.getByteCount();
|
|
byte[] buf = m_smbPkt.getBuffer();
|
|
|
|
// Extract the session details
|
|
|
|
int maxBufSize = m_smbPkt.getParameter(2);
|
|
int maxMpx = m_smbPkt.getParameter(3);
|
|
int vcNum = m_smbPkt.getParameter(4);
|
|
|
|
// Extract the password string
|
|
|
|
byte[] pwd = null;
|
|
int pwdLen = m_smbPkt.getParameter(7);
|
|
|
|
if (pwdLen > 0)
|
|
{
|
|
pwd = new byte[pwdLen];
|
|
for (int i = 0; i < pwdLen; i++)
|
|
pwd[i] = buf[dataPos + i];
|
|
dataPos += pwdLen;
|
|
dataLen -= pwdLen;
|
|
}
|
|
|
|
// Extract the user name string
|
|
|
|
String user = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (user == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Update the buffer pointers
|
|
|
|
dataLen -= user.length() + 1;
|
|
dataPos += user.length() + 1;
|
|
}
|
|
|
|
// Extract the clients primary domain name string
|
|
|
|
String domain = "";
|
|
|
|
if (dataLen > 0)
|
|
{
|
|
|
|
// Extract the callers domain name
|
|
|
|
domain = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (domain == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Update the buffer pointers
|
|
|
|
dataLen -= domain.length() + 1;
|
|
dataPos += domain.length() + 1;
|
|
}
|
|
}
|
|
|
|
// Extract the clients native operating system
|
|
|
|
String clientOS = "";
|
|
|
|
if (dataLen > 0)
|
|
{
|
|
|
|
// Extract the callers operating system name
|
|
|
|
clientOS = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (clientOS == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
|
|
logger.debug("Session setup from user=" + user + ", password=" + pwd + ", domain=" + domain + ", os="
|
|
+ clientOS + ", VC=" + vcNum + ", maxBuf=" + maxBufSize + ", maxMpx=" + maxMpx);
|
|
|
|
// Store the client maximum buffer size and maximum multiplexed requests count
|
|
|
|
m_sess.setClientMaximumBufferSize(maxBufSize);
|
|
m_sess.setClientMaximumMultiplex(maxMpx);
|
|
|
|
// Create the client information and store in the session
|
|
|
|
ClientInfo client = new ClientInfo(user, pwd);
|
|
client.setDomain(domain);
|
|
client.setOperatingSystem(clientOS);
|
|
if (m_sess.hasRemoteAddress())
|
|
client.setClientAddress(m_sess.getRemoteAddress().getHostAddress());
|
|
|
|
if (m_sess.getClientInformation() == null)
|
|
{
|
|
|
|
// Set the session client details
|
|
|
|
m_sess.setClientInformation(client);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Get the current client details from the session
|
|
|
|
ClientInfo curClient = m_sess.getClientInformation();
|
|
|
|
if (curClient.getUserName() == null || curClient.getUserName().length() == 0)
|
|
{
|
|
|
|
// Update the client information
|
|
|
|
m_sess.setClientInformation(client);
|
|
}
|
|
else
|
|
{
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
|
|
logger.debug("Session already has client information set");
|
|
}
|
|
}
|
|
|
|
// Authenticate the user, if the server is using user mode security
|
|
|
|
CifsAuthenticator auth = getSession().getSMBServer().getAuthenticator();
|
|
boolean isGuest = false;
|
|
|
|
if (auth != null)
|
|
{
|
|
|
|
// Validate the user
|
|
|
|
int sts = auth.authenticateUser(client, m_sess, CifsAuthenticator.LANMAN);
|
|
if (sts > 0 && (sts & CifsAuthenticator.AUTH_GUEST) != 0)
|
|
isGuest = true;
|
|
else if (sts != CifsAuthenticator.AUTH_ALLOW)
|
|
{
|
|
|
|
// Invalid user, reject the session setup request
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Set the guest flag for the client and logged on status
|
|
|
|
client.setGuest(isGuest);
|
|
getSession().setLoggedOn(true);
|
|
|
|
// Build the session setup response SMB
|
|
|
|
outPkt.setParameterCount(3);
|
|
outPkt.setParameter(0, 0); // No chained response
|
|
outPkt.setParameter(1, 0); // Offset to chained response
|
|
outPkt.setParameter(2, isGuest ? 1 : 0);
|
|
outPkt.setByteCount(0);
|
|
|
|
outPkt.setTreeId(0);
|
|
outPkt.setUserId(0);
|
|
|
|
// Set the various flags
|
|
|
|
// outPkt.setFlags( SMBSrvPacket.FLG_CASELESS);
|
|
int flags = outPkt.getFlags();
|
|
flags &= ~SMBSrvPacket.FLG_CASELESS;
|
|
outPkt.setFlags(flags);
|
|
outPkt.setFlags2(SMBSrvPacket.FLG2_LONGFILENAMES);
|
|
|
|
// Pack the OS, dialect and domain name strings.
|
|
|
|
int pos = outPkt.getByteOffset();
|
|
buf = outPkt.getBuffer();
|
|
|
|
pos = DataPacker.putString("Java", buf, pos, true);
|
|
pos = DataPacker.putString("JLAN Server " + m_sess.getServer().isVersion(), buf, pos, true);
|
|
pos = DataPacker.putString(m_sess.getServer().getConfiguration().getDomainName(), buf, pos, true);
|
|
|
|
outPkt.setByteCount(pos - outPkt.getByteOffset());
|
|
|
|
// Check if there is a chained command, or commands
|
|
|
|
if (m_smbPkt.hasAndXCommand() && dataPos < m_smbPkt.getReceivedLength())
|
|
{
|
|
|
|
// Process any chained commands, AndX
|
|
|
|
pos = procAndXCommands(outPkt);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Indicate that there are no chained replies
|
|
|
|
outPkt.setAndXCommand(SMBSrvPacket.NO_ANDX_CMD);
|
|
}
|
|
|
|
// Send the negotiate response
|
|
|
|
m_sess.sendResponseSMB(outPkt, pos);
|
|
|
|
// Update the session state
|
|
|
|
m_sess.setState(SMBSrvSessionState.SMBSESSION);
|
|
|
|
// Notify listeners that a user has logged onto the session
|
|
|
|
m_sess.getSMBServer().sessionLoggedOn(m_sess);
|
|
}
|
|
|
|
/**
|
|
* Process a transact2 request. The transact2 can contain many different sub-requests.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected void procTransact2(SMBSrvPacket outPkt) throws IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that we received enough parameters for a transact2 request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(15, 0) == false)
|
|
{
|
|
|
|
// Not enough parameters for a valid transact2 request
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( m_smbPkt.getUserId());
|
|
if ( vc == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree id from the received packet and validate that it is a valid
|
|
// connection id.
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Create a transact packet using the received SMB packet
|
|
|
|
SMBSrvTransPacket tranPkt = new SMBSrvTransPacket(m_smbPkt.getBuffer());
|
|
|
|
// Create a transact buffer to hold the transaction setup, parameter and data blocks
|
|
|
|
SrvTransactBuffer transBuf = null;
|
|
int subCmd = tranPkt.getSubFunction();
|
|
|
|
if (tranPkt.getTotalParameterCount() == tranPkt.getParameterBlockCount()
|
|
&& tranPkt.getTotalDataCount() == tranPkt.getDataBlockCount())
|
|
{
|
|
|
|
// Create a transact buffer using the packet buffer, the entire request is contained in
|
|
// a single
|
|
// packet
|
|
|
|
transBuf = new SrvTransactBuffer(tranPkt);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Create a transact buffer to hold the multiple transact request parameter/data blocks
|
|
|
|
transBuf = new SrvTransactBuffer(tranPkt.getSetupCount(), tranPkt.getTotalParameterCount(), tranPkt
|
|
.getTotalDataCount());
|
|
transBuf.setType(tranPkt.getCommand());
|
|
transBuf.setFunction(subCmd);
|
|
|
|
// Append the setup, parameter and data blocks to the transaction data
|
|
|
|
byte[] buf = tranPkt.getBuffer();
|
|
|
|
transBuf.appendSetup(buf, tranPkt.getSetupOffset(), tranPkt.getSetupCount() * 2);
|
|
transBuf.appendParameter(buf, tranPkt.getParameterBlockOffset(), tranPkt.getParameterBlockCount());
|
|
transBuf.appendData(buf, tranPkt.getDataBlockOffset(), tranPkt.getDataBlockCount());
|
|
}
|
|
|
|
// Set the return data limits for the transaction
|
|
|
|
transBuf.setReturnLimits(tranPkt.getMaximumReturnSetupCount(), tranPkt.getMaximumReturnParameterCount(),
|
|
tranPkt.getMaximumReturnDataCount());
|
|
|
|
// Check for a multi-packet transaction, for a multi-packet transaction we just acknowledge
|
|
// the receive with
|
|
// an empty response SMB
|
|
|
|
if (transBuf.isMultiPacket())
|
|
{
|
|
|
|
// Save the partial transaction data
|
|
|
|
vc.setTransaction(transBuf);
|
|
|
|
// Send an intermediate acknowedgement response
|
|
|
|
m_sess.sendSuccessResponseSMB();
|
|
return;
|
|
}
|
|
|
|
// Check if the transaction is on the IPC$ named pipe, the request requires special
|
|
// processing
|
|
|
|
if (conn.getSharedDevice().getType() == ShareType.ADMINPIPE)
|
|
{
|
|
IPCHandler.procTransaction(vc, transBuf, m_sess, outPkt);
|
|
return;
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TRAN))
|
|
logger.debug("Transaction [" + m_smbPkt.getTreeId() + "] tbuf=" + transBuf);
|
|
|
|
// Process the transaction buffer
|
|
|
|
processTransactionBuffer(transBuf, outPkt);
|
|
}
|
|
|
|
/**
|
|
* Process a transact2 secondary request.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected void procTransact2Secondary(SMBSrvPacket outPkt) throws IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that we received enough parameters for a transact2 request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(8, 0) == false)
|
|
{
|
|
|
|
// Not enough parameters for a valid transact2 request
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( m_smbPkt.getUserId());
|
|
if ( vc == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree id from the received packet and validate that it is a valid
|
|
// connection id.
|
|
|
|
int treeId = m_smbPkt.getTreeId();
|
|
TreeConnection conn = vc.findConnection(treeId);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Check if there is an active transaction, and it is an NT transaction
|
|
|
|
if (vc.hasTransaction() == false
|
|
|| (vc.getTransaction().isType() == PacketType.Transaction && m_smbPkt.getCommand() != PacketType.TransactionSecond)
|
|
|| (vc.getTransaction().isType() == PacketType.Transaction2 && m_smbPkt.getCommand() != PacketType.Transaction2Second))
|
|
{
|
|
|
|
// No transaction to continue, or packet does not match the existing transaction, return
|
|
// an error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Create an NT transaction using the received packet
|
|
|
|
SMBSrvTransPacket tpkt = new SMBSrvTransPacket(m_smbPkt.getBuffer());
|
|
byte[] buf = tpkt.getBuffer();
|
|
SrvTransactBuffer transBuf = vc.getTransaction();
|
|
|
|
// Append the parameter data to the transaction buffer, if any
|
|
|
|
int plen = tpkt.getSecondaryParameterBlockCount();
|
|
if (plen > 0)
|
|
{
|
|
|
|
// Append the data to the parameter buffer
|
|
|
|
DataBuffer paramBuf = transBuf.getParameterBuffer();
|
|
paramBuf.appendData(buf, tpkt.getSecondaryParameterBlockOffset(), plen);
|
|
}
|
|
|
|
// Append the data block to the transaction buffer, if any
|
|
|
|
int dlen = tpkt.getSecondaryDataBlockCount();
|
|
if (dlen > 0)
|
|
{
|
|
|
|
// Append the data to the data buffer
|
|
|
|
DataBuffer dataBuf = transBuf.getDataBuffer();
|
|
dataBuf.appendData(buf, tpkt.getSecondaryDataBlockOffset(), dlen);
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TRAN))
|
|
logger.debug("Transaction Secondary [" + treeId + "] paramLen=" + plen + ", dataLen=" + dlen);
|
|
|
|
// Check if the transaction has been received or there are more sections to be received
|
|
|
|
int totParam = tpkt.getTotalParameterCount();
|
|
int totData = tpkt.getTotalDataCount();
|
|
|
|
int paramDisp = tpkt.getParameterBlockDisplacement();
|
|
int dataDisp = tpkt.getDataBlockDisplacement();
|
|
|
|
if ((paramDisp + plen) == totParam && (dataDisp + dlen) == totData)
|
|
{
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TRAN))
|
|
logger.debug("Transaction complete, processing ...");
|
|
|
|
// Clear the in progress transaction
|
|
|
|
vc.setTransaction(null);
|
|
|
|
// Check if the transaction is on the IPC$ named pipe, the request requires special
|
|
// processing
|
|
|
|
if (conn.getSharedDevice().getType() == ShareType.ADMINPIPE)
|
|
{
|
|
IPCHandler.procTransaction(vc, transBuf, m_sess, outPkt);
|
|
return;
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TRAN))
|
|
logger.debug("Transaction second [" + treeId + "] tbuf=" + transBuf);
|
|
|
|
// Process the transaction
|
|
|
|
processTransactionBuffer(transBuf, outPkt);
|
|
}
|
|
else
|
|
{
|
|
|
|
// There are more transaction parameter/data sections to be received, return an
|
|
// intermediate response
|
|
|
|
m_sess.sendSuccessResponseSMB();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a transaction buffer
|
|
*
|
|
* @param tbuf TransactBuffer
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception IOException If a network error occurs
|
|
* @exception SMBSrvException If an SMB error occurs
|
|
*/
|
|
private final void processTransactionBuffer(SrvTransactBuffer tbuf, SMBSrvPacket outPkt) throws IOException,
|
|
SMBSrvException
|
|
{
|
|
|
|
// Get the transaction sub-command code and validate
|
|
|
|
switch (tbuf.getFunction())
|
|
{
|
|
|
|
// Start a file search
|
|
|
|
case PacketType.Trans2FindFirst:
|
|
procTrans2FindFirst(tbuf, outPkt);
|
|
break;
|
|
|
|
// Continue a file search
|
|
|
|
case PacketType.Trans2FindNext:
|
|
procTrans2FindNext(tbuf, outPkt);
|
|
break;
|
|
|
|
// Query file system information
|
|
|
|
case PacketType.Trans2QueryFileSys:
|
|
procTrans2QueryFileSys(tbuf, outPkt);
|
|
break;
|
|
|
|
// Query path
|
|
|
|
case PacketType.Trans2QueryPath:
|
|
procTrans2QueryPath(tbuf, outPkt);
|
|
break;
|
|
|
|
// Unknown transact2 command
|
|
|
|
default:
|
|
|
|
// Return an unrecognized command error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a transact2 file search request.
|
|
*
|
|
* @param tbuf Transaction request details
|
|
* @param outPkt Packet to use for the reply.
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected final void procTrans2FindFirst(SrvTransactBuffer tbuf, SMBSrvPacket outPkt) throws java.io.IOException,
|
|
SMBSrvException
|
|
{
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( m_smbPkt.getUserId());
|
|
if ( vc == null) {
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
int treeId = m_smbPkt.getTreeId();
|
|
TreeConnection conn = vc.findConnection(treeId);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Get the search parameters
|
|
|
|
DataBuffer paramBuf = tbuf.getParameterBuffer();
|
|
|
|
int srchAttr = paramBuf.getShort();
|
|
int maxFiles = paramBuf.getShort();
|
|
int srchFlag = paramBuf.getShort();
|
|
int infoLevl = paramBuf.getShort();
|
|
paramBuf.skipBytes(4);
|
|
|
|
String srchPath = paramBuf.getString(tbuf.isUnicode());
|
|
|
|
// Check if the search path is valid
|
|
|
|
if (srchPath == null || srchPath.length() == 0)
|
|
{
|
|
|
|
// Invalid search request
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Access the shared device disk interface
|
|
|
|
SearchContext ctx = null;
|
|
DiskInterface disk = null;
|
|
int searchId = -1;
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface
|
|
|
|
disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Allocate a search slot for the new search
|
|
|
|
searchId = vc.allocateSearchSlot();
|
|
if (searchId == -1)
|
|
{
|
|
|
|
// Failed to allocate a slot for the new search
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNoResourcesAvailable, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("Start trans search [" + searchId + "] - " + srchPath + ", attr=0x"
|
|
+ Integer.toHexString(srchAttr) + ", maxFiles=" + maxFiles + ", infoLevel=" + infoLevl
|
|
+ ", flags=0x" + Integer.toHexString(srchFlag));
|
|
|
|
// Start a new search
|
|
|
|
ctx = disk.startSearch(m_sess, conn, srchPath, srchAttr);
|
|
if (ctx != null)
|
|
{
|
|
|
|
// Store details of the search in the context
|
|
|
|
ctx.setTreeId(treeId);
|
|
ctx.setMaximumFiles(maxFiles);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Failed to start the search, return a no more files error
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSFileNotFound, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Save the search context
|
|
|
|
vc.setSearchContext(searchId, ctx);
|
|
|
|
// Create the reply transact buffer
|
|
|
|
SrvTransactBuffer replyBuf = new SrvTransactBuffer(tbuf);
|
|
DataBuffer dataBuf = replyBuf.getDataBuffer();
|
|
|
|
// Determine the maximum return data length
|
|
|
|
int maxLen = replyBuf.getReturnDataLimit();
|
|
|
|
// Check if resume keys are required
|
|
|
|
boolean resumeReq = (srchFlag & FindFirstNext.ReturnResumeKey) != 0 ? true : false;
|
|
|
|
// Loop until we have filled the return buffer or there are no more files to return
|
|
|
|
int fileCnt = 0;
|
|
int packLen = 0;
|
|
int lastNameOff = 0;
|
|
|
|
boolean pktDone = false;
|
|
boolean searchDone = false;
|
|
|
|
FileInfo info = new FileInfo();
|
|
|
|
while (pktDone == false && fileCnt < maxFiles)
|
|
{
|
|
|
|
// Get file information from the search
|
|
|
|
if (ctx.nextFileInfo(info) == false)
|
|
{
|
|
|
|
// No more files
|
|
|
|
pktDone = true;
|
|
searchDone = true;
|
|
}
|
|
|
|
// Check if the file information will fit into the return buffer
|
|
|
|
else if (FindInfoPacker.calcInfoSize(info, infoLevl, false, true) <= maxLen)
|
|
{
|
|
|
|
// Pack a dummy resume key, if required
|
|
|
|
if (resumeReq)
|
|
{
|
|
dataBuf.putZeros(4);
|
|
maxLen -= 4;
|
|
}
|
|
|
|
// Save the offset to the last file information structure
|
|
|
|
lastNameOff = dataBuf.getPosition();
|
|
|
|
// Pack the file information
|
|
|
|
packLen = FindInfoPacker.packInfo(info, dataBuf, infoLevl, tbuf.isUnicode());
|
|
|
|
// Update the file count for this packet
|
|
|
|
fileCnt++;
|
|
|
|
// Recalculate the remaining buffer space
|
|
|
|
maxLen -= packLen;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the search restart point
|
|
|
|
ctx.restartAt(info);
|
|
|
|
// No more buffer space
|
|
|
|
pktDone = true;
|
|
}
|
|
}
|
|
|
|
// Pack the parameter block
|
|
|
|
paramBuf = replyBuf.getParameterBuffer();
|
|
|
|
paramBuf.putShort(searchId);
|
|
paramBuf.putShort(fileCnt);
|
|
paramBuf.putShort(ctx.hasMoreFiles() ? 0 : 1);
|
|
paramBuf.putShort(0);
|
|
paramBuf.putShort(lastNameOff);
|
|
|
|
// Send the transaction response
|
|
|
|
SMBSrvTransPacket tpkt = new SMBSrvTransPacket(outPkt.getBuffer());
|
|
tpkt.doTransactionResponse(m_sess, replyBuf);
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("Search [" + searchId + "] Returned " + fileCnt + " files, moreFiles="
|
|
+ ctx.hasMoreFiles());
|
|
|
|
// Check if the search is complete
|
|
|
|
if (searchDone == true)
|
|
{
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("End start search [" + searchId + "] (Search complete)");
|
|
|
|
// Release the search context
|
|
|
|
vc.deallocateSearchSlot(searchId);
|
|
}
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
|
|
// Deallocate the search
|
|
|
|
if (searchId != -1)
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Search path does not exist
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSNoMoreFiles, SMBStatus.ErrDos);
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Deallocate the search
|
|
|
|
if (searchId != -1)
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
}
|
|
catch (UnsupportedInfoLevelException ex)
|
|
{
|
|
|
|
// Deallocate the search
|
|
|
|
if (searchId != -1)
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Requested information level is not supported
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a transact2 file search continue request.
|
|
*
|
|
* @param tbuf Transaction request details
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected final void procTrans2FindNext(SrvTransactBuffer tbuf, SMBSrvPacket outPkt) throws java.io.IOException,
|
|
SMBSrvException
|
|
{
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( m_smbPkt.getUserId());
|
|
if ( vc == null) {
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
int treeId = m_smbPkt.getTreeId();
|
|
TreeConnection conn = vc.findConnection(treeId);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Get the search parameters
|
|
|
|
DataBuffer paramBuf = tbuf.getParameterBuffer();
|
|
|
|
int searchId = paramBuf.getShort();
|
|
int maxFiles = paramBuf.getShort();
|
|
int infoLevl = paramBuf.getShort();
|
|
int reskey = paramBuf.getInt();
|
|
int srchFlag = paramBuf.getShort();
|
|
|
|
String resumeName = paramBuf.getString(tbuf.isUnicode());
|
|
|
|
// Access the shared device disk interface
|
|
|
|
SearchContext ctx = null;
|
|
DiskInterface disk = null;
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface
|
|
|
|
disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Retrieve the search context
|
|
|
|
ctx = vc.getSearchContext(searchId);
|
|
if (ctx == null)
|
|
{
|
|
|
|
// DEBUG
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("Search context null - [" + searchId + "]");
|
|
|
|
// Invalid search handle
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSNoMoreFiles, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("Continue search [" + searchId + "] - " + resumeName + ", maxFiles=" + maxFiles
|
|
+ ", infoLevel=" + infoLevl + ", flags=0x" + Integer.toHexString(srchFlag));
|
|
|
|
// Create the reply transaction buffer
|
|
|
|
SrvTransactBuffer replyBuf = new SrvTransactBuffer(tbuf);
|
|
DataBuffer dataBuf = replyBuf.getDataBuffer();
|
|
|
|
// Determine the maximum return data length
|
|
|
|
int maxLen = replyBuf.getReturnDataLimit();
|
|
|
|
// Check if resume keys are required
|
|
|
|
boolean resumeReq = (srchFlag & FindFirstNext.ReturnResumeKey) != 0 ? true : false;
|
|
|
|
// Loop until we have filled the return buffer or there are no more files to return
|
|
|
|
int fileCnt = 0;
|
|
int packLen = 0;
|
|
int lastNameOff = 0;
|
|
|
|
boolean pktDone = false;
|
|
boolean searchDone = false;
|
|
|
|
FileInfo info = new FileInfo();
|
|
|
|
while (pktDone == false && fileCnt < maxFiles)
|
|
{
|
|
|
|
// Get file information from the search
|
|
|
|
if (ctx.nextFileInfo(info) == false)
|
|
{
|
|
|
|
// No more files
|
|
|
|
pktDone = true;
|
|
searchDone = true;
|
|
}
|
|
|
|
// Check if the file information will fit into the return buffer
|
|
|
|
else if (FindInfoPacker.calcInfoSize(info, infoLevl, false, true) <= maxLen)
|
|
{
|
|
|
|
// Pack a dummy resume key, if required
|
|
|
|
if (resumeReq)
|
|
dataBuf.putZeros(4);
|
|
|
|
// Save the offset to the last file information structure
|
|
|
|
lastNameOff = dataBuf.getPosition();
|
|
|
|
// Pack the file information
|
|
|
|
packLen = FindInfoPacker.packInfo(info, dataBuf, infoLevl, tbuf.isUnicode());
|
|
|
|
// Update the file count for this packet
|
|
|
|
fileCnt++;
|
|
|
|
// Recalculate the remaining buffer space
|
|
|
|
maxLen -= packLen;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the search restart point
|
|
|
|
ctx.restartAt(info);
|
|
|
|
// No more buffer space
|
|
|
|
pktDone = true;
|
|
}
|
|
}
|
|
|
|
// Pack the parameter block
|
|
|
|
paramBuf = replyBuf.getParameterBuffer();
|
|
|
|
paramBuf.putShort(fileCnt);
|
|
paramBuf.putShort(ctx.hasMoreFiles() ? 0 : 1);
|
|
paramBuf.putShort(0);
|
|
paramBuf.putShort(lastNameOff);
|
|
|
|
// Send the transaction response
|
|
|
|
SMBSrvTransPacket tpkt = new SMBSrvTransPacket(outPkt.getBuffer());
|
|
tpkt.doTransactionResponse(m_sess, replyBuf);
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("Search [" + searchId + "] Returned " + fileCnt + " files, moreFiles="
|
|
+ ctx.hasMoreFiles());
|
|
|
|
// Check if the search is complete
|
|
|
|
if (searchDone == true)
|
|
{
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_SEARCH))
|
|
logger.debug("End start search [" + searchId + "] (Search complete)");
|
|
|
|
// Release the search context
|
|
|
|
vc.deallocateSearchSlot(searchId);
|
|
}
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
|
|
// Deallocate the search
|
|
|
|
if (searchId != -1)
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Search path does not exist
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSNoMoreFiles, SMBStatus.ErrDos);
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Deallocate the search
|
|
|
|
if (searchId != -1)
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
}
|
|
catch (UnsupportedInfoLevelException ex)
|
|
{
|
|
|
|
// Deallocate the search
|
|
|
|
if (searchId != -1)
|
|
vc.deallocateSearchSlot(searchId);
|
|
|
|
// Requested information level is not supported
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a transact2 file system query request.
|
|
*
|
|
* @param tbuf Transaction request details
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected final void procTrans2QueryFileSys(SrvTransactBuffer tbuf, SMBSrvPacket outPkt)
|
|
throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Get the tree connection details
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Get the query file system required information level
|
|
|
|
DataBuffer paramBuf = tbuf.getParameterBuffer();
|
|
|
|
int infoLevl = paramBuf.getShort();
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_INFO))
|
|
logger.debug("Query File System Info - level = 0x" + Integer.toHexString(infoLevl));
|
|
|
|
// Access the shared device disk interface
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface and context
|
|
|
|
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
DiskDeviceContext diskCtx = (DiskDeviceContext) conn.getContext();
|
|
|
|
// Set the return parameter count, so that the data area position can be calculated.
|
|
|
|
outPkt.setParameterCount(10);
|
|
|
|
// Pack the disk information into the data area of the transaction reply
|
|
|
|
byte[] buf = outPkt.getBuffer();
|
|
int prmPos = DataPacker.longwordAlign(outPkt.getByteOffset());
|
|
int dataPos = prmPos; // no parameters returned
|
|
|
|
// Create a data buffer using the SMB packet. The response should always fit into a
|
|
// single
|
|
// reply packet.
|
|
|
|
DataBuffer replyBuf = new DataBuffer(buf, dataPos, buf.length - dataPos);
|
|
|
|
// Determine the information level requested
|
|
|
|
SrvDiskInfo diskInfo = null;
|
|
VolumeInfo volInfo = null;
|
|
|
|
switch (infoLevl)
|
|
{
|
|
|
|
// Standard disk information
|
|
|
|
case DiskInfoPacker.InfoStandard:
|
|
|
|
// Get the disk information
|
|
|
|
diskInfo = getDiskInformation(disk, diskCtx);
|
|
|
|
// Pack the disk information into the return data packet
|
|
|
|
DiskInfoPacker.packStandardInfo(diskInfo, replyBuf);
|
|
break;
|
|
|
|
// Volume label information
|
|
|
|
case DiskInfoPacker.InfoVolume:
|
|
|
|
// Get the volume label information
|
|
|
|
volInfo = getVolumeInformation(disk, diskCtx);
|
|
|
|
// Pack the volume label information
|
|
|
|
DiskInfoPacker.packVolumeInfo(volInfo, replyBuf, tbuf.isUnicode());
|
|
break;
|
|
|
|
// Full volume information
|
|
|
|
case DiskInfoPacker.InfoFsVolume:
|
|
|
|
// Get the volume information
|
|
|
|
volInfo = getVolumeInformation(disk, diskCtx);
|
|
|
|
// Pack the volume information
|
|
|
|
DiskInfoPacker.packFsVolumeInformation(volInfo, replyBuf, tbuf.isUnicode());
|
|
break;
|
|
|
|
// Filesystem size information
|
|
|
|
case DiskInfoPacker.InfoFsSize:
|
|
|
|
// Get the disk information
|
|
|
|
diskInfo = getDiskInformation(disk, diskCtx);
|
|
|
|
// Pack the disk information into the return data packet
|
|
|
|
DiskInfoPacker.packFsSizeInformation(diskInfo, replyBuf);
|
|
break;
|
|
|
|
// Filesystem device information
|
|
|
|
case DiskInfoPacker.InfoFsDevice:
|
|
DiskInfoPacker.packFsDevice(0, 0, replyBuf);
|
|
break;
|
|
|
|
// Filesystem attribute information
|
|
|
|
case DiskInfoPacker.InfoFsAttribute:
|
|
DiskInfoPacker.packFsAttribute(0, 255, "JLAN", tbuf.isUnicode(), replyBuf);
|
|
break;
|
|
}
|
|
|
|
// Check if any data was packed, if not then the information level is not supported
|
|
|
|
if (replyBuf.getPosition() == dataPos)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
int dataLen = replyBuf.getLength();
|
|
SMBSrvTransPacket.initTransactReply(outPkt, 0, prmPos, dataLen, dataPos);
|
|
outPkt.setByteCount(replyBuf.getPosition() - outPkt.getByteOffset());
|
|
|
|
// Send the transact reply
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a transact2 query path information request.
|
|
*
|
|
* @param tbuf Transaction request details
|
|
* @param outPkt SMBSrvPacket
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
*/
|
|
protected final void procTrans2QueryPath(SrvTransactBuffer tbuf, SMBSrvPacket outPkt) throws java.io.IOException,
|
|
SMBSrvException
|
|
{
|
|
|
|
// Get the tree connection details
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasReadAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Get the query path information level and file/directory name
|
|
|
|
DataBuffer paramBuf = tbuf.getParameterBuffer();
|
|
|
|
int infoLevl = paramBuf.getShort();
|
|
paramBuf.skipBytes(4);
|
|
|
|
String path = paramBuf.getString(tbuf.isUnicode());
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_INFO))
|
|
logger.debug("Query Path - level = 0x" + Integer.toHexString(infoLevl) + ", path = " + path);
|
|
|
|
// Access the shared device disk interface
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface
|
|
|
|
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Set the return parameter count, so that the data area position can be calculated.
|
|
|
|
outPkt.setParameterCount(10);
|
|
|
|
// Pack the file information into the data area of the transaction reply
|
|
|
|
byte[] buf = outPkt.getBuffer();
|
|
int prmPos = DataPacker.longwordAlign(outPkt.getByteOffset());
|
|
int dataPos = prmPos; // no parameters returned
|
|
|
|
// Create a data buffer using the SMB packet. The response should always fit into a
|
|
// single
|
|
// reply packet.
|
|
|
|
DataBuffer replyBuf = new DataBuffer(buf, dataPos, buf.length - dataPos);
|
|
|
|
// Get the file information
|
|
|
|
FileInfo fileInfo = disk.getFileInformation(m_sess, conn, path);
|
|
|
|
if (fileInfo == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTObjectNotFound, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
// Pack the file information into the return data packet
|
|
|
|
int dataLen = QueryInfoPacker.packInfo(fileInfo, replyBuf, infoLevl, true);
|
|
|
|
// Check if any data was packed, if not then the information level is not supported
|
|
|
|
if (dataLen == 0)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
|
|
SMBSrvTransPacket.initTransactReply(outPkt, 0, prmPos, dataLen, dataPos);
|
|
outPkt.setByteCount(replyBuf.getPosition() - outPkt.getByteOffset());
|
|
|
|
// Send the transact reply
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
|
|
// Requested file does not exist
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTObjectNotFound, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
catch (UnsupportedInfoLevelException ex)
|
|
{
|
|
|
|
// Requested information level is not supported
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.NTErr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process the SMB tree connect request.
|
|
*
|
|
* @param outPkt Response SMB packet.
|
|
* @exception java.io.IOException If an I/O error occurs
|
|
* @exception SMBSrvException If an SMB protocol error occurs
|
|
* @exception TooManyConnectionsException Too many concurrent connections on this session.
|
|
*/
|
|
|
|
protected void procTreeConnectAndX(SMBSrvPacket outPkt) throws SMBSrvException, TooManyConnectionsException,
|
|
java.io.IOException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid tree connect request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(4, 3) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the virtual circuit for the request
|
|
|
|
VirtualCircuit vc = m_sess.findVirtualCircuit( m_smbPkt.getUserId());
|
|
if ( vc == null) {
|
|
m_sess.sendErrorResponseSMB(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Extract the parameters
|
|
|
|
int flags = m_smbPkt.getParameter(2);
|
|
int pwdLen = m_smbPkt.getParameter(3);
|
|
|
|
// Get the data bytes position and length
|
|
|
|
int dataPos = m_smbPkt.getByteOffset();
|
|
int dataLen = m_smbPkt.getByteCount();
|
|
byte[] buf = m_smbPkt.getBuffer();
|
|
|
|
// Extract the password string
|
|
|
|
String pwd = null;
|
|
|
|
if (pwdLen > 0)
|
|
{
|
|
pwd = new String(buf, dataPos, pwdLen);
|
|
dataPos += pwdLen;
|
|
dataLen -= pwdLen;
|
|
}
|
|
|
|
// Extract the requested share name, as a UNC path
|
|
|
|
String uncPath = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (uncPath == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Extract the service type string
|
|
|
|
dataPos += uncPath.length() + 1; // null terminated
|
|
dataLen -= uncPath.length() + 1; // null terminated
|
|
|
|
String service = DataPacker.getString(buf, dataPos, dataLen);
|
|
if (service == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Convert the service type to a shared device type, client may specify '?????' in which
|
|
// case we ignore the error.
|
|
|
|
int servType = ShareType.ServiceAsType(service);
|
|
if (servType == ShareType.UNKNOWN && service.compareTo("?????") != 0)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TREE))
|
|
logger.debug("Tree Connect AndX - " + uncPath + ", " + service);
|
|
|
|
// Parse the requested share name
|
|
|
|
PCShare share = null;
|
|
|
|
try
|
|
{
|
|
share = new PCShare(uncPath);
|
|
}
|
|
catch (InvalidUNCPathException ex)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Map the IPC$ share to the admin pipe type
|
|
|
|
if (servType == ShareType.NAMEDPIPE && share.getShareName().compareTo("IPC$") == 0)
|
|
servType = ShareType.ADMINPIPE;
|
|
|
|
// Find the requested shared device
|
|
|
|
SharedDevice shareDev = null;
|
|
|
|
try
|
|
{
|
|
|
|
// Get/create the shared device
|
|
|
|
shareDev = m_sess.getSMBServer().findShare(share.getNodeName(), share.getShareName(), servType,
|
|
getSession(), true);
|
|
}
|
|
catch (InvalidUserException ex)
|
|
{
|
|
|
|
// Return a logon failure status
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
// Return a general status, bad network name
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidNetworkName, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the share is valid
|
|
|
|
if (shareDev == null || (servType != ShareType.UNKNOWN && shareDev.getType() != servType))
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Authenticate the share connection depending upon the security mode the server is running
|
|
// under
|
|
|
|
CifsAuthenticator auth = getSession().getSMBServer().getAuthenticator();
|
|
int filePerm = FileAccess.Writeable;
|
|
|
|
if (auth != null)
|
|
{
|
|
|
|
// Validate the share connection
|
|
|
|
filePerm = auth.authenticateShareConnect(m_sess.getClientInformation(), shareDev, pwd, m_sess);
|
|
if (filePerm < 0)
|
|
{
|
|
|
|
// Invalid share connection request
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVNoAccessRights, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Allocate a tree id for the new connection
|
|
|
|
int treeId = vc.addConnection(shareDev);
|
|
outPkt.setTreeId(treeId);
|
|
|
|
// Set the file permission that this user has been granted for this share
|
|
|
|
TreeConnection tree = vc.findConnection(treeId);
|
|
tree.setPermission(filePerm);
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_TREE))
|
|
logger.debug("Tree Connect AndX - Allocated Tree Id = " + treeId + ", Permission = "
|
|
+ FileAccess.asString(filePerm));
|
|
|
|
// Build the tree connect response
|
|
|
|
outPkt.setParameterCount(3);
|
|
outPkt.setAndXCommand(0xFF); // no chained reply
|
|
outPkt.setParameter(1, 0);
|
|
outPkt.setParameter(2, 0);
|
|
|
|
// Pack the service type
|
|
|
|
int pos = outPkt.getByteOffset();
|
|
pos = DataPacker.putString(ShareType.TypeAsService(shareDev.getType()), buf, pos, true);
|
|
outPkt.setByteCount(pos - outPkt.getByteOffset());
|
|
|
|
// Send the response
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
|
|
// Inform the driver that a connection has been opened
|
|
|
|
if (tree.getInterface() != null)
|
|
tree.getInterface().treeOpened(m_sess, tree);
|
|
}
|
|
|
|
/**
|
|
* Process the file write request.
|
|
*
|
|
* @param outPkt SMBSrvPacket
|
|
*/
|
|
protected final void procWriteAndX(SMBSrvPacket outPkt) throws java.io.IOException, SMBSrvException
|
|
{
|
|
|
|
// Check that the received packet looks like a valid write andX request
|
|
|
|
if (m_smbPkt.checkPacketIsValid(12, 0) == false)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVUnrecognizedCommand, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Get the tree connection details
|
|
|
|
TreeConnection conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.SRVInvalidTID, SMBStatus.ErrSrv);
|
|
return;
|
|
}
|
|
|
|
// Check if the user has the required access permission
|
|
|
|
if (conn.hasWriteAccess() == false)
|
|
{
|
|
|
|
// User does not have the required access rights
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// If the connection is to the IPC$ remote admin named pipe pass the request to the IPC
|
|
// handler.
|
|
|
|
if (conn.getSharedDevice().getType() == ShareType.ADMINPIPE)
|
|
{
|
|
|
|
// Use the IPC$ handler to process the request
|
|
|
|
IPCHandler.processIPCRequest(m_sess, outPkt);
|
|
return;
|
|
}
|
|
|
|
// Extract the write file parameters
|
|
|
|
int fid = m_smbPkt.getParameter(2);
|
|
int offset = m_smbPkt.getParameterLong(3);
|
|
int dataLen = m_smbPkt.getParameter(10);
|
|
int dataPos = m_smbPkt.getParameter(11) + RFCNetBIOSProtocol.HEADER_LEN;
|
|
|
|
NetworkFile netFile = conn.findFile(fid);
|
|
|
|
if (netFile == null)
|
|
{
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_FILEIO))
|
|
logger.debug("File Write AndX [" + netFile.getFileId() + "] : Size=" + dataLen + " ,Pos=" + offset);
|
|
|
|
// Write data to the file
|
|
|
|
byte[] buf = m_smbPkt.getBuffer();
|
|
int wrtlen = 0;
|
|
|
|
// Access the disk interface and write to the file
|
|
|
|
try
|
|
{
|
|
|
|
// Access the disk interface that is associated with the shared device
|
|
|
|
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
|
|
|
// Write to the file
|
|
|
|
wrtlen = disk.writeFile(m_sess, conn, netFile, buf, dataPos, dataLen, offset);
|
|
}
|
|
catch (InvalidDeviceInterfaceException ex)
|
|
{
|
|
|
|
// Failed to get/initialize the disk interface
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
|
|
return;
|
|
}
|
|
catch (java.io.IOException ex)
|
|
{
|
|
|
|
// Debug
|
|
|
|
logger.error("File Write Error [" + netFile.getFileId() + "] : ", ex);
|
|
|
|
// Failed to read the file
|
|
|
|
m_sess.sendErrorResponseSMB(SMBStatus.HRDWriteFault, SMBStatus.ErrHrd);
|
|
return;
|
|
}
|
|
|
|
// Return the count of bytes actually written
|
|
|
|
outPkt.setParameterCount(6);
|
|
outPkt.setAndXCommand(0xFF);
|
|
outPkt.setParameter(1, 0);
|
|
outPkt.setParameter(2, wrtlen);
|
|
outPkt.setParameter(3, 0); // remaining byte count for pipes only
|
|
outPkt.setParameter(4, 0); // reserved
|
|
outPkt.setParameter(5, 0); // "
|
|
outPkt.setByteCount(0);
|
|
|
|
// Send the write response
|
|
|
|
m_sess.sendResponseSMB(outPkt);
|
|
}
|
|
|
|
/**
|
|
* runProtocol method comment.
|
|
*/
|
|
public boolean runProtocol() throws java.io.IOException, SMBSrvException, TooManyConnectionsException
|
|
{
|
|
|
|
// Check if the SMB packet is initialized
|
|
|
|
if (m_smbPkt == null)
|
|
m_smbPkt = m_sess.getReceivePacket();
|
|
|
|
// Check if the received packet has a valid SMB signature
|
|
|
|
if (m_smbPkt.checkPacketSignature() == false)
|
|
throw new IOException("Invalid SMB signature");
|
|
|
|
// Determine if the request has a chained command, if so then we will copy the incoming
|
|
// request so that
|
|
// a chained reply can be built.
|
|
|
|
SMBSrvPacket outPkt = m_smbPkt;
|
|
boolean chainedCmd = hasChainedCommand(m_smbPkt);
|
|
|
|
if (chainedCmd)
|
|
{
|
|
|
|
// Debug
|
|
|
|
if (logger.isDebugEnabled() && m_sess.hasDebug(SMBSrvSession.DBG_STATE))
|
|
logger.debug("AndX Command = 0x" + Integer.toHexString(m_smbPkt.getAndXCommand()));
|
|
|
|
// Copy the request packet into a new packet for the reply
|
|
|
|
outPkt = new SMBSrvPacket(m_smbPkt);
|
|
}
|
|
|
|
// Reset the byte unpack offset
|
|
|
|
m_smbPkt.resetBytePointer();
|
|
|
|
// Determine the SMB command type
|
|
|
|
boolean handledOK = true;
|
|
|
|
switch (m_smbPkt.getCommand())
|
|
{
|
|
|
|
// Session setup
|
|
|
|
case PacketType.SessionSetupAndX:
|
|
procSessionSetup(outPkt);
|
|
break;
|
|
|
|
// Tree connect
|
|
|
|
case PacketType.TreeConnectAndX:
|
|
procTreeConnectAndX(outPkt);
|
|
break;
|
|
|
|
// Transaction2
|
|
|
|
case PacketType.Transaction2:
|
|
case PacketType.Transaction:
|
|
procTransact2(outPkt);
|
|
break;
|
|
|
|
// Transaction/transaction2 secondary
|
|
|
|
case PacketType.TransactionSecond:
|
|
case PacketType.Transaction2Second:
|
|
procTransact2Secondary(outPkt);
|
|
break;
|
|
|
|
// Close a search started via the FindFirst transaction2 command
|
|
|
|
case PacketType.FindClose2:
|
|
procFindClose(outPkt);
|
|
break;
|
|
|
|
// Open a file
|
|
|
|
case PacketType.OpenAndX:
|
|
procOpenAndX(outPkt);
|
|
break;
|
|
|
|
// Read a file
|
|
|
|
case PacketType.ReadAndX:
|
|
procReadAndX(outPkt);
|
|
break;
|
|
|
|
// Write to a file
|
|
|
|
case PacketType.WriteAndX:
|
|
procWriteAndX(outPkt);
|
|
break;
|
|
|
|
// Tree disconnect
|
|
|
|
case PacketType.TreeDisconnect:
|
|
procTreeDisconnect(outPkt);
|
|
break;
|
|
|
|
// Lock/unlock regions of a file
|
|
|
|
case PacketType.LockingAndX:
|
|
procLockingAndX(outPkt);
|
|
break;
|
|
|
|
// Logoff a user
|
|
|
|
case PacketType.LogoffAndX:
|
|
procLogoffAndX(outPkt);
|
|
break;
|
|
|
|
// Tree connection (without AndX batching)
|
|
|
|
case PacketType.TreeConnect:
|
|
super.runProtocol();
|
|
break;
|
|
|
|
// Rename file
|
|
|
|
case PacketType.RenameFile:
|
|
procRenameFile(outPkt);
|
|
break;
|
|
|
|
// Echo request
|
|
|
|
case PacketType.Echo:
|
|
super.procEcho(outPkt);
|
|
break;
|
|
|
|
// Default
|
|
|
|
default:
|
|
|
|
// Get the tree connection details, if it is a disk or printer type connection then pass
|
|
// the request to the
|
|
// core protocol handler
|
|
|
|
int treeId = m_smbPkt.getTreeId();
|
|
TreeConnection conn = null;
|
|
if (treeId != -1)
|
|
conn = m_sess.findTreeConnection(m_smbPkt);
|
|
|
|
if (conn != null)
|
|
{
|
|
|
|
// Check if this is a disk or print connection, if so then send the request to the
|
|
// core protocol handler
|
|
|
|
if (conn.getSharedDevice().getType() == ShareType.DISK
|
|
|| conn.getSharedDevice().getType() == ShareType.PRINTER)
|
|
{
|
|
|
|
// Chain to the core protocol handler
|
|
|
|
handledOK = super.runProtocol();
|
|
}
|
|
else if (conn.getSharedDevice().getType() == ShareType.ADMINPIPE)
|
|
{
|
|
|
|
// Send the request to IPC$ remote admin handler
|
|
|
|
IPCHandler.processIPCRequest(m_sess, outPkt);
|
|
handledOK = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Return the handled status
|
|
|
|
return handledOK;
|
|
}
|
|
} |