Derek Hulley 488450a988 Merged 1.4 to HEAD
svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4313 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4314 .
   svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4317 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4318 .


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4656 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2006-12-19 14:28:55 +00:00

797 lines
24 KiB
Java

/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.smb.server;
import java.io.IOException;
import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
import org.alfresco.filesys.server.filesys.TreeConnection;
import org.alfresco.filesys.smb.DataType;
import org.alfresco.filesys.smb.PacketType;
import org.alfresco.filesys.smb.SMBStatus;
import org.alfresco.filesys.smb.TransactBuffer;
import org.alfresco.filesys.smb.dcerpc.DCEBuffer;
import org.alfresco.filesys.smb.dcerpc.DCEBufferException;
import org.alfresco.filesys.smb.dcerpc.DCECommand;
import org.alfresco.filesys.smb.dcerpc.DCEDataPacker;
import org.alfresco.filesys.smb.dcerpc.DCEPipeType;
import org.alfresco.filesys.smb.dcerpc.UUID;
import org.alfresco.filesys.smb.dcerpc.server.DCEPipeFile;
import org.alfresco.filesys.smb.dcerpc.server.DCESrvPacket;
import org.alfresco.filesys.util.DataBuffer;
import org.alfresco.filesys.util.DataPacker;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* DCE/RPC Protocol Handler Class
*/
public class DCERPCHandler
{
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
/**
* Process a DCE/RPC request
*
* @param sess SMBSrvSession
* @param srvTrans SMBSrvTransPacket
* @param outPkt SMBSrvPacket
* @exception IOException
* @exception SMBSrvException
*/
public static final void processDCERPCRequest(SMBSrvSession sess, SMBSrvTransPacket srvTrans, SMBSrvPacket outPkt)
throws IOException, SMBSrvException
{
// Get the tree id from the received packet and validate that it is a valid
// connection id.
TreeConnection conn = sess.findTreeConnection( srvTrans);
if (conn == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
return;
}
// Get the file id and validate
int fid = srvTrans.getSetupParameter(1);
int maxData = srvTrans.getParameter(3) - DCEBuffer.OPERATIONDATA;
// Get the IPC pipe file for the specified file id
DCEPipeFile pipeFile = (DCEPipeFile) conn.findFile(fid);
if (pipeFile == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
return;
}
// Create a DCE/RPC buffer from the received data
DCEBuffer dceBuf = new DCEBuffer(srvTrans.getBuffer(), srvTrans.getParameter(10)
+ RFCNetBIOSProtocol.HEADER_LEN);
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
logger.debug("TransactNmPipe pipeFile=" + pipeFile.getName() + ", fid=" + fid + ", dceCmd=0x"
+ Integer.toHexString(dceBuf.getHeaderValue(DCEBuffer.HDR_PDUTYPE)));
// Process the received DCE buffer
processDCEBuffer(sess, dceBuf, pipeFile);
// Check if there is a reply buffer to return to the caller
if (pipeFile.hasBufferedData() == false)
return;
DCEBuffer txBuf = pipeFile.getBufferedData();
// Initialize the reply
DCESrvPacket dcePkt = new DCESrvPacket(outPkt.getBuffer());
// Always only one fragment as the data either fits into the first reply fragment or the
// client will read the remaining data by issuing read requests on the pipe
int flags = DCESrvPacket.FLG_ONLYFRAG;
dcePkt.initializeDCEReply();
txBuf.setHeaderValue(DCEBuffer.HDR_FLAGS, flags);
// Build the reply data
byte[] buf = dcePkt.getBuffer();
int pos = DCEDataPacker.longwordAlign(dcePkt.getByteOffset());
// Set the DCE fragment size and send the reply DCE/RPC SMB
int dataLen = txBuf.getLength();
txBuf.setHeaderValue(DCEBuffer.HDR_FRAGLEN, dataLen);
// Copy the data from the DCE output buffer to the reply SMB packet
int len = txBuf.getLength();
int sts = SMBStatus.NTSuccess;
if (len > maxData)
{
// Write the maximum transmit fragment to the reply
len = maxData + DCEBuffer.OPERATIONDATA;
dataLen = maxData + DCEBuffer.OPERATIONDATA;
// Indicate a buffer overflow status
sts = SMBStatus.NTBufferOverflow;
}
else
{
// Clear the DCE/RPC pipe buffered data, the reply will fit into a single response
// packet
pipeFile.setBufferedData(null);
}
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
logger.debug("Reply DCEbuf flags=0x" + Integer.toHexString(flags) + ", len=" + len + ", status=0x"
+ Integer.toHexString(sts));
// Copy the reply data to the reply packet
try
{
pos += txBuf.copyData(buf, pos, len);
}
catch (DCEBufferException ex)
{
sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
return;
}
// Set the SMB transaction data length
int byteLen = pos - dcePkt.getByteOffset();
dcePkt.setParameter(1, dataLen);
dcePkt.setParameter(6, dataLen);
dcePkt.setByteCount(byteLen);
dcePkt.setFlags2(SMBPacket.FLG2_LONGERRORCODE);
dcePkt.setLongErrorCode(sts);
sess.sendResponseSMB(dcePkt);
}
/**
* Process a DCE/RPC request
*
* @param sess SMBSrvSession
* @param vc VirtualCircuit
* @param tbuf TransactBuffer
* @param outPkt SMBSrvPacket
* @exception IOException
* @exception SMBSrvException
*/
public static final void processDCERPCRequest(SMBSrvSession sess, VirtualCircuit vc, TransactBuffer tbuf, SMBSrvPacket outPkt)
throws IOException, SMBSrvException
{
// Check if the transaction buffer has setup and data buffers
if (tbuf.hasSetupBuffer() == false || tbuf.hasDataBuffer() == false)
{
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 = vc.findConnection( tbuf.getTreeId());
if (conn == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
return;
}
// Get the file id and validate
DataBuffer setupBuf = tbuf.getSetupBuffer();
setupBuf.skipBytes(2);
int fid = setupBuf.getShort();
int maxData = tbuf.getReturnDataLimit() - DCEBuffer.OPERATIONDATA;
// Get the IPC pipe file for the specified file id
DCEPipeFile pipeFile = (DCEPipeFile) conn.findFile(fid);
if (pipeFile == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
return;
}
// Create a DCE/RPC buffer from the received transaction data
DCEBuffer dceBuf = new DCEBuffer(tbuf);
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
logger.debug("TransactNmPipe pipeFile=" + pipeFile.getName() + ", fid=" + fid + ", dceCmd=0x"
+ Integer.toHexString(dceBuf.getHeaderValue(DCEBuffer.HDR_PDUTYPE)));
// Process the received DCE buffer
processDCEBuffer(sess, dceBuf, pipeFile);
// Check if there is a reply buffer to return to the caller
if (pipeFile.hasBufferedData() == false)
return;
DCEBuffer txBuf = pipeFile.getBufferedData();
// Initialize the reply
DCESrvPacket dcePkt = new DCESrvPacket(outPkt.getBuffer());
// Always only one fragment as the data either fits into the first reply fragment or the
// client will read the remaining data by issuing read requests on the pipe
int flags = DCESrvPacket.FLG_ONLYFRAG;
dcePkt.initializeDCEReply();
txBuf.setHeaderValue(DCEBuffer.HDR_FLAGS, flags);
// Build the reply data
byte[] buf = dcePkt.getBuffer();
int pos = DCEDataPacker.longwordAlign(dcePkt.getByteOffset());
// Set the DCE fragment size and send the reply DCE/RPC SMB
int dataLen = txBuf.getLength();
txBuf.setHeaderValue(DCEBuffer.HDR_FRAGLEN, dataLen);
// Copy the data from the DCE output buffer to the reply SMB packet
int len = txBuf.getLength();
int sts = SMBStatus.NTSuccess;
if (len > maxData)
{
// Write the maximum transmit fragment to the reply
len = maxData + DCEBuffer.OPERATIONDATA;
dataLen = maxData + DCEBuffer.OPERATIONDATA;
// Indicate a buffer overflow status
sts = SMBStatus.NTBufferOverflow;
}
else
{
// Clear the DCE/RPC pipe buffered data, the reply will fit into a single response
// packet
pipeFile.setBufferedData(null);
}
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
logger.debug("Reply DCEbuf flags=0x" + Integer.toHexString(flags) + ", len=" + len + ", status=0x"
+ Integer.toHexString(sts));
// Copy the reply data to the reply packet
try
{
pos += txBuf.copyData(buf, pos, len);
}
catch (DCEBufferException ex)
{
sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
return;
}
// Set the SMB transaction data length
int byteLen = pos - dcePkt.getByteOffset();
dcePkt.setParameter(1, dataLen);
dcePkt.setParameter(6, dataLen);
dcePkt.setByteCount(byteLen);
dcePkt.setFlags2(SMBPacket.FLG2_LONGERRORCODE);
dcePkt.setLongErrorCode(sts);
sess.sendResponseSMB(dcePkt);
}
/**
* Process a DCE/RPC write request to the named pipe file
*
* @param sess SMBSrvSession
* @param inPkt SMBSrvPacket
* @param outPkt SMBSrvPacket
* @exception IOException
* @exception SMBSrvException
*/
public static final void processDCERPCRequest(SMBSrvSession sess, SMBSrvPacket inPkt, SMBSrvPacket outPkt)
throws IOException, SMBSrvException
{
// Get the tree id from the received packet and validate that it is a valid
// connection id.
TreeConnection conn = sess.findTreeConnection( inPkt);
if (conn == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
return;
}
// Determine if this is a write or write andX request
int cmd = inPkt.getCommand();
// Get the file id and validate
int fid = -1;
if (cmd == PacketType.WriteFile)
fid = inPkt.getParameter(0);
else
fid = inPkt.getParameter(2);
// Get the IPC pipe file for the specified file id
DCEPipeFile pipeFile = (DCEPipeFile) conn.findFile(fid);
if (pipeFile == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
return;
}
// Create a DCE buffer for the received data
DCEBuffer dceBuf = null;
byte[] buf = inPkt.getBuffer();
int pos = 0;
int len = 0;
if (cmd == PacketType.WriteFile)
{
// Get the data offset
pos = inPkt.getByteOffset();
// Check that the received data is valid
if (buf[pos++] != DataType.DataBlock)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidData, SMBStatus.ErrDos);
return;
}
len = DataPacker.getIntelShort(buf, pos);
pos += 2;
}
else
{
// Get the data offset and length
len = inPkt.getParameter(10);
pos = inPkt.getParameter(11) + RFCNetBIOSProtocol.HEADER_LEN;
}
// Create a DCE buffer mapped to the received packet
dceBuf = new DCEBuffer(buf, pos);
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_IPC))
logger.debug("Write pipeFile=" + pipeFile.getName() + ", fid=" + fid + ", dceCmd=0x"
+ Integer.toHexString(dceBuf.getHeaderValue(DCEBuffer.HDR_PDUTYPE)));
// Process the DCE buffer
processDCEBuffer(sess, dceBuf, pipeFile);
// Check if there is a valid reply buffered
int bufLen = 0;
if (pipeFile.hasBufferedData())
bufLen = pipeFile.getBufferedData().getLength();
// Send the write/write andX reply
if (cmd == PacketType.WriteFile)
{
// Build the write file reply
outPkt.setParameterCount(1);
outPkt.setParameter(0, len);
outPkt.setByteCount(0);
}
else
{
// Build the write andX reply
outPkt.setParameterCount(6);
outPkt.setAndXCommand(0xFF);
outPkt.setParameter(1, 0);
outPkt.setParameter(2, len);
outPkt.setParameter(3, bufLen);
outPkt.setParameter(4, 0);
outPkt.setParameter(5, 0);
outPkt.setByteCount(0);
}
// Send the write reply
outPkt.setFlags2(SMBPacket.FLG2_LONGERRORCODE);
sess.sendResponseSMB(outPkt);
}
/**
* Process a DCE/RPC pipe read request
*
* @param sess SMBSrvSession
* @param inPkt SMBSrvPacket
* @param outPkt SMBSrvPacket
* @exception IOException
* @exception SMBSrvException
*/
public static final void processDCERPCRead(SMBSrvSession sess, SMBSrvPacket inPkt, SMBSrvPacket outPkt)
throws IOException, SMBSrvException
{
// Get the tree id from the received packet and validate that it is a valid
// connection id.
TreeConnection conn = sess.findTreeConnection( inPkt);
if (conn == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidDrive, SMBStatus.ErrDos);
return;
}
// Determine if this is a read or read andX request
int cmd = inPkt.getCommand();
// Get the file id and read length, and validate
int fid = -1;
int rdLen = -1;
if (cmd == PacketType.ReadFile)
{
fid = inPkt.getParameter(0);
rdLen = inPkt.getParameter(1);
}
else
{
fid = inPkt.getParameter(2);
rdLen = inPkt.getParameter(5);
}
// Get the IPC pipe file for the specified file id
DCEPipeFile pipeFile = (DCEPipeFile) conn.findFile(fid);
if (pipeFile == null)
{
sess.sendErrorResponseSMB(SMBStatus.DOSInvalidHandle, SMBStatus.ErrDos);
return;
}
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_IPC))
logger.debug("Read pipeFile=" + pipeFile.getName() + ", fid=" + fid + ", rdLen=" + rdLen);
// Check if there is a valid reply buffered
if (pipeFile.hasBufferedData())
{
// Get the buffered data
DCEBuffer bufData = pipeFile.getBufferedData();
int bufLen = bufData.getAvailableLength();
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_IPC))
logger.debug(" Buffered data available=" + bufLen);
// Check if there is less data than the read size
if (rdLen > bufLen)
rdLen = bufLen;
// Build the read response
if (cmd == PacketType.ReadFile)
{
// Build the read response
outPkt.setParameterCount(5);
outPkt.setParameter(0, rdLen);
for (int i = 1; i < 5; i++)
outPkt.setParameter(i, 0);
outPkt.setByteCount(rdLen + 3);
// Copy the data to the response
byte[] buf = outPkt.getBuffer();
int pos = outPkt.getByteOffset();
buf[pos++] = (byte) DataType.DataBlock;
DataPacker.putIntelShort(rdLen, buf, pos);
pos += 2;
try
{
bufData.copyData(buf, pos, rdLen);
}
catch (DCEBufferException ex)
{
logger.error("DCR/RPC read", ex);
}
}
else
{
// Build the read andX response
outPkt.setParameterCount(12);
outPkt.setAndXCommand(0xFF);
for (int i = 1; i < 12; i++)
outPkt.setParameter(i, 0);
// Copy the data to the response
byte[] buf = outPkt.getBuffer();
int pos = DCEDataPacker.longwordAlign(outPkt.getByteOffset());
outPkt.setParameter(5, rdLen);
outPkt.setParameter(6, pos - RFCNetBIOSProtocol.HEADER_LEN);
outPkt.setByteCount((pos + rdLen) - outPkt.getByteOffset());
try
{
bufData.copyData(buf, pos, rdLen);
}
catch (DCEBufferException ex)
{
logger.error("DCE/RPC error", ex);
}
}
}
else
{
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_IPC))
logger.debug(" No buffered data available");
// Return a zero length read response
if (cmd == PacketType.ReadFile)
{
// Initialize the read response
outPkt.setParameterCount(5);
for (int i = 0; i < 5; i++)
outPkt.setParameter(i, 0);
outPkt.setByteCount(0);
}
else
{
// Return a zero length read andX response
outPkt.setParameterCount(12);
outPkt.setAndXCommand(0xFF);
for (int i = 1; i < 12; i++)
outPkt.setParameter(i, 0);
outPkt.setByteCount(0);
}
}
// Clear the status code
outPkt.setLongErrorCode(SMBStatus.NTSuccess);
// Send the read reply
outPkt.setFlags2(SMBPacket.FLG2_LONGERRORCODE);
sess.sendResponseSMB(outPkt);
}
/**
* Process the DCE/RPC request buffer
*
* @param sess SMBSrvSession
* @param buf DCEBuffer
* @param pipeFile DCEPipeFile
* @exception IOException
* @exception SMBSrvException
*/
public static final void processDCEBuffer(SMBSrvSession sess, DCEBuffer dceBuf, DCEPipeFile pipeFile)
throws IOException, SMBSrvException
{
// Process the DCE/RPC request
switch (dceBuf.getHeaderValue(DCEBuffer.HDR_PDUTYPE))
{
// DCE Bind
case DCECommand.BIND:
procDCEBind(sess, dceBuf, pipeFile);
break;
// DCE Request
case DCECommand.REQUEST:
procDCERequest(sess, dceBuf, pipeFile);
break;
default:
sess.sendErrorResponseSMB(SMBStatus.SRVNoAccessRights, SMBStatus.ErrSrv);
break;
}
}
/**
* Process a DCE bind request
*
* @param sess SMBSrvSession
* @param dceBuf DCEBuffer
* @param pipeFile DCEPipeFile
* @exception IOException
* @exception SMBSrvException
*/
public static final void procDCEBind(SMBSrvSession sess, DCEBuffer dceBuf, DCEPipeFile pipeFile)
throws IOException, SMBSrvException
{
try
{
// DEBUG
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
logger.debug("DCE Bind");
// Get the call id and skip the DCE header
int callId = dceBuf.getHeaderValue(DCEBuffer.HDR_CALLID);
dceBuf.skipBytes(DCEBuffer.DCEDATA);
// Unpack the bind request
int maxTxSize = dceBuf.getShort();
int maxRxSize = dceBuf.getShort();
int groupId = dceBuf.getInt();
int ctxElems = dceBuf.getByte(DCEBuffer.ALIGN_INT);
int presCtxId = dceBuf.getByte(DCEBuffer.ALIGN_SHORT);
int trfSyntax = dceBuf.getByte(DCEBuffer.ALIGN_SHORT);
UUID uuid1 = dceBuf.getUUID(true);
UUID uuid2 = dceBuf.getUUID(true);
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
{
logger.debug("Bind: maxTx=" + maxTxSize + ", maxRx=" + maxRxSize + ", groupId=" + groupId
+ ", ctxElems=" + ctxElems + ", presCtxId=" + presCtxId + ", trfSyntax=" + trfSyntax);
logger.debug(" uuid1=" + uuid1.toString());
logger.debug(" uuid2=" + uuid2.toString());
}
// Update the IPC pipe file
pipeFile.setMaxTransmitFragmentSize(maxTxSize);
pipeFile.setMaxReceiveFragmentSize(maxRxSize);
// Create an output DCE buffer for the reply and add the bind acknowledge header
DCEBuffer txBuf = new DCEBuffer();
txBuf.putBindAckHeader(dceBuf.getHeaderValue(DCEBuffer.HDR_CALLID));
txBuf.setHeaderValue(DCEBuffer.HDR_FLAGS, DCEBuffer.FLG_ONLYFRAG);
// Pack the bind acknowledge DCE reply
txBuf.putShort(maxTxSize);
txBuf.putShort(maxRxSize);
txBuf.putInt(0x53F0);
String srvPipeName = DCEPipeType.getServerPipeName(pipeFile.getPipeId());
txBuf.putShort(srvPipeName.length() + 1);
txBuf.putASCIIString(srvPipeName, true, DCEBuffer.ALIGN_INT);
txBuf.putInt(1);
txBuf.putShort(0);
txBuf.putShort(0);
txBuf.putUUID(uuid2, true);
txBuf.setHeaderValue(DCEBuffer.HDR_FRAGLEN, txBuf.getLength());
// Attach the reply buffer to the pipe file
pipeFile.setBufferedData(txBuf);
}
catch (DCEBufferException ex)
{
sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
return;
}
}
/**
* Process a DCE request
*
* @param sess SMBSrvSession
* @param dceBuf DCEBuffer
* @param pipeFile DCEPipeFile
* @exception IOException
* @exception SMBSrvException
*/
public static final void procDCERequest(SMBSrvSession sess, DCEBuffer inBuf, DCEPipeFile pipeFile)
throws IOException, SMBSrvException
{
// Debug
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
logger.debug("DCE Request opNum=0x" + Integer.toHexString(inBuf.getHeaderValue(DCEBuffer.HDR_OPCODE)));
// Pass the request to the DCE pipe request handler
if (pipeFile.hasRequestHandler())
pipeFile.getRequestHandler().processRequest(sess, inBuf, pipeFile);
else
sess.sendErrorResponseSMB(SMBStatus.SRVNoAccessRights, SMBStatus.ErrSrv);
}
}