/*
* Copyright (C) 2006-2008 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.jlan.server.auth.ntlm;
import java.io.UnsupportedEncodingException;
import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.util.DataPacker;
/**
* NTLM Message Types Base Class
*
* @author gkspencer
*/
public abstract class NTLMMessage {
// Default buffer size to allocate
private static final int DefaultBlobSize = 256;
// Field offsets
public static final int OffsetSignature = 0;
public static final int OffsetType = 8;
// Buffer header length
public static final int BufferHeaderLen = 8;
// Buffer, offset and lenght of the NTLM blob
private byte[] m_buf;
private int m_offset;
private int m_len;
/**
* Default constructor
*/
protected NTLMMessage() {
// Allocate a buffer
m_buf = new byte[DefaultBlobSize];
m_len = DefaultBlobSize;
}
/**
* Class constructor
*
* @param buf byte[]
* @param offset int
* @param len int
*/
protected NTLMMessage(byte[] buf, int offset, int len) {
m_buf = buf;
m_offset = offset;
m_len = len;
}
/**
* Return the message type
*
* @return int
*/
public final int isMessageType() {
return DataPacker.getIntelInt(m_buf, m_offset + OffsetType);
}
/**
* Return the message flags
*
* @return int
*/
public abstract int getFlags();
/**
* Return the state of the specified flag
*
* @param flag int
* @return boolean
*/
public final boolean hasFlag(int flag) {
return (getFlags() & flag) != 0 ? true : false;
}
/**
* Return the message length
*
* @return int
*/
public int getLength() {
return m_len;
}
/**
* Set the message type
*
* @param typ int
*/
public final void setMessageType(int typ) {
DataPacker.putIntelInt(typ, m_buf, m_offset + OffsetType);
}
/**
* Copy the NTLM blob data from the specified buffer
*
* @param buf byte[]
* @param offset int
* @param len int
*/
public final void copyFrom(byte[] buf, int offset, int len) {
// Allocate a new buffer, if required
if (m_buf == null || m_offset != 0 || m_buf.length < len)
m_buf = new byte[len];
// Copy the security blob data
System.arraycopy(buf, offset, m_buf, 0, len);
}
/**
* Return the NTLM message as a byte array
*
* @return byte[]
*/
public final byte[] getBytes() {
byte[] byts = new byte[getLength()];
System.arraycopy(m_buf, m_offset, byts, 0, getLength());
return byts;
}
/**
* Set the message flags
*
* @param flags int
*/
protected abstract void setFlags(int flags);
/**
* Initialize the blob
*
* @param typ int
* @param flags int
*/
protected void initializeHeader(int typ, int flags) {
// Set the signature
System.arraycopy(NTLM.Signature, 0, m_buf, m_offset, NTLM.Signature.length);
setMessageType(typ);
setFlags(flags);
}
/**
* Return a short/16bit value
*
* @param offset int
* @return int
*/
protected final int getShortValue(int offset) {
return DataPacker.getIntelShort(m_buf, m_offset + offset);
}
/**
* Return an int/32bit value
*
* @param offset int
* @return int
*/
protected final int getIntValue(int offset) {
return DataPacker.getIntelInt(m_buf, m_offset + offset);
}
/**
* Return the offset for a byte value
*
* @param offset int
* @return int
*/
protected final int getByteOffset(int offset) {
return getIntValue(offset + 4);
}
/**
* Return a byte value that has a header
*
* @param offset int
* @return byte[]
*/
protected final byte[] getByteValue(int offset) {
// Get the byte block length
int bLen = getShortValue(offset);
if (bLen == 0)
return null;
int bOff = getIntValue(offset + 4);
return getRawBytes(bOff, bLen);
}
/**
* Return a block of byte data
*
* @param offset int
* @param len int
* @return byte[]
*/
protected final byte[] getRawBytes(int offset, int len) {
byte[] byts = new byte[len];
System.arraycopy(m_buf, m_offset + offset, byts, 0, len);
return byts;
}
/**
* Return the length of a string
*
* @param offset int
* @return int
*/
protected final int getStringLength(int offset) {
int bufpos = m_offset + offset;
if (bufpos + 2 > m_len)
return -1;
return DataPacker.getIntelShort(m_buf, bufpos);
}
/**
* Return the allocated length of a string
*
* @param offset int
* @return int
*/
protected final int getStringAllocatedLength(int offset) {
int bufpos = m_offset + offset;
if (bufpos + 8 > m_len)
return -1;
return DataPacker.getIntelShort(m_buf, bufpos + 2);
}
/**
* Return the string data offset
*
* @param offset int
* @return int
*/
protected final int getStringOffset(int offset) {
int bufpos = m_offset + offset;
if (bufpos + 8 > m_len)
return -1;
return DataPacker.getIntelInt(m_buf, bufpos + 4);
}
/**
* Return a string value
*
* @param offset int
* @param isuni boolean
* @return String
*/
protected final String getStringValue(int offset, boolean isuni) {
int bufpos = m_offset + offset;
if (offset + 8 > m_len)
return null;
// Get the offset to the string
int len = DataPacker.getIntelShort(m_buf, bufpos);
int pos = DataPacker.getIntelInt(m_buf, bufpos + 4);
// Get the string value
if (pos + len > m_len)
return null;
// Unpack the string
String str = null;
try {
str = new String(m_buf, m_offset + pos, len, isuni ? "UnicodeLittle" : "US-ASCII");
}
catch (UnsupportedEncodingException ex) {
Debug.println( ex);
}
return str;
}
/**
* Return a raw string
*
* @param offset int
* @param len int
* @param isuni boolean
* @return String
*/
protected final String getRawString(int offset, int len, boolean isuni) {
return DataPacker.getString(m_buf, m_offset + offset, len, isuni);
}
/**
* Set a short/16bit value
*
* @param offset int
* @param sval int
*/
protected final void setShortValue(int offset, int sval) {
DataPacker.putIntelShort(sval, m_buf, m_offset + offset);
}
/**
* Set an int/32bit value
*
* @param offset int
* @param val int
*/
protected final void setIntValue(int offset, int val) {
DataPacker.putIntelInt(val, m_buf, m_offset + offset);
}
/**
* Set a raw byte value
*
* @param offset int
* @param byts byte[]
*/
protected final void setRawBytes(int offset, byte[] byts) {
System.arraycopy(byts, 0, m_buf, m_offset + offset, byts.length);
}
/**
* Set raw int values
*
* @param offset int
* @param ints int[]
*/
protected final void setRawInts(int offset, int[] ints) {
int bufpos = m_offset + offset;
for (int i = 0; i < ints.length; i++) {
DataPacker.putIntelInt(ints[i], m_buf, bufpos);
bufpos += 4;
}
}
/**
* Pack a raw string
*
* @param offset int
* @param str String
* @param isuni boolean
* @return int
*/
protected final int setRawString(int offset, String str, boolean isuni) {
return DataPacker.putString(str, m_buf, m_offset + offset, false, isuni);
}
/**
* Zero out an area of bytes
*
* @param offset int
* @param len int
*/
protected final void zeroBytes(int offset, int len) {
int bufpos = m_offset + offset;
for (int i = 0; i < len; i++)
m_buf[bufpos++] = (byte) 0;
}
/**
* Set a byte array value
*
* @param offset int
* @param byts byte[]
* @param dataOffset int
* @return int
*/
protected final int setByteValue(int offset, byte[] byts, int dataOffset) {
int bytsLen = byts != null ? byts.length : 0;
if (m_offset + offset + 12 > m_buf.length || m_offset + dataOffset + bytsLen > m_buf.length)
throw new ArrayIndexOutOfBoundsException();
// Pack the byte pointer block
DataPacker.putIntelShort(bytsLen, m_buf, m_offset + offset);
DataPacker.putIntelShort(bytsLen, m_buf, m_offset + offset + 2);
DataPacker.putIntelInt(dataOffset, m_buf, m_offset + offset + 4);
// Pack the bytes
if (bytsLen > 0)
System.arraycopy(byts, 0, m_buf, m_offset + dataOffset, bytsLen);
// Return the new data buffer offset
return dataOffset + DataPacker.wordAlign(bytsLen);
}
/**
* Set a string value
*
* @param offset int
* @param val String
* @param strOffset int
* @param isuni boolean
* @return int
*/
protected final int setStringValue(int offset, String val, int strOffset, boolean isuni) {
// Get the length in bytes
int len = val.length();
if (isuni)
len *= 2;
if (m_offset + offset + 8 > m_buf.length || m_offset + strOffset + len > m_buf.length)
throw new ArrayIndexOutOfBoundsException();
// Pack the string pointer block
DataPacker.putIntelShort(len, m_buf, m_offset + offset);
DataPacker.putIntelShort(len, m_buf, m_offset + offset + 2);
DataPacker.putIntelInt(strOffset, m_buf, m_offset + offset + 4);
// Pack the string
return DataPacker.putString(val, m_buf, m_offset + strOffset, false, isuni) - m_offset;
}
/**
* Set the message length
*
* @param len int
*/
protected final void setLength(int len) {
m_len = len;
}
/**
* Validate and determine the NTLM message type
*
* @param buf byte[]
* @return int
*/
public final static int isNTLMType(byte[] buf) {
return isNTLMType(buf, 0);
}
/**
* Validate and determine the NTLM message type
*
* @param buf byte[]
* @param offset int
* @return int
*/
public final static int isNTLMType(byte[] buf, int offset) {
// Validate the buffer
if (buf == null || buf.length < BufferHeaderLen)
return -1;
for (int i = 0; i < NTLM.Signature.length; i++) {
if (buf[offset + i] != NTLM.Signature[i])
return -1;
}
// Get the NTLM message type
return DataPacker.getIntelInt(buf, offset + OffsetType);
}
}