All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jcifs.ntlmssp.Type2Message Maven / Gradle / Ivy

/* jcifs smb client library in Java
 * Copyright (C) 2002  "Michael B. Allen" 
 *                 "Eric Glass" 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package jcifs.ntlmssp;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jcifs.CIFSContext;


/**
 * Represents an NTLMSSP Type-2 message.
 */
public class Type2Message extends NtlmMessage {

    private static final Logger log = LoggerFactory.getLogger(Type2Message.class);

    private byte[] challenge;
    private String target;
    private byte[] context;
    private byte[] targetInformation;

    private static final Map TARGET_INFO_CACHE = new HashMap<>();


    private static byte[] getDefaultTargetInfo ( CIFSContext tc ) {
        String domain = tc.getConfig().getDefaultDomain();
        byte[] ti = TARGET_INFO_CACHE.get(domain);
        if ( ti != null ) {
            return ti;
        }

        ti = makeTargetInfo(tc, domain);
        TARGET_INFO_CACHE.put(domain, ti);
        return ti;
    }


    /**
     * @param domain
     * @param domainLength
     * @param server
     * @return
     */
    private static byte[] makeTargetInfo ( CIFSContext tc, String domainStr ) {
        byte[] domain = new byte[0];
        if ( domainStr != null ) {
            try {
                domain = domainStr.getBytes(UNI_ENCODING);
            }
            catch ( IOException ex ) {
                log.debug("Failed to get domain bytes", ex);
            }
        }
        int domainLength = domain.length;
        byte[] server = new byte[0];
        String host = tc.getNameServiceClient().getLocalHost().getHostName();
        if ( host != null ) {
            try {
                server = host.getBytes(UNI_ENCODING);
            }
            catch ( IOException ex ) {
                log.debug("Failed to get host bytes", ex);
            }
        }
        int serverLength = server.length;
        byte[] targetInfo = new byte[ ( domainLength > 0 ? domainLength + 4 : 0 ) + ( serverLength > 0 ? serverLength + 4 : 0 ) + 4];
        int offset = 0;
        if ( domainLength > 0 ) {
            writeUShort(targetInfo, offset, 2);
            offset += 2;
            writeUShort(targetInfo, offset, domainLength);
            offset += 2;
            System.arraycopy(domain, 0, targetInfo, offset, domainLength);
            offset += domainLength;
        }
        if ( serverLength > 0 ) {
            writeUShort(targetInfo, offset, 1);
            offset += 2;
            writeUShort(targetInfo, offset, serverLength);
            offset += 2;
            System.arraycopy(server, 0, targetInfo, offset, serverLength);
        }
        return targetInfo;
    }


    /**
     * Creates a Type-2 message using default values from the current
     * environment.
     * 
     * @param tc
     *            context to use
     */
    public Type2Message ( CIFSContext tc ) {
        this(tc, getDefaultFlags(tc), null, null);
    }


    /**
     * Creates a Type-2 message in response to the given Type-1 message
     * using default values from the current environment.
     * 
     * @param tc
     *            context to use
     * @param type1
     *            The Type-1 message which this represents a response to.
     */
    public Type2Message ( CIFSContext tc, Type1Message type1 ) {
        this(tc, type1, null, null);
    }


    /**
     * Creates a Type-2 message in response to the given Type-1 message.
     * 
     * @param tc
     *            context to use
     * @param type1
     *            The Type-1 message which this represents a response to.
     * @param challenge
     *            The challenge from the domain controller/server.
     * @param target
     *            The authentication target.
     */
    public Type2Message ( CIFSContext tc, Type1Message type1, byte[] challenge, String target ) {
        this(
            tc,
            getDefaultFlags(tc, type1),
            challenge,
            ( type1 != null && target == null && type1.getFlag(NTLMSSP_REQUEST_TARGET) ) ? tc.getConfig().getDefaultDomain() : target);
    }


    /**
     * Creates a Type-2 message with the specified parameters.
     * 
     * @param tc
     *            context to use
     * @param flags
     *            The flags to apply to this message.
     * @param challenge
     *            The challenge from the domain controller/server.
     * @param target
     *            The authentication target.
     */
    public Type2Message ( CIFSContext tc, int flags, byte[] challenge, String target ) {
        setFlags(flags);
        setChallenge(challenge);
        setTarget(target);
        if ( target != null ) {
            setTargetInformation(getDefaultTargetInfo(tc));
        }
    }


    /**
     * Creates a Type-2 message using the given raw Type-2 material.
     *
     * @param material
     *            The raw Type-2 material used to construct this message.
     * @throws IOException
     *             If an error occurs while parsing the material.
     */
    public Type2Message ( byte[] material ) throws IOException {
        parse(material);
    }


    /**
     * Returns the default flags for a generic Type-2 message in the
     * current environment.
     * 
     * @param tc
     *            context to use
     * @return An int containing the default flags.
     */
    public static int getDefaultFlags ( CIFSContext tc ) {
        return NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_VERSION
                | ( tc.getConfig().isUseUnicode() ? NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM );
    }


    /**
     * Returns the default flags for a Type-2 message created in response
     * to the given Type-1 message in the current environment.
     * 
     * @param tc
     *            context to use
     * @param type1
     *            request message
     *
     * @return An int containing the default flags.
     */
    public static int getDefaultFlags ( CIFSContext tc, Type1Message type1 ) {
        if ( type1 == null )
            return getDefaultFlags(tc);
        int flags = NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_VERSION;
        int type1Flags = type1.getFlags();
        flags |= ( ( type1Flags & NTLMSSP_NEGOTIATE_UNICODE ) != 0 ) ? NTLMSSP_NEGOTIATE_UNICODE : NTLMSSP_NEGOTIATE_OEM;
        if ( ( type1Flags & NTLMSSP_REQUEST_TARGET ) != 0 ) {
            String domain = tc.getConfig().getDefaultDomain();
            if ( domain != null ) {
                flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_DOMAIN;
            }
        }
        return flags;
    }


    /**
     * Returns the challenge for this message.
     *
     * @return A byte[] containing the challenge.
     */
    public byte[] getChallenge () {
        return this.challenge;
    }


    /**
     * Sets the challenge for this message.
     *
     * @param challenge
     *            The challenge from the domain controller/server.
     */
    public void setChallenge ( byte[] challenge ) {
        this.challenge = challenge;
    }


    /**
     * Returns the authentication target.
     *
     * @return A String containing the authentication target.
     */
    public String getTarget () {
        return this.target;
    }


    /**
     * Sets the authentication target.
     *
     * @param target
     *            The authentication target.
     */
    public void setTarget ( String target ) {
        this.target = target;
    }


    /**
     * Returns the target information block.
     *
     * @return A byte[] containing the target information block.
     *         The target information block is used by the client to create an
     *         NTLMv2 response.
     */
    public byte[] getTargetInformation () {
        return this.targetInformation;
    }


    /**
     * Sets the target information block.
     * The target information block is used by the client to create
     * an NTLMv2 response.
     * 
     * @param targetInformation
     *            The target information block.
     */
    public void setTargetInformation ( byte[] targetInformation ) {
        this.targetInformation = targetInformation;
    }


    /**
     * Returns the local security context.
     *
     * @return A byte[] containing the local security
     *         context. This is used by the client to negotiate local
     *         authentication.
     */
    public byte[] getContext () {
        return this.context;
    }


    /**
     * Sets the local security context. This is used by the client
     * to negotiate local authentication.
     *
     * @param context
     *            The local security context.
     */
    public void setContext ( byte[] context ) {
        this.context = context;
    }


    @Override
    public byte[] toByteArray () throws IOException {
        int size = 48;
        int flags = getFlags();
        String targetName = getTarget();
        byte[] targetInformationBytes = getTargetInformation();
        byte[] targetBytes = new byte[0];

        if ( getFlag(NTLMSSP_REQUEST_TARGET) ) {
            if ( targetName != null && targetName.length() != 0 ) {
                targetBytes = ( flags & NTLMSSP_NEGOTIATE_UNICODE ) != 0 ? targetName.getBytes(UNI_ENCODING)
                        : targetName.toUpperCase().getBytes(getOEMEncoding());
                size += targetBytes.length;
            }
            else {
                flags &= ( 0xffffffff ^ NTLMSSP_REQUEST_TARGET );
            }
        }

        if ( targetInformationBytes != null ) {
            size += targetInformationBytes.length;
            flags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
        }

        if ( getFlag(NTLMSSP_NEGOTIATE_VERSION) ) {
            size += 8;
        }

        byte[] type2 = new byte[size];
        int pos = 0;

        System.arraycopy(NTLMSSP_SIGNATURE, 0, type2, pos, NTLMSSP_SIGNATURE.length);
        pos += NTLMSSP_SIGNATURE.length;

        writeULong(type2, pos, NTLMSSP_TYPE2);
        pos += 4;

        // TargetNameFields
        int targetNameOff = writeSecurityBuffer(type2, pos, targetBytes);
        pos += 8;

        writeULong(type2, pos, flags);
        pos += 4;

        // ServerChallenge
        byte[] challengeBytes = getChallenge();
        System.arraycopy(challengeBytes != null ? challengeBytes : new byte[8], 0, type2, pos, 8);
        pos += 8;

        // Reserved
        byte[] contextBytes = getContext();
        System.arraycopy(contextBytes != null ? contextBytes : new byte[8], 0, type2, pos, 8);
        pos += 8;

        // TargetInfoFields
        int targetInfoOff = writeSecurityBuffer(type2, pos, targetInformationBytes);
        pos += 8;

        if ( getFlag(NTLMSSP_NEGOTIATE_VERSION) ) {
            System.arraycopy(NTLMSSP_VERSION, 0, type2, pos, NTLMSSP_VERSION.length);
            pos += NTLMSSP_VERSION.length;
        }

        pos += writeSecurityBufferContent(type2, pos, targetNameOff, targetBytes);
        pos += writeSecurityBufferContent(type2, pos, targetInfoOff, targetInformationBytes);

        return type2;
    }


    @Override
    public String toString () {
        String targetString = getTarget();
        byte[] challengeBytes = getChallenge();
        byte[] contextBytes = getContext();
        byte[] targetInformationBytes = getTargetInformation();

        return "Type2Message[target=" + targetString + ",challenge=" + ( challengeBytes == null ? "null" : "<" + challengeBytes.length + " bytes>" )
                + ",context=" + ( contextBytes == null ? "null" : "<" + contextBytes.length + " bytes>" ) + ",targetInformation="
                + ( targetInformationBytes == null ? "null" : "<" + targetInformationBytes.length + " bytes>" ) + ",flags=0x"
                + jcifs.util.Hexdump.toHexString(getFlags(), 8) + "]";
    }


    private void parse ( byte[] input ) throws IOException {
        int pos = 0;
        for ( int i = 0; i < 8; i++ ) {
            if ( input[ i ] != NTLMSSP_SIGNATURE[ i ] ) {
                throw new IOException("Not an NTLMSSP message.");
            }
        }
        pos += 8;

        if ( readULong(input, pos) != NTLMSSP_TYPE2 ) {
            throw new IOException("Not a Type 2 message.");
        }
        pos += 4;

        int flags = readULong(input, pos + 8);
        setFlags(flags);

        byte[] targetName = readSecurityBuffer(input, pos);
        int targetNameOff = readULong(input, pos + 4);
        if ( targetName.length != 0 ) {
            setTarget(new String(targetName, ( ( flags & NTLMSSP_NEGOTIATE_UNICODE ) != 0 ) ? UNI_ENCODING : getOEMEncoding()));
        }
        pos += 12; // 8 for target, 4 for flags

        if ( !allZeros8(input, pos) ) {
            byte[] challengeBytes = new byte[8];
            System.arraycopy(input, pos, challengeBytes, 0, challengeBytes.length);
            setChallenge(challengeBytes);
        }
        pos += 8;

        if ( targetNameOff < pos + 8 || input.length < pos + 8 ) {
            // no room for Context/Reserved
            return;
        }

        if ( !allZeros8(input, pos) ) {
            byte[] contextBytes = new byte[8];
            System.arraycopy(input, pos, contextBytes, 0, contextBytes.length);
            setContext(contextBytes);
        }
        pos += 8;

        if ( targetNameOff < pos + 8 || input.length < pos + 8 ) {
            // no room for target info
            return;
        }

        byte[] targetInfo = readSecurityBuffer(input, pos);
        if ( targetInfo.length != 0 ) {
            setTargetInformation(targetInfo);
        }
    }


    private static boolean allZeros8 ( byte[] input, int pos ) {
        for ( int i = pos; i < pos + 8; i++ ) {
            if ( input[ i ] != 0 ) {
                return false;
            }
        }
        return true;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy