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

jcifs.internal.smb1.SMB1SigningDigest Maven / Gradle / Ivy

Go to download

JCIFS is an Open Source client library that implements the CIFS/SMB networking protocol in 100% Java

There is a newer version: 2.1.37
Show newest version
/*
 * 
 * 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.internal.smb1;


import java.security.MessageDigest;
import java.util.Date;

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

import jcifs.Configuration;
import jcifs.SmbConstants;
import jcifs.internal.CommonServerMessageBlock;
import jcifs.internal.SMBSigningDigest;
import jcifs.internal.smb1.com.SmbComReadAndXResponse;
import jcifs.internal.smb1.trans.nt.SmbComNtCancel;
import jcifs.internal.util.SMBUtil;
import jcifs.smb.NtlmPasswordAuthenticator;
import jcifs.smb.SmbException;
import jcifs.smb.SmbTransportInternal;
import jcifs.util.Crypto;
import jcifs.util.Hexdump;


/**
 * 
 * @internal
 */
public class SMB1SigningDigest implements SMBSigningDigest {

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

    private MessageDigest digest;
    private byte[] macSigningKey;
    private boolean bypass = false;
    private int updates;
    private int signSequence;


    /**
     * 
     * @param macSigningKey
     * @param bypass
     */
    public SMB1SigningDigest ( byte[] macSigningKey, boolean bypass ) {
        this(macSigningKey, bypass, 0);
    }


    /**
     * 
     * @param macSigningKey
     * @param bypass
     * @param initialSequence
     */
    public SMB1SigningDigest ( byte[] macSigningKey, boolean bypass, int initialSequence ) {
        this.digest = Crypto.getMD5();
        this.macSigningKey = macSigningKey;
        this.signSequence = initialSequence;
        this.bypass = bypass;

        if ( log.isTraceEnabled() ) {
            log.trace("macSigningKey:");
            log.trace(Hexdump.toHexString(macSigningKey, 0, macSigningKey.length));
        }
    }


    /**
     * This constructor used to instance a SigningDigest object for
     * signing/verifying SMB using kerberos session key.
     * The MAC Key = concat(Session Key, Digest of Challenge);
     * Because of Kerberos Authentication don't have challenge,
     * The MAC Key = Session Key
     * 
     * @param macSigningKey
     *            The MAC key used to sign or verify SMB.
     */
    public SMB1SigningDigest ( byte[] macSigningKey ) {
        this.digest = Crypto.getMD5();
        this.macSigningKey = macSigningKey;
    }


    /**
     * Construct a digest with a non-zero starting sequence number
     * 
     * @param macSigningKey
     * @param initialSequence
     */
    public SMB1SigningDigest ( byte[] macSigningKey, int initialSequence ) {
        this.digest = Crypto.getMD5();
        this.macSigningKey = macSigningKey;
        this.signSequence = initialSequence;
    }


    /**
     * 
     * @param transport
     * @param auth
     * @throws SmbException
     */
    public SMB1SigningDigest ( SmbTransportInternal transport, NtlmPasswordAuthenticator auth ) throws SmbException {
        this.digest = Crypto.getMD5();
        try {
            byte[] serverEncryptionKey = transport.getServerEncryptionKey();
            switch ( transport.getContext().getConfig().getLanManCompatibility() ) {
            case 0:
            case 1:
            case 2:
                this.macSigningKey = new byte[40];
                auth.getUserSessionKey(transport.getContext(), serverEncryptionKey, this.macSigningKey, 0);
                System.arraycopy(auth.getUnicodeHash(transport.getContext(), serverEncryptionKey), 0, this.macSigningKey, 16, 24);
                break;
            case 3:
            case 4:
            case 5:
                this.macSigningKey = new byte[16];
                auth.getUserSessionKey(transport.getContext(), serverEncryptionKey, this.macSigningKey, 0);
                break;
            default:
                this.macSigningKey = new byte[40];
                auth.getUserSessionKey(transport.getContext(), serverEncryptionKey, this.macSigningKey, 0);
                System.arraycopy(auth.getUnicodeHash(transport.getContext(), serverEncryptionKey), 0, this.macSigningKey, 16, 24);
                break;
            }
        }
        catch ( Exception ex ) {
            throw new SmbException("", ex);
        }
        if ( log.isTraceEnabled() ) {
            log.trace("LM_COMPATIBILITY=" + transport.getContext().getConfig().getLanManCompatibility());
            log.trace(Hexdump.toHexString(this.macSigningKey, 0, this.macSigningKey.length));
        }
    }


    /**
     * Update digest with data
     * 
     * @param input
     * @param offset
     * @param len
     */
    public void update ( byte[] input, int offset, int len ) {
        if ( log.isTraceEnabled() ) {
            log.trace("update: " + this.updates + " " + offset + ":" + len);
            log.trace(Hexdump.toHexString(input, offset, Math.min(len, 256)));
        }
        if ( len == 0 ) {
            return; /* CRITICAL */
        }
        this.digest.update(input, offset, len);
        this.updates++;
    }


    /**
     * @return calculated digest
     */
    public byte[] digest () {
        byte[] b;

        b = this.digest.digest();

        if ( log.isTraceEnabled() ) {
            log.trace("digest: ");
            log.trace(Hexdump.toHexString(b, 0, b.length));
        }
        this.updates = 0;

        return b;
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.internal.SMBSigningDigest#sign(byte[], int, int, jcifs.internal.CommonServerMessageBlock,
     *      jcifs.internal.CommonServerMessageBlock)
     */
    @Override
    public void sign ( byte[] data, int offset, int length, CommonServerMessageBlock request, CommonServerMessageBlock response ) {
        if ( log.isTraceEnabled() ) {
            log.trace("Signing with seq " + this.signSequence);
        }

        ( (ServerMessageBlock) request ).setSignSeq(this.signSequence);
        if ( response != null ) {
            ( (ServerMessageBlock) response ).setSignSeq(this.signSequence + 1);
        }

        try {
            update(this.macSigningKey, 0, this.macSigningKey.length);
            int index = offset + SmbConstants.SIGNATURE_OFFSET;
            for ( int i = 0; i < 8; i++ )
                data[ index + i ] = 0;
            SMBUtil.writeInt4(this.signSequence, data, index);
            update(data, offset, length);
            System.arraycopy(digest(), 0, data, index, 8);
            if ( this.bypass ) {
                this.bypass = false;
                System.arraycopy("BSRSPYL ".getBytes(), 0, data, index, 8);
            }
        }
        catch ( Exception ex ) {
            log.error("Signature failed", ex);
        }
        finally {
            if ( request instanceof SmbComNtCancel ) {
                this.signSequence++;
            }
            else {
                this.signSequence += 2;
            }
        }
    }


    /**
     * 
     * {@inheritDoc}
     *
     * @see jcifs.internal.SMBSigningDigest#verify(byte[], int, int, int, jcifs.internal.CommonServerMessageBlock)
     */
    @Override
    public boolean verify ( byte[] data, int offset, int l, int extraPad, CommonServerMessageBlock m ) {

        ServerMessageBlock msg = (ServerMessageBlock) m;

        if ( ( msg.getFlags2() & SmbConstants.FLAGS2_SECURITY_SIGNATURES ) == 0 ) {
            // signature requirements need to be checked somewhere else
            log.warn("Expected signed response, but is not signed");
            return false;
        }

        update(this.macSigningKey, 0, this.macSigningKey.length);
        int index = offset;
        update(data, index, SmbConstants.SIGNATURE_OFFSET);
        index += SmbConstants.SIGNATURE_OFFSET;
        byte[] sequence = new byte[8];
        SMBUtil.writeInt4(msg.getSignSeq(), sequence, 0);
        update(sequence, 0, sequence.length);
        index += 8;
        if ( msg.getCommand() == ServerMessageBlock.SMB_COM_READ_ANDX ) {
            /*
             * SmbComReadAndXResponse reads directly from the stream into separate byte[] b.
             */
            SmbComReadAndXResponse raxr = (SmbComReadAndXResponse) msg;
            int length = msg.getLength() - raxr.getDataLength();
            update(data, index, length - SmbConstants.SIGNATURE_OFFSET - 8);
            update(raxr.getData(), raxr.getOffset(), raxr.getDataLength());
        }
        else {
            update(data, index, msg.getLength() - SmbConstants.SIGNATURE_OFFSET - 8);
        }
        byte[] signature = digest();
        for ( int i = 0; i < 8; i++ ) {
            if ( signature[ i ] != data[ offset + SmbConstants.SIGNATURE_OFFSET + i ] ) {
                if ( log.isDebugEnabled() ) {
                    log.debug("signature verification failure"); //$NON-NLS-1$
                    log.debug("Expect: " + Hexdump.toHexString(signature, 0, 8));
                    log.debug("Have: " + Hexdump.toHexString(data, offset + SmbConstants.SIGNATURE_OFFSET, 8));
                }
                return true;
            }
        }

        return false;
    }


    @Override
    public String toString () {
        return "MacSigningKey=" + Hexdump.toHexString(this.macSigningKey, 0, this.macSigningKey.length);
    }


    /**
     * 
     * @param cfg
     * @param t
     * @param dst
     * @param dstIndex
     */
    public static void writeUTime ( Configuration cfg, long t, byte[] dst, int dstIndex ) {
        if ( t == 0L || t == 0xFFFFFFFFFFFFFFFFL ) {
            SMBUtil.writeInt4(0xFFFFFFFF, dst, dstIndex);
            return;
        }

        if ( cfg.getLocalTimezone().inDaylightTime(new Date()) ) {
            // in DST
            if ( cfg.getLocalTimezone().inDaylightTime(new Date(t)) ) {
                // t also in DST so no correction
            }
            else {
                // t not in DST so subtract 1 hour
                t -= 3600000;
            }
        }
        else {
            // not in DST
            if ( cfg.getLocalTimezone().inDaylightTime(new Date(t)) ) {
                // t is in DST so add 1 hour
                t += 3600000;
            }
            else {
                // t isn't in DST either
            }
        }
        SMBUtil.writeInt4((int) ( t / 1000L ), dst, dstIndex);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy