AnsweredAssumed Answered

Problem with CIFS & LDAP

Question asked by iandreeva on Aug 13, 2012
Latest reply on Jul 24, 2014 by jmt13
Hello all,
I am using LDAP for user authentication and I am trying to configure my own authenticator for the CIFS Server. I need the custom authenticator to remove the authentication for CIFS server (remove the popup which asks for credentials, directly access the repository) . It would be very helpful if someone tells me an easier way to do this. When I try to access my repository from my Windows Explorer with default authenticator (enterprise) everything is ok, the OS asks me for my credentials and if they are correct I have access. I read some things in the forum and in the wiki and what I gathered from all the reading is that I have to configure file-servers.properties, file-servers.xml and create my own class which extends CifsAuthenticator  class. So I’m trying the following:
file-servers.properties:
filesystem.name=Alfresco

cifs.enabled=true
cifs.localname=${localname}
cifs.domain=
cifs.broadcast=255.255.255.255
cifs.bindto=0.0.0.0
cifs.ipv6=disabled
cifs.hostannounce=true

ftp.enabled=true
ftp.ipv6=disabled

nfs.enabled=false
file-servers.xml:
<alfresco-config area="file-servers">

   <config evaluator="string-compare" condition="CIFS Server">
      <serverEnable enabled="${cifs.enabled}"/>
     
      <host name="${cifs.localname}A" domain="${cifs.domain}"/>
      <comment>Alfresco CIFS Server</comment>

      <!– Set to the broadcast mask for the subnet –>
      <broadcast>${cifs.broadcast}</broadcast>
     
      <!– Set to the IP for the adapter for Java socket –>
      <bindto>${cifs.bindto}</bindto>

      <!– Use Java socket based NetBIOS over TCP/IP and native SMB on linux –>
      <tcpipSMB ipv6="${cifs.ipv6}" platforms="linux,solaris,macosx"/>
      <netBIOSSMB bindto="${cifs.bindto}" platforms="linux,solaris,macosx"/>

      <!– Announce the server to the workgroup/domain –>
      <!– Use enabled="false" attribute to disable announcements –>             
      <hostAnnounce interval="5" enabled="${cifs.hostannounce}"/>

      <!– Use Win32 NetBIOS interface on Windows –>
      <Win32NetBIOS/>

      <!– Announce the server to the workgroup/domain –>
      <!– Use enabled="false" attribute to disable announcements –>             
      <Win32Announce interval="5" enabled="${cifs.hostannounce}"/>

      <!– CIFS authentication –>
      <!– Available types are 'alfresco', 'passthru' and 'enterprise' –>
      <authenticator type="enterprise">
         <class>org.alfresco.filesys.auth.cifs.CustomCifsAuthenticator</class>
      </authenticator>
   </config>

   <config evaluator="string-compare" condition="FTP Server">
      <serverEnable enabled="${ftp.enabled}"/>

     <!– IPv6 support –>
     <IPv6 state="${ftp.ipv6}"/>
    
      <!– FTP authentication –>
      <!– Available types are 'alfresco' and 'passthru' –>
      <authenticator type="alfresco"/>
           
   </config>
  
   <config evaluator="string-compare" condition="NFS Server">
      <serverEnable enabled="${nfs.enabled}"/>

      <!– Map NFS user/group ids to Alfresco users –>     
      <rpcAuthenticator>
         <userMappings>
            <user name="admin" uid="0" gid="0"/>
         </userMappings>
      </rpcAuthenticator>
   </config>
   
   <config evaluator="string-compare" condition="Filesystems">
      <filesystems>
         
         <!– Alfresco repository access shared filesystem –>
         <filesystem name="${filesystem.name}">
            <store>workspace://SpacesStore</store>
            <rootPath>/app:company_home</rootPath>

            <!– Add a URL file to each folder that links back to the web client –>
            <urlFile>
               <filename>__Alfresco.url</filename>
               <webpath>http://${localname}:8080/alfresco/</webpath>
            </urlFile>

            <!– Mark locked files as offline –>
            <offlineFiles/>

            <!– Desktop actions –>
            <desktopActions>
               <global>
                  <path>alfresco/desktop/Alfresco.exe</path>
                  <webpath>http://${localname}:8080/alfresco/</webpath>
               </global>
               <action>
                  <class>org.alfresco.filesys.repo.desk.CheckInOutDesktopAction</class>
                  <name>CheckInOut</name>
                  <filename>__CheckInOut.exe</filename>
               </action>
               <action>
                  <class>org.alfresco.filesys.repo.desk.JavaScriptDesktopAction</class>
                  <name>JavaScriptURL</name>
                  <filename>__ShowDetails.exe</filename>
                  <script>alfresco/desktop/showDetails.js</script>
                  <attributes>anyFiles</attributes>
                  <preprocess>copyToTarget</preprocess>
               </action>

               <!– Other desktop actions which may be enabled –>
               <!–
                <action>
                    <class>org.alfresco.filesys.repo.desk.EchoDesktopAction</class>
                    <name>Echo</name>
                    <filename>__AlfrescoEcho.exe</filename>
                </action>
                <action>
                    <class>org.alfresco.filesys.repo.desk.URLDesktopAction</class>
                    <name>URL</name>
                    <filename>__AlfrescoURL.exe</filename>
                </action>
                <action>
                    <class>org.alfresco.filesys.repo.desk.CmdLineDesktopAction</class>
                    <name>CmdLine</name>
                    <filename>__AlfrescoCmd.exe</filename>
                </action>
                <action>
                    <class>org.alfresco.filesys.repo.desk.JavaScriptDesktopAction</class>
                    <name>JavaScript</name>
                    <filename>__AlfrescoScript.exe</filename>
                    <script>alfresco/desktop/dumpRequest.js</script>
                    <attributes>anyFiles, multiplePaths , allowNoParams</attributes>
                    <preprocess>confirm, copyToTarget</preprocess>
                </action>
                –>                             
            </desktopActions>

            <!– Additional access control of the filesystem –>
            <!– Access type of 'none' will stop the filesystem from showing up for that user/address/protocol –>             
            <!–
            <accessControl default="Write">
               <user name="admin" access="Write"/>
               <address subnet="192.168.1.0" mask="255.255.255.0" access="Write"/>
            </accessControl>
            –>
         </filesystem>
          
           <!– AVM virtualization view of all stores/versions for WCM –>
           <!– virtual view stores filter can be any of the following: normal, site, staging, author, preview –>
         <avmfilesystem name="AVM">
            <virtualView stores="site,staging,author"/>
         </avmfilesystem>
          
      </filesystems>
   </config>

   <config evaluator="string-compare" condition="Filesystem Security">
      <!– Domain mappings used for passthru authentication routing              –>
      <!– Used when the client does not provide a domain name in the NTLM logon –>
      <!–
      <DomainMappings>
          <Domain name="ALFRESCO" subnet="192.168.1.0" mask="192.168.1.255"/>
      </DomainMappings>
      –>
     
      <!– Custom share mapper when multi-tenancy is enabled –>
      <!–
      <shareMapper type="multi-tenant">
        <debug/>
      </shareMapper>
      –>
     
      <!– Global access control list                                                                    –>
      <!– Applied to all filesystems that do not have an <accessControl> block                          –>
      <!– Access type of 'none' will stop the filesystem from showing up for that user/address/protocol –>             
      <!–
      <globalAccessControl default="None">
         <user name="admin" access="Write"/>
         <address ip="192.168.1.2" access="Write"/>
      </globalAccessControl>
      –>
   </config>

</alfresco-config>
And for CustomCifsAuthenticator I used the code from the class org.alfresco.filesys.auth.cifs .EnterpriseCifsAuthenticator, and put some System.out.println just to see if it would work (I’ll implement the logic later), here’s the source:
package org.alfresco.filesys.auth.cifs;

import org.alfresco.jlan.smb.server.SMBSrvException;
import org.alfresco.jlan.smb.server.SMBSrvPacket;
import org.alfresco.jlan.smb.server.SMBSrvSession;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.sasl.RealmCallback;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.server.auth.AuthenticatorException;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.auth.NTLanManAuthContext;
import org.alfresco.jlan.server.auth.kerberos.KerberosApReq;
import org.alfresco.jlan.server.auth.kerberos.KerberosDetails;
import org.alfresco.jlan.server.auth.kerberos.KrbAuthContext;
import org.alfresco.jlan.server.auth.kerberos.SessionSetupPrivilegedAction;
import org.alfresco.jlan.server.auth.ntlm.NTLM;
import org.alfresco.jlan.server.auth.ntlm.NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.NTLMv2Blob;
import org.alfresco.jlan.server.auth.ntlm.TargetInfo;
import org.alfresco.jlan.server.auth.ntlm.Type1NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.Type2NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.Type3NTLMMessage;
import org.alfresco.jlan.server.auth.spnego.NegTokenInit;
import org.alfresco.jlan.server.auth.spnego.NegTokenTarg;
import org.alfresco.jlan.server.auth.spnego.OID;
import org.alfresco.jlan.server.auth.spnego.SPNEGO;
import org.alfresco.jlan.server.config.InvalidConfigurationException;
import org.alfresco.jlan.server.config.ServerConfiguration;
import org.alfresco.jlan.server.core.NoPooledMemoryException;
import org.alfresco.jlan.smb.Capability;
import org.alfresco.jlan.smb.SMBStatus;
import org.alfresco.jlan.smb.dcerpc.UUID;
import org.alfresco.jlan.smb.server.VirtualCircuit;
import org.alfresco.jlan.util.DataPacker;
import org.alfresco.jlan.util.HexDump;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.NTLMMode;
import org.alfresco.repo.security.authentication.ntlm.NLTMAuthenticator;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.ietf.jgss.Oid;
import org.springframework.extensions.config.ConfigElement;

/**
* Enterprise CIFS Authenticator Class
*
* <p>CIFS authenticator that supports NTLMSSP and Kerberos logins.
*
* @author gkspencer
*/
public class CustomCifsAuthenticator extends CifsAuthenticatorBase implements CallbackHandler
{
    // Constants
    //
    // Default login configuration entry name

    private static final String LoginConfigEntry = "AlfrescoCIFS";

    // NTLM flags mask, used to mask out features that are not supported

    private static final int NTLM_FLAGS = NTLM.Flag56Bit +
                                          NTLM.Flag128Bit +
                                          NTLM.FlagLanManKey +
                                          NTLM.FlagNegotiateNTLM +
                                          NTLM.FlagNTLM2Key +
                                          NTLM.FlagNegotiateUnicode;

    // Use NTLMSSP or SPNEGO

    private boolean m_useRawNTLMSSP = true;

    // Flag to control whether NTLMv1 is accepted

    private boolean m_acceptNTLMv1 = true;

    // Kerberos settings
    //
    // Account name and password for server ticket
    //
    // The account name must be built from the CIFS server name, in the format :-
    //
    // cifs/<server_name>@<realm>

    private String m_accountName;
    private String m_password;

    // Kerberos realm

    private String m_krbRealm;

    // Login configuration entry name

    private String m_loginEntryName = LoginConfigEntry;

    // Server login context

    private LoginContext m_loginContext;

    // SPNEGO NegTokenInit blob, sent to the client in the SMB negotiate response

    private Vector<Oid> m_mechTypes;
    private byte[] m_negTokenInit;
    private String m_mecListMIC;

    private boolean kerberosDebug;

    private boolean disableNTLM;

    /**
     * Class constructor
     */
    public CustomCifsAuthenticator()
    {
        setExtendedSecurity(true);
    }

    /**
     * Sets the HTTP service account password. (the Principal should be configured in java.login.config)
     *
     * @param password
     *            the password to set
     */
    public void setPassword(String password)
    {
        this.m_password = password;
    }

    /**
     * Sets the HTTP service account realm.
     *
     * @param realm
     *            the realm to set
     */
    public void setRealm(String realm)
    {
        m_krbRealm = realm;
    }

    /**
     * Sets the HTTP service login configuration entry name.
     *
     * @param loginEntryName
     *            the loginEntryName to set
     */
    public void setJaasConfigEntryName(String jaasConfigEntryName)
    {
        m_loginEntryName = jaasConfigEntryName;
    }

    public void setKerberosDebug(boolean kerberosDebug)
    {
        this.kerberosDebug = kerberosDebug;
    }

    public void setDisableNTLM(boolean disableNTLM)
    {
        this.disableNTLM = disableNTLM;
    }

    public void setUseSPNEGO(boolean useSPNEGO)
    {
        m_useRawNTLMSSP = !useSPNEGO;
    }

    public void setDisallowNTLMv1(boolean disallowNTLMv1)
    {
        this.m_acceptNTLMv1 = !disallowNTLMv1;
    }

    /**
     * Initialize the authenticator (via the config service)
     *
     * @param config ServerConfiguration
     * @param params ConfigElement
     * @exception InvalidConfigurationException
     */
    public void initialize(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException
    {
        // Check if Java API Kerberos debug output should be enabled

        setKerberosDebug(params.getChild("kerberosDebug") != null);

        // Check if Kerberos is enabled, get the Kerberos realm
        ConfigElement krbRealm = params.getChild("Realm");
        if (krbRealm != null && krbRealm.getValue() != null && krbRealm.getValue().length() > 0)
        {
            setRealm(krbRealm.getValue());

            // Get the CIFS service account password

            ConfigElement srvPassword = params.getChild("Password");
            if (srvPassword != null && srvPassword.getValue() != null && srvPassword.getValue().length() > 0)
            {
                setPassword(srvPassword.getValue());
            }
            else
                throw new InvalidConfigurationException("CIFS service account password not specified");

            // Get the login configuration entry name

            ConfigElement loginEntry = params.getChild("LoginEntry");

            if (loginEntry != null)
            {
                if (loginEntry.getValue() != null && loginEntry.getValue().length() > 0)
                {
                    // Set the login configuration entry name to use
                    setJaasConfigEntryName(loginEntry.getValue());
                }
                else
                    throw new InvalidConfigurationException("Invalid login entry specified");
            }

            setDisableNTLM(params.getChild("disableNTLM") != null);

            // Indicate that SPNEGO security blobs are being used
            setUseSPNEGO(true);
        }
        else
        {
            // Check if raw NTLMSSP or SPNEGO/NTLMSSP should be used
            setUseSPNEGO(params.getChild("useSPNEGO") != null);
        }

        // Check if NTLMv1 logons are accepted

        setDisallowNTLMv1(params.getChild("disallowNTLMv1") != null);

        // Trigger super class initialisation
        super.initialize(config, params);
    }

   
    /**
     * Initialize the authenticator (after properties have been set)
     *
     * @exception InvalidConfigurationException
     */
    @Override
    public void initialize() throws InvalidConfigurationException
    {
        super.initialize();

        // Check if Java API Kerberos debug output should be enabled

        if (this.kerberosDebug)
        {
            // Enable Kerberos API debug output

            System.setProperty("sun.security.jgss.debug", "true");
            System.setProperty("sun.security.krb5.debug", "true");
        }

        // Check if Kerberos is enabled
        if (m_krbRealm != null && m_krbRealm.length() > 0)
        {

            // Get the CIFS service account password
            if (m_password == null || m_password.length() == 0)
            {
                throw new InvalidConfigurationException("CIFS service account password not specified");
            }

            // Get the login configuration entry name
            if (m_loginEntryName == null || m_loginEntryName.length() == 0)
            {
                throw new InvalidConfigurationException("Invalid login entry specified");
            }

            // Create a login context for the CIFS server service

            try
            {
                // Login the CIFS server service

                m_loginContext = new LoginContext(m_loginEntryName, this);
                m_loginContext.login();
            }
            catch (LoginException ex)
            {
                // Debug

                if (logger.isErrorEnabled())
                    logger.error("CIFS Kerberos authenticator error", ex);

                throw new InvalidConfigurationException("Failed to login CIFS server service");
            }

            // Get the CIFS service account name from the subject

            Subject subj = m_loginContext.getSubject();
            Principal princ = subj.getPrincipals().iterator().next();

            m_accountName = princ.getName();

            // DEBUG

            if (logger.isDebugEnabled())
                logger.debug("Logged on using principal " + m_accountName);

            // Create the Oid list for the SPNEGO NegTokenInit, include NTLMSSP for fallback

            m_mechTypes = new Vector<Oid>();

            // DEBUG

            if (logger.isDebugEnabled())
            {
                logger.debug("Enabling mechTypes :-");
                logger.debug("  Kerberos5");
                logger.debug("  MS-Kerberos5");
            }

            // Always enable Kerberos

            m_mechTypes.add(OID.KERBEROS5);
            m_mechTypes.add(OID.MSKERBEROS5);

            if (!disableNTLM)
            {
                m_mechTypes.add(OID.NTLMSSP);

                // DEBUG

                if (logger.isDebugEnabled())
                    logger.debug("  NTLMSSP");
            }

            // Indicate that SPNEGO security blobs are being used

            m_useRawNTLMSSP = false;
        }
        // Check if raw NTLMSSP or SPNEGO/NTLMSSP should be used
        else if (!m_useRawNTLMSSP)
        {
            // SPNEGO security blobs are being used

            // Create the Oid list for the SPNEGO NegTokenInit

            m_mechTypes = new Vector<Oid>();

            m_mechTypes.add(OID.NTLMSSP);

        }
        else
        {
            // Use raw NTLMSSP security blobs
        }

        // Make sure that either Kerberos support is enabled and/or the authentication component
        // supports MD4 hashed passwords

        if (!isKerberosEnabled() && (!(getAuthenticationComponent() instanceof NLTMAuthenticator) || getNTLMAuthenticator().getNTLMMode() != NTLMMode.MD4_PROVIDER))
        {
            // Log an error

            logger.error("No valid CIFS authentication combination available");
            logger.error("Either enable Kerberos support or use an SSO-enabled authentication component that supports MD4 hashed passwords");

            // Throw an exception to stop the CIFS server startup

            throw new AlfrescoRuntimeException("Invalid CIFS authenticator configuration");
        }
    }
   
    /**
     * As the mechListMIC principle may vary according to the CIFS server configuration, initialisation and retrieval of
     * the cached SPNEGO NegTokenInit has been moved to this method.
     *
     * @return encoded SPNEGO NegTokenInit
     * @throws AuthenticatorException
     */
    private synchronized byte[] getNegTokenInit() throws AuthenticatorException
    {
        String mecListMIC = null;

        // Check if Kerberos is enabled
        byte[] encoded = null;       
        if (m_krbRealm != null && m_krbRealm.length() > 0)
        {
            // Build the mechListMIC principle
            //
            // Note: This field is not as specified

            StringBuilder mic = new StringBuilder();

            mic.append("cifs/");
            mic.append(this.m_config.getServerName().toLowerCase());
            mic.append("@");
            mic.append(m_krbRealm);

            mecListMIC = mic.toString();
        
            // If the principal is the same, use the cached pre-encoded version
            if (mecListMIC.equals(m_mecListMIC))
            {
                encoded = m_negTokenInit;
            }
        }
        // Check if raw NTLMSSP or SPNEGO/NTLMSSP should be used
        else if (!m_useRawNTLMSSP)
        {
            encoded = m_negTokenInit;
        }
        else
        {
            return null;
        }

        if (encoded != null)
        {
            return encoded;
        }
       
        // Build the SPNEGO NegTokenInit blob

        try
        {
            // Build the SPNEGO NegTokenInit that contains the authentication types that the CIFS server accepts

            NegTokenInit negTokenInit = new NegTokenInit(m_mechTypes, mecListMIC);

            // Encode the NegTokenInit blob

            encoded = negTokenInit.encode();
            m_negTokenInit = encoded;
            m_mecListMIC = mecListMIC;
        }
        catch (IOException ex)
        {
            // Debug

            if (logger.isErrorEnabled())
                logger.error("Error creating SPNEGO NegTokenInit blob", ex);

            throw new AuthenticatorException("Failed to create SPNEGO NegTokenInit blob");
        }
       
        return encoded;
    }

    /**
     * Determine if Kerberos support is enabled
     *
     * @return boolean
     */
    private final boolean isKerberosEnabled()
    {
        return m_krbRealm != null && m_loginContext != null;
    }

    /**
     * Determine if raw NTLMSSP or SPNEGO security blobs are being used
     *
     * @return boolean
     */
    private final boolean useRawNTLMSSP()
    {
        return m_useRawNTLMSSP;
    }

    /**
     * Determine if NTLMv1 logons are accepted
     *
     * @return boolean
     */
    private final boolean acceptNTLMv1Logon()
    {
        return m_acceptNTLMv1;
    }

    /**
     * JAAS callback handler
     *
     * @param callbacks Callback[]
     * @exception IOException
     * @exception UnsupportedCallbackException
     */
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
    {
        // Process the callback list

        for (int i = 0; i < callbacks.length; i++)
        {
            // Request for user name

            if (callbacks[i] instanceof NameCallback)
            {
                NameCallback cb = (NameCallback) callbacks[i];
                // cb.setName(m_accountName);
                cb.setName("");
            }

            // Request for password
            else if (callbacks[i] instanceof PasswordCallback)
            {
                PasswordCallback cb = (PasswordCallback) callbacks[i];
                cb.setPassword(m_password.toCharArray());
            }

            // Request for realm

            else if (callbacks[i] instanceof RealmCallback)
            {
                RealmCallback cb = (RealmCallback) callbacks[i];
                cb.setText(m_krbRealm);
            }
            else
            {
                throw new UnsupportedCallbackException(callbacks[i]);
            }
        }
    }

    /**
     * Return the encryption key/challenge length
     *
     * @return int
     */
    public int getEncryptionKeyLength()
    {
        return 8;
    }

    /**
     * Return the server capability flags
     *
     * @return int
     */
    public int getServerCapabilities()
    {
        return Capability.Unicode + Capability.RemoteAPIs + Capability.NTSMBs + Capability.NTFind +
               Capability.NTStatus + Capability.LargeFiles + Capability.LargeRead + Capability.LargeWrite +
               Capability.ExtendedSecurity;
    }

    /**
     * Generate the CIFS negotiate response packet, the authenticator should add authentication specific fields
     * to the response.
     *
     * @param sess SMBSrvSession
     * @param respPkt SMBSrvPacket
     * @param extendedSecurity boolean
     * @exception AuthenticatorException
     */
    public void generateNegotiateResponse(SMBSrvSession sess, SMBSrvPacket respPkt, boolean extendedSecurity)
            throws AuthenticatorException
    {
       System.out
            .println("CustomCifsAuthenticator.generateNegotiateResponse()");
        // If the client does not support extended security then return a standard negotiate response
        // with an 8 byte challenge

        if (extendedSecurity == false)
        {
            super.generateNegotiateResponse(sess, respPkt, extendedSecurity);
            return;
        }

        // Make sure the extended security negotiation flag is set

        if ((respPkt.getFlags2() & SMBSrvPacket.FLG2_EXTENDEDSECURITY) == 0)
            respPkt.setFlags2(respPkt.getFlags2() + SMBSrvPacket.FLG2_EXTENDEDSECURITY);

        // Get the negotiate response byte area position

        int pos = respPkt.getByteOffset();
        byte[] buf = respPkt.getBuffer();

        // Pack the CIFS server GUID into the negotiate response

        UUID serverGUID = sess.getSMBServer().getServerGUID();

        System.arraycopy(serverGUID.getBytes(), 0, buf, pos, 16);
        pos += 16;

        // If SPNEGO is enabled then pack the NegTokenInit blob

        if (useRawNTLMSSP() == false)
        {
            byte[] negTokenInit = getNegTokenInit();
            System.arraycopy(negTokenInit, 0, buf, pos, m_negTokenInit.length);
            pos += negTokenInit.length;
        }

        // Set the negotiate response length

        respPkt.setByteCount(pos - respPkt.getByteOffset());
    }

    /**
     * Process the CIFS session setup request packet and build the session setup response
     *
     * @param sess SMBSrvSession
     * @param reqPkt SMBSrvPacket
     * @exception SMBSrvException
     */
    public void processSessionSetup(final SMBSrvSession sess, final SMBSrvPacket reqPkt)
        throws SMBSrvException
    {
       System.out.println("CustomCifsAuthenticator.processSessionSetup()");
        //  Check that the received packet looks like a valid NT session setup andX request

        if (reqPkt.checkPacketIsValid(12, 0) == false)
            throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);

        //  Check if the request is using security blobs or the older hashed password format
       
        if ( reqPkt.getParameterCount() == 13)
        {
            try
            {
                // Start a transaction

                doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
                {

                    public Object execute() throws Throwable
                    {
                        // Process the hashed password session setup

                        doHashedPasswordLogon(sess, reqPkt);
                        return null;
                    }
                });
            }
            catch ( Exception ex)
            {
                //  Convert to an access denied exception
               
                throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
            }
           
            // Hashed password processing complete
           
            return;
        }
       
        //  Extract the session details

        int maxBufSize = reqPkt.getParameter(2);
        int maxMpx     = reqPkt.getParameter(3);
        int vcNum      = reqPkt.getParameter(4);
        final int secBlobLen = reqPkt.getParameter(7);
        int capabs     = reqPkt.getParameterLong(10);

        //  Extract the client details from the session setup request

        int dataPos = reqPkt.getByteOffset();
        final byte[] buf = reqPkt.getBuffer();

        //  Determine if ASCII or unicode strings are being used
           
        final boolean isUni = reqPkt.isUnicode();

        //  Make a note of the security blob position
       
        final int secBlobPos = dataPos;
       
        //  Extract the clients primary domain name string

        dataPos += secBlobLen;
        reqPkt.setPosition( dataPos);
       
        String domain = "";

        if (reqPkt.hasMoreData()) {

            //    Extract the callers domain name

            domain = reqPkt.unpackString(isUni);
           
            if (domain == null)
                throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
        }

        //  Extract the clients native operating system

        String clientOS = "";

        if (reqPkt.hasMoreData()) {

          //    Extract the callers operating system name

            clientOS = reqPkt.unpackString(isUni);
           
          if (clientOS == null)
              throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
        }

        //  DEBUG

        if (logger.isDebugEnabled())
          logger.debug("NT Session setup " + (useRawNTLMSSP() ? "NTLMSSP" : "SPNEGO") + ", MID=" + reqPkt.getMultiplexId() + ", UID=" + reqPkt.getUserId() + ", PID=" + reqPkt.getProcessId());

        //  Store the client maximum buffer size, maximum multiplexed requests count and client capability flags
           
        sess.setClientMaximumBufferSize(maxBufSize != 0 ? maxBufSize : SMBSrvSession.DefaultBufferSize);
        sess.setClientMaximumMultiplex(maxMpx);
        sess.setClientCapabilities(capabs);

        //  Create the client information and store in the session

        final ClientInfo client = new AlfrescoClientInfo();
        client.setDomain(domain);
        client.setOperatingSystem(clientOS);
       
        client.setLogonType( ClientInfo.LogonNormal);

        // Set the remote address, if available
       
        if ( sess.hasRemoteAddress())
          client.setClientAddress(sess.getRemoteAddress().getHostAddress());

        //  Set the process id for this client, for multi-stage logons
       
        client.setProcessId( reqPkt.getProcessId());
       
        // Get the current sesion setup object, or null
       
        Object setupObj = sess.getSetupObject( client.getProcessId());
       
        //  Process the security blob
       
        byte[] respBlob = null;
        final boolean isNTLMSSP;
       
        try
        {
                     
            // Check if the blob has the NTLMSSP signature
           
            if ( secBlobLen >= NTLM.Signature.length) {
             
              // Check for the NTLMSSP signature
             
              int idx = 0;
              while ( idx < NTLM.Signature.length && buf[secBlobPos + idx] == NTLM.Signature[ idx])
                idx++;
             
              isNTLMSSP = ( idx == NTLM.Signature.length);
            }
            else {
              isNTLMSSP = false;               
            }

            // Start a transaction
           
            respBlob = doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<byte[]>()
            {

                public byte[] execute() throws Throwable
                {
                    // Process the security blob

                    if (isNTLMSSP)
                    {
                        // Process an NTLMSSP security blob

                        return doNtlmsspSessionSetup(sess, client, buf, secBlobPos, secBlobLen, isUni);
                    }
                    else
                    {
                        // Process an SPNEGO security blob

                        return doSpnegoSessionSetup(sess, client, buf, secBlobPos, secBlobLen, isUni);
                    }
                }
            });
           
        }
        catch ( Exception ex)
        {
            //  Cleanup any stored context
           
            sess.removeSetupObject( client.getProcessId());
           
            //  Convert to an access denied exception
           
            throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
        }

        // Debug
       
        if ( logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
            logger.debug("User " + client.getUserName() + " logged on " + (client != null ? " (type " + client.getLogonTypeString() + ")" : ""));

        //  Update the client information if not already set
           
        if ( sess.getClientInformation() == null ||
             sess.getClientInformation().getUserName().length() == 0) {
                     
            //  Set the client details for the session
                   
            sess.setClientInformation(client);
        }

        //  Get the response blob length, it can be null
       
        int respLen = respBlob != null ? respBlob.length : 0;
       
      // Use the original packet for the response

      SMBSrvPacket respPkt = reqPkt;
      
        //  Check if there is/was a session setup object stored in the session, this indicates a multi-stage session
        //  setup so set the status code accordingly
       
        boolean loggedOn = false;
       
        if ( respBlob != null || sess.hasSetupObject( client.getProcessId()) || setupObj != null)
        {
            //  NTLMSSP has two stages, if there is a stored setup object then indicate more processing
            //  required
           
            if ( sess.hasSetupObject( client.getProcessId()))
                respPkt.setLongErrorCode( SMBStatus.NTMoreProcessingRequired);
            else
            {
                respPkt.setLongErrorCode( SMBStatus.NTSuccess);
               
                // Indicate that the user is logged on
               
                loggedOn = true;
            }

         // Set the parameter count then check if the security blob will fit into the current
         // packet buffer

         respPkt.setParameterCount(4);
         int reqLen = respLen + 100; // allow for strings

         if ( reqLen > respPkt.getAvailableLength()) {

            try {

               // Allocate a new buffer for the response

               respPkt = sess.getPacketPool().allocatePacket(respPkt.getByteOffset() + reqLen, reqPkt);
            }
            catch (NoPooledMemoryException ex) {

               // DEBUG

               if ( Debug.EnableDbg && hasDebug())
                  Debug.println("Authenticator failed to allocate packet from pool, reqSiz="
                        + (respPkt.getByteOffset() + respLen));

               // Return a server error to the client

               throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNoBuffers, SMBStatus.ErrSrv);
            }
         }

         // Fill in the rest of the packet header

         respPkt.setParameter(0, 0xFF);    // No chained response
         respPkt.setParameter(1, 0);    // Offset to chained response

         respPkt.setParameter(2, 0);    // Action
         respPkt.setParameter(3, respLen);
        }
        else
        {
            //  Build a completed session setup response
           
            respPkt.setLongErrorCode( SMBStatus.NTSuccess);
           
            //  Build the session setup response SMB
   
            respPkt.setParameterCount(12);
            respPkt.setParameter(0, 0xFF);      //  No chained response
            respPkt.setParameter(1, 0);         //  Offset to chained response
   
            respPkt.setParameter(2, SMBSrvSession.DefaultBufferSize);
            respPkt.setParameter(3, SMBSrvSession.NTMaxMultiplexed);
            respPkt.setParameter(4, 0);         //  virtual circuit number
            respPkt.setParameterLong(5, 0);     //  session key
            respPkt.setParameter(7, respLen);
                                                //  security blob length
            respPkt.setParameterLong(8, 0);     //  reserved
            respPkt.setParameterLong(10, getServerCapabilities());
           
            // Indicate that the user is logged on
           
            loggedOn = true;
        }
       
        // If the user is logged on then allocate a virtual circuit

        int uid = 0;
       
        if ( loggedOn == true) {

          // Clear any stored session setup object for the logon
         
          sess.removeSetupObject( client.getProcessId());
         
          // Check if the user is an administrator
         
          checkForAdminUserName( client);
         
          // Get the users home folder node, if available
         
          getHomeFolderForUser( client);
         
          // Create a virtual circuit for the new logon
         
          VirtualCircuit vc = new VirtualCircuit( vcNum, client);
          uid = sess.addVirtualCircuit( vc);
         
          if ( uid == VirtualCircuit.InvalidUID)
          {
             // DEBUG
           
             if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
                logger.debug("Failed to allocate UID for virtual circuit, " + vc);
           
             // Failed to allocate a UID
           
             throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
          }
          else if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE)) {
           
             // DEBUG
           
             logger.debug("Allocated UID=" + uid + " for VC=" + vc);
          }
        }
       
        // Common session setup response
       
        respPkt.setCommand( reqPkt.getCommand());
        respPkt.setByteCount(0);

        respPkt.setTreeId( 0);
        respPkt.setUserId( uid);

        //  Set the various flags

        int flags = respPkt.getFlags();
        flags &= ~SMBSrvPacket.FLG_CASELESS;
        respPkt.setFlags(flags);
       
        int flags2 = SMBSrvPacket.FLG2_LONGFILENAMES + SMBSrvPacket.FLG2_EXTENDEDSECURITY + SMBSrvPacket.FLG2_LONGERRORCODE;
        if ( isUni)
          flags2 += SMBSrvPacket.FLG2_UNICODE;
        respPkt.setFlags2( flags2);
       
        //  Pack the security blob

        int pos = respPkt.getByteOffset();
        byte[] buf1 = respPkt.getBuffer();

        if ( respBlob != null)
        {
            System.arraycopy( respBlob, 0, buf1, pos, respBlob.length);
            pos += respBlob.length;
        }
       
        // Pack the OS, dialect and domain name strings
       
        if ( isUni)
            pos = DataPacker.wordAlign(pos);

        pos = DataPacker.putString("Java", buf1, pos, true, isUni);
        pos = DataPacker.putString("Alfresco CIFS Server " + sess.getServer().isVersion(), buf1, pos, true, isUni);
        pos = DataPacker.putString(getCIFSConfig().getDomainName(), buf1, pos, true, isUni);
       
        respPkt.setByteCount(pos - respPkt.getByteOffset());
    }
   
    /**
     * Process an NTLMSSP security blob
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @param secbuf byte[]
     * @param secpos int
     * @param seclen int
     * @param unicode boolean
     * @exception SMBSrvException
     */
    private final byte[] doNtlmsspSessionSetup( SMBSrvSession sess, ClientInfo client,
            byte[] secbuf, int secpos, int seclen, boolean unicode) throws SMBSrvException
    {
        // Determine the NTLmSSP message type
       
        int msgType = NTLMMessage.isNTLMType( secbuf, secpos);
        byte[] respBlob = null;
       
        if ( msgType == -1)
        {
            // DEBUG
           
            if ( logger.isDebugEnabled())
            {
                logger.debug("Invalid NTLMSSP token received");
                logger.debug("  Token=" + HexDump.hexString( secbuf, secpos, seclen, " "));
            }

            // Return a logon failure status
           
            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
       
        // Check for a type 1 NTLMSSP message
       
        else if ( msgType ==  NTLM.Type1)
        {
            // Create the type 1 NTLM message from the token
           
            Type1NTLMMessage type1Msg = new Type1NTLMMessage( secbuf, secpos, seclen);
           
            //  Build the type 2 NTLM response message
            //
            //  Get the flags from the client request and mask out unsupported features
           
            int ntlmFlags = type1Msg.getFlags() & NTLM_FLAGS;
           
            //  Generate a challenge for the response
           
            NTLanManAuthContext ntlmCtx = new NTLanManAuthContext();
           
            //  Build a type2 message to send back to the client, containing the challenge

            String domain = sess.getSMBServer().getServerName();
           
            List<TargetInfo> tList = new ArrayList<TargetInfo>();
           
            tList.add(new TargetInfo(NTLM.TargetDomain, domain));
            tList.add(new TargetInfo(NTLM.TargetServer, sess.getServerName()));
            tList.add(new TargetInfo(NTLM.TargetDNSDomain, domain));
            tList.add(new TargetInfo(NTLM.TargetFullDNS, domain));
           
            ntlmFlags = NTLM.FlagChallengeAccept + NTLM.FlagRequestTarget +
                        NTLM.Flag128Bit + NTLM.FlagNegotiateNTLM + NTLM.FlagNegotiateUnicode +
                        NTLM.FlagNTLM2Key + NTLM.FlagKeyExchange + NTLM.FlagTargetInfo;
           
            if ( acceptNTLMv1Logon())
                ntlmFlags += NTLM.Flag56Bit;
           
            // NTLM.FlagAlwaysSign + NTLM.FlagNegotiateSign +
           
            Type2NTLMMessage type2Msg = new Type2NTLMMessage();
           
            type2Msg.buildType2(ntlmFlags, domain, ntlmCtx.getChallenge(), null, tList);

            //  Store the type 2 message in the session until the session setup is complete
           
            sess.setSetupObject( client.getProcessId(), type2Msg);
           
            // Set the response blob using the type 2 message
           
            respBlob = type2Msg.getBytes();
        }
        else if ( msgType == NTLM.Type3)
        {
            //  Create the type 3 NTLM message from the token
           
            Type3NTLMMessage type3Msg = new Type3NTLMMessage( secbuf, secpos, seclen, unicode);
           
            //  Make sure a type 2 message was stored in the first stage of the session setup
           
            if ( sess.hasSetupObject( client.getProcessId()) == false || sess.getSetupObject( client.getProcessId()) instanceof Type2NTLMMessage == false)
            {
                //  Clear the setup object
               
                sess.removeSetupObject( client.getProcessId());
               
                //  Return a logon failure

                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }

            //  Determine if the client sent us NTLMv1 or NTLMv2
           
            if ( type3Msg.hasFlag( NTLM.Flag128Bit) && type3Msg.hasFlag( NTLM.FlagNTLM2Key))
            {
                //  Determine if the client sent us an NTLMv2 blob or an NTLMv2 session key
               
                if ( type3Msg.getNTLMHashLength() > 24)
                {
                    //  Looks like an NTLMv2 blob
                   
                    doNTLMv2Logon( sess, client, type3Msg);

                    //  Debug
                   
                    if ( logger.isDebugEnabled())
                        logger.debug("Logged on using NTLMSSP/NTLMv2");
                }
                else
                {
                    //  Looks like an NTLMv2 session key
                   
                    doNTLMv2SessionKeyLogon( sess, client, type3Msg);

                    //  Debug
                   
                    if ( logger.isDebugEnabled())
                        logger.debug("Logged on using NTLMSSP/NTLMv2SessKey");
                }
            }
            else
            {
                //  Looks like an NTLMv1 blob
               
                doNTLMv1Logon( sess, client, type3Msg);

                //  Debug
               
                if ( logger.isDebugEnabled())
                    logger.debug("Logged on using NTLMSSP/NTLMv1");
            }
        }
       
        // Return the response blob
       
        return respBlob;
    }

    /**
     * Process an SPNEGO security blob
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @param secbuf byte[]
     * @param secpos int
     * @param seclen int
     * @param unicode boolean
     * @exception SMBSrvException
     */
    private final byte[] doSpnegoSessionSetup( SMBSrvSession sess, ClientInfo client,
            byte[] secbuf, int secpos, int seclen, boolean unicode) throws SMBSrvException
    {
        //  Check the received token type, if it is a target token and there is a stored session setup object, this is the second
        //  stage of an NTLMSSP session setup that is wrapped with SPNEGO

        int tokType = -1;
       
        try
        {
            tokType = SPNEGO.checkTokenType( secbuf, secpos, seclen);
        }
        catch ( IOException ex)
        {
        }

        //  Check for the second stage of an NTLMSSP logon
       
        NegTokenTarg negTarg = null;
       
        if ( tokType == SPNEGO.NegTokenTarg && sess.hasSetupObject( client.getProcessId()) && sess.getSetupObject( client.getProcessId()) instanceof Type2NTLMMessage)
        {
            //  Get the NTLMSSP blob from the NegTokenTarg blob
           
            NegTokenTarg negToken = new NegTokenTarg();
           
            try
            {
                // Decode the security blob
               
                negToken.decode( secbuf, secpos, seclen);
            }
            catch ( IOException ex)
            {
                // Log the error
               
                logger.error(ex);
               
                // Return a logon failure status
               
                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }

            //  Get the second stage NTLMSSP blob
           
            byte[] ntlmsspBlob = negToken.getResponseToken();

            //  Perform an NTLMSSP session setup
           
            byte[] ntlmsspRespBlob = doNtlmsspSessionSetup( sess, client, ntlmsspBlob, 0, ntlmsspBlob.length, unicode);
           
            //  NTLMSSP is a two stage process, set the SPNEGO status
           
            int spnegoSts = SPNEGO.AcceptCompleted;
           
            if ( sess.hasSetupObject( client.getProcessId()))
                spnegoSts = SPNEGO.AcceptIncomplete;
           
            //  Package the NTLMSSP response in an SPNEGO response

            negTarg = new NegTokenTarg( spnegoSts, null, ntlmsspRespBlob);
        }
        else if ( tokType == SPNEGO.NegTokenInit)
        {
            //  Parse the SPNEGO security blob to get the Kerberos ticket
           
            NegTokenInit negToken = new NegTokenInit();
           
            try
            {
                // Decode the security blob
               
                negToken.decode( secbuf, secpos, seclen);
            }
            catch ( IOException ex)
            {
                // Log the error
               
                logger.error(ex);
               
                // Return a logon failure status
               
                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
   
            //  Determine the authentication mechanism the client is using and logon
           
            String oidStr = null;
            if ( negToken.numberOfOids() > 0)
                oidStr = negToken.getOidAt( 0).toString();
           
            if ( oidStr != null && oidStr.equals( OID.ID_NTLMSSP))
            {
                //  NTLMSSP logon, get the NTLMSSP security blob that is inside the SPNEGO blob
               
                byte[] ntlmsspBlob = negToken.getMechtoken();
   
                //  Perform an NTLMSSP session setup
               
                byte[] ntlmsspRespBlob = doNtlmsspSessionSetup( sess, client, ntlmsspBlob, 0, ntlmsspBlob.length, unicode);
               
                //  NTLMSSP is a two stage process, set the SPNEGO status
               
                int spnegoSts = SPNEGO.AcceptCompleted;
               
                if ( sess.hasSetupObject( client.getProcessId()))
                    spnegoSts = SPNEGO.AcceptIncomplete;
               
                //  Package the NTLMSSP response in an SPNEGO response
   
                negTarg = new NegTokenTarg( spnegoSts, OID.NTLMSSP, ntlmsspRespBlob);
            }
            else if (  oidStr != null && (oidStr.equals( OID.ID_MSKERBEROS5) || oidStr.equals(OID.ID_KERBEROS5)))
            {
                //  Kerberos logon
               
                negTarg = doKerberosLogon( sess, negToken, client);
            }
            else
            {
                //  Debug
               
                if ( logger.isDebugEnabled())
                {
                    logger.debug("No matching authentication OID found");
                    logger.debug("  " + negToken.toString());
                }
                   
                //  No valid authentication mechanism
               
                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
        }
        else
        {
            //  Unknown SPNEGO token type
           
            logger.error( "Unknown SPNEGO token type");
           
            // Return a logon failure status
           
            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
       
        // Generate the NegTokenTarg blob

        byte[] respBlob = null;
       
        try
        {
            // Generate the response blob
           
           respBlob = negTarg.encode();
        }
        catch ( IOException ex)
        {
            //  Debug
           
            if ( logger.isDebugEnabled())
                logger.debug("Failed to encode NegTokenTarg", ex);

            //  Failed to build response blob
           
            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
       
        //  Return the SPNEGO response blob
       
        return respBlob;
    }
   
    /**
     * Perform a Kerberos login and return an SPNEGO response
     *
     * @param sess SMBSrvSession
     * @param negToken NegTokenInit
     * @param client ClientInfo
     * @return NegTokenTarg
     * @exception SMBSrvException
     */
    private final NegTokenTarg doKerberosLogon( SMBSrvSession sess, NegTokenInit negToken, ClientInfo client)
        throws SMBSrvException
    {
        //  Authenticate the user
       
        KerberosDetails krbDetails = null;
        NegTokenTarg negTokenTarg = null;
       
        try
        {
           // Parse the mechToken to get the AP-REQ details
           
           KerberosApReq krbApReq = new KerberosApReq();
           krbApReq.parseMechToken( negToken.getMechtoken());
           
           if ( logger.isDebugEnabled())
              logger.debug( "Kerberos AP-REQ - " + krbApReq);
           
           // Check if mutual authentication is required

           KrbAuthContext krbAuthCtx = null;
           
           if ( krbApReq.hasMutualAuthentication())
           {
              // Allocate the Kerberos authentication and parse the AP-REQ
              
              krbAuthCtx = new KrbAuthContext();
              krbAuthCtx.setDebug(hasDebug());
              
              // DEBUG
              
              if ( logger.isDebugEnabled())
                 logger.debug("Kerberos mutual auth required, parsing AP-REQ");
              
              try {
                 
                 // Parse the AP-REQ
                 
                 krbAuthCtx.parseKerberosApReq( m_loginContext.getSubject(), krbApReq);
              }
              catch ( IOException ex)
              {
                 // Failed to parse AP-REQ
                 
                 if ( logger.isDebugEnabled())
                    logger.debug("Failed to parse AP-REQ, " + ex.toString());
                 
                    // Return a logon failure status
                   
                    throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
              }
           }
           
            //  Run the session setup as a privileged action
           
            SessionSetupPrivilegedAction sessSetupAction = new SessionSetupPrivilegedAction( m_accountName, negToken.getMechtoken());
            Object result = Subject.doAs( m_loginContext.getSubject(), sessSetupAction);
   
            if ( result != null)
            {
                // Access the Kerberos response
               
                krbDetails = (KerberosDetails) result;

                // Determine the response OID
               
                Oid respOid = null;
               
                if ( negToken.hasOid( OID.MSKERBEROS5))
                {
                   respOid = OID.MSKERBEROS5;
                   
                   // DEBUG
                   
                   if ( logger.isDebugEnabled())
                      logger.debug("Using OID MS Kerberos5 for NegTokenTarg");
                }
                else
                {
                   respOid = OID.KERBEROS5;
                   
                   // DEBUG
                   
                   if ( logger.isDebugEnabled())
                      logger.debug("Using OID Kerberos5 for NegTokenTarg");
                }

                // If mutual authentication is required then we unpack the AP-REP and add in the missing
                // subkey that the AD client requires
               
                if ( krbAuthCtx != null)
                {
                   try
                   {
                      // Parse the AP-REP and add the missing subkey, return the updated response blob
                      
                      byte[] respToken = krbAuthCtx.parseKerberosApRep( krbDetails.getResponseToken());
                      krbDetails.setResponseToken(respToken);
                      
                      // Create the NegtokenTarg
                      
                      negTokenTarg = new NegTokenTarg( SPNEGO.AcceptCompleted, respOid, krbDetails.getResponseToken());
                      
                      // DEBUG
                      
                      if ( logger.isDebugEnabled())
                         logger.debug("Created NegTokenTarg using updated AP-REP, added subkey");
                   }
                   catch (Exception ex)
                   {
                    if ( logger.isDebugEnabled()) {
                       logger.debug("AP-REP Error:");
                       logger.debug( ex);
                    }
                   }
                }
                else
                {
                   // Create the NegTokenTarg response blob
                  
                   negTokenTarg = new NegTokenTarg( SPNEGO.AcceptCompleted, respOid, krbDetails.getResponseToken());
                  
                   // DEBUG
                  
                   if ( logger.isDebugEnabled())
                      logger.debug("Created NegTokenTarg using standard Krb5 API response");
                }
               
               // Check if this is a null logon
               
               String userName = krbDetails.getUserName();
               
               if ( userName != null)
               {
                  // Check for the machine account name
                  
                    // ALF-4395: Sometimes machine account name comes lowercase
                    // and new Alfresco user is being created with machine name
                    // if ( userName.endsWith( "$") && userName.equals( userName.toUpperCase()))
                    if ( userName.endsWith( "$"))
                  {
                     // Null logon
                     
                      client.setLogonType( ClientInfo.LogonNull);

                      //  Debug
                       
                        if ( logger.isDebugEnabled())
                            logger.debug("Machine account logon, " + userName + ", as null logon");
                  }
                  else
                  {
                        // Set the current user to be authenticated, save the authentication token

                      try
                      {
                            AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
                            getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(krbDetails.getUserName()));
                            alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket() );
                        }
                        catch (AuthenticationException e)
                        {
                            // Invalid user or max tickets exceeded. Return a logon failure status
                           
                            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
   
                        }
                       
                        // Store the full user name in the client information, indicate that this is not a guest logon
                       
                        // ALF-4599: CIFS access to alfresco creates wrong users with Realm suffix
                        // client.setUserName( krbDetails.getSourceName());
                        client.setUserName( krbDetails.getUserName());
                        client.setGuest( false);
                          
                        // Indicate that the session is logged on
                       
                        sess.setLoggedOn(true);
                  }
               }
               else
               {
                  // Null logon
                  
                  client.setLogonType( ClientInfo.LogonNull);
               }
               
                // Indicate that the session is logged on
               
                sess.setLoggedOn(true);
               
                //  Debug
               
                if ( logger.isDebugEnabled())
                   logger.debug("Logged on using Kerberos, user " + userName);
            }
            else
            {
               // Debug
               
               if ( logger.isDebugEnabled())
                  logger.debug( "No SPNEGO response, Kerberos logon failed");
               
                // Return a logon failure status
               
                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
            }
        }
        catch (Exception ex)
        {
            // Log the error
           
           if ( logger.isErrorEnabled()) {
              logger.error("Kerberos logon error");
              logger.error(ex);
           }
   
            // Return a logon failure status
           
            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
        }
   
        // Return the response SPNEGO blob
       
        return negTokenTarg;
    }
   
    /**
     * Perform an NTLMv1 logon using the NTLMSSP type3 message
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @param type3Msg Type3NTLMMessage
     * @exception SMBSrvException
     */
    private final void doNTLMv1Logon(SMBSrvSession sess, ClientInfo client, Type3NTLMMessage type3Msg)
        throws SMBSrvException
    {
        // Check if NTLMv1 logons are allowed
       
        if ( acceptNTLMv1Logon() == false)
        {
            //  NTLMv1 password hashes not accepted
           
            logger.warn("NTLMv1 not accepted, client " + sess.getRemoteName());
           
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
       
        //  Get the type 2 message that contains the challenge sent to the client
       
        Type2NTLMMessage type2Msg = (Type2NTLMMessage) sess.getSetupObject( client.getProcessId());
        sess.removeSetupObject( client.getProcessId());
       
        // Check if we are using local MD4 password hashes or passthru authentication
       
        if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
        {
            // Get the NTLM logon details
           
            String userName = type3Msg.getUserName();
           
            //  Check for a null logon
           
            if ( userName.length() == 0)
            {
                //  DEBUG
               
                if ( logger.isDebugEnabled())
                    logger.debug("Null logon");
               
                //  Indicate a null logon in the client information
               
                client.setLogonType( ClientInfo.LogonNull);
                return;
            }
           
            //  Get the stored MD4 hashed password for the user, or null if the user does not exist
           
            String md4hash = getNTLMAuthenticator().getMD4HashedPassword(userName);
           
            if ( md4hash != null)
            {
                // Generate the local encrypted password using the challenge that was sent to the client
               
                byte[] p21 = new byte[21];
                byte[] md4byts = m_md4Encoder.decodeHash(md4hash);
                System.arraycopy(md4byts, 0, p21, 0, 16);
               
                // Generate the local hash of the password using the same challenge
               
                byte[] localHash = null;
               
                try
                {
                    localHash = getEncryptor().doNTLM1Encryption(p21, type2Msg.getChallenge());
                }
                catch (NoSuchAlgorithmException ex)
                {
                }
               
                // Validate the password
               
                byte[] clientHash = type3Msg.getNTLMHash();

                if ( clientHash != null && localHash != null && clientHash.length == localHash.length)
                {
                    int i = 0;

                    while ( i < clientHash.length && clientHash[i] == localHash[i])
                        i++;
                   
                    if ( i != clientHash.length)
                    {
                        //  Return a logon failure

                        throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                    }
                }

                // Setup the Acegi authenticated user
               
                try
                {
                    AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
                    getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(userName));               
                    alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
                }
                catch (AuthenticationException e)
                {
                    // Invalid user or max tickets exceeded. Return a logon failure status

                    throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
                }
               
                // Store the full user name in the client information, indicate that this is not a guest logon
               
                client.setUserName( userName.toLowerCase());
                client.setGuest( false);
               
                // Indicate that the session is logged on
               
                sess.setLoggedOn(true);
            }
            else
            {
                //  Log a warning, user does not exist
               
                logger.warn("User does not exist, " + userName);
               
                //  Return a logon failure

                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
        }
        else
        {
            //  Log a warning, authentication component does not support MD4 hashed passwords
           
            logger.warn("Authentication component does not support MD4 password hashes");
           
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
    }

    /**
     * Perform an NTLMv1 logon using the NTLMSSP type3 message
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @exception SMBSrvException
     */
    private final void doNTLMv1Logon(SMBSrvSession sess, ClientInfo client)
        throws SMBSrvException
    {
        // Check if NTLMv1 logons are allowed
       
        if ( acceptNTLMv1Logon() == false)
        {
            //  NTLMv1 password hashes not accepted
           
            logger.warn("NTLMv1 not accepted, client " + sess.getRemoteName());
           
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
       
        // Check if we are using local MD4 password hashes or passthru authentication
       
        if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
        {
            //  Check for a null logon
           
            if ( client.getUserName().length() == 0)
            {
                //  DEBUG
               
                if ( logger.isDebugEnabled())
                    logger.debug("Null logon");
               
                //  Indicate a null logon in the client information
               
                client.setLogonType( ClientInfo.LogonNull);
                return;
            }
           
            //  Get the stored MD4 hashed password for the user, or null if the user does not exist
           
            String md4hash = getNTLMAuthenticator().getMD4HashedPassword(client.getUserName());
           
            if ( md4hash != null)
            {
                // Generate the local encrypted password using the challenge that was sent to the client
               
                byte[] p21 = new byte[21];
                byte[] md4byts = m_md4Encoder.decodeHash(md4hash);
                System.arraycopy(md4byts, 0, p21, 0, 16);

                // Get the challenge that was sent to the client during negotiation
               
                byte[] challenge = null;
                if ( sess.hasAuthenticationContext())
                {
                    // Get the challenge from the authentication context
                   
                    NTLanManAuthContext ntlmCtx = (NTLanManAuthContext) sess.getAuthenticationContext();
                    challenge = ntlmCtx.getChallenge();
                }
               
                // Generate the local hash of the password using the same challenge
               
                byte[] localHash = null;
               
                try
                {
                    localHash = getEncryptor().doNTLM1Encryption(p21, challenge);
                }
                catch (NoSuchAlgorithmException ex)
                {
                }
               
                // Validate the password
               
                byte[] clientHash = client.getPassword();
               
                if ( clientHash != null && localHash != null && clientHash.length == localHash.length)
                {
                    int i = 0;

                    while ( i < clientHash.length && clientHash[i] == localHash[i])
                        i++;
                   
                    if ( i != clientHash.length)
                    {
                        //  Return a logon failure

                        throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                    }
                }

                // Setup the Acegi authenticated user
               
                try
                {
                    AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
                    getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(client.getUserName()));               
                    alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
                }
                catch (AuthenticationException e)
                {
                    // Invalid user or max tickets exceeded. Return a logon failure status

                    throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
                }
                // Store the full user name in the client information, indicate that this is not a guest logon
               
                client.setGuest( false);
               
                // Indicate that the session is logged on
               
                sess.setLoggedOn(true);
            }
            else
            {
                //  Log a warning, user does not exist
               
                logger.warn("User does not exist, " + client.getUserName());
               
                //  Return a logon failure

                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
        }
        else
        {
            //  Log a warning, authentication component does not support MD4 hashed passwords
           
            logger.warn("Authentication component does not support MD4 password hashes");
           
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
    }

    /**
     * Perform an NTLMv2 logon using the NTLMSSP type3 message
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @param type3Msg Type3NTLMMessage
     * @exception SMBSrvException
     */
    private final void doNTLMv2Logon(SMBSrvSession sess, ClientInfo client, Type3NTLMMessage type3Msg)
        throws SMBSrvException
    {
        //  Get the type 2 message that contains the challenge sent to the client
       
        Type2NTLMMessage type2Msg = (Type2NTLMMessage) sess.getSetupObject( client.getProcessId());
        sess.removeSetupObject( client.getProcessId());
       
         // Check if we are using local MD4 password hashes or passthru authentication
       
        if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
        {
            // Get the NTLM logon details
           
            String userName = type3Msg.getUserName();
           
            //  Check for a null logon
           
            if ( userName.length() == 0)
            {
                //  DEBUG
               
                if ( logger.isDebugEnabled())
                    logger.debug("Null logon");
               
                //  Indicate a null logon in the client information
               
                client.setLogonType( ClientInfo.LogonNull);
                return;
            }
           
            //  Get the stored MD4 hashed password for the user, or null if the user does not exist
           
            String md4hash = getNTLMAuthenticator().getMD4HashedPassword(userName);
           
            if ( md4hash != null)
            {
                try
                {
                    // Generate the v2 hash using the challenge that was sent to the client
   
                    byte[] v2hash = getEncryptor().doNTLM2Encryption( m_md4Encoder.decodeHash(md4hash), type3Msg.getUserName(), type3Msg.getDomain());
                   
                    // Get the NTLMv2 blob sent by the client and the challenge that was sent by the server
                   
                    NTLMv2Blob v2blob = new NTLMv2Blob(type3Msg.getNTLMHash());
                    byte[] srvChallenge = type2Msg.getChallenge();
                   
                    // Calculate the HMAC of the received blob and compare
                   
                    byte[] srvHmac = v2blob.calculateHMAC( srvChallenge, v2hash);
                    byte[] clientHmac = v2blob.getHMAC();
   
                    if ( clientHmac != null && srvHmac != null && clientHmac.length == srvHmac.length)
                    {
                        int i = 0;
   
                        while ( i < clientHmac.length && clientHmac[i] == srvHmac[i])
                            i++;
                       
                        if ( i != clientHmac.length)
                        {
                            //  Return a logon failure
   
                            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                        }
                    }
   
                    // Setup the Acegi authenticated user
                   
                    AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
                    getAuthenticationComponent().setCurrentUser( mapUserNameToPerson( userName));               
                    alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
                   
                    // Store the full user name in the client information, indicate that this is not a guest logon
                   
                    client.setUserName( userName.toLowerCase());
                    client.setGuest( false);
                   
                    // Indicate that the session is logged on
                   
                    sess.setLoggedOn(true);
                }
                catch ( Exception ex)
                {
                    // Log the error
                   
                    if (ex instanceof AuthenticationException)
                    {
                        logger.debug(ex);
                    }
                    else
                    {
                        logger.error(ex);                       
                    }
                   
                    //  Return a logon failure

                    throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                }
            }
            else
            {
                //  Log a warning, user does not exist
               
                logger.warn("User does not exist, " + userName);
               
                //  Return a logon failure

                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
        }
        else
        {
            //  Log a warning, authentication component does not support MD4 hashed passwords
           
            logger.warn("Authentication component does not support MD4 password hashes");
           
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
    }
   
    /**
     * Perform an NTLMv2 logon using the NTLMSSP type3 message
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @exception SMBSrvException
     */
    private final void doNTLMv2Logon(SMBSrvSession sess, ClientInfo client)
        throws SMBSrvException
    {
        // Check if we are using local MD4 password hashes or passthru authentication
       
        if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
        {
            //  Check for a null logon
           
            if ( client.getUserName().length() == 0)
            {
                //  DEBUG
               
                if ( logger.isDebugEnabled())
                    logger.debug("Null logon");
               
                //  Indicate a null logon in the client information
               
                client.setLogonType( ClientInfo.LogonNull);
                return;
            }
           
            //  Get the stored MD4 hashed password for the user, or null if the user does not exist
           
            String md4hash = getNTLMAuthenticator().getMD4HashedPassword(client.getUserName());
           
            if ( md4hash != null)
            {
                try
                {
                    // Create the NTLMv2 blob from the received hashed password bytes
                   
                    NTLMv2Blob v2blob = new NTLMv2Blob(client.getPassword());
                   
                    // Generate the v2 hash using the challenge that was sent to the client
   
                    byte[] v2hash = getEncryptor().doNTLM2Encryption( m_md4Encoder.decodeHash(md4hash), client.getUserName(), client.getDomain());

                    // Get the challenge that was sent to the client during negotiation
                   
                    byte[] srvChallenge = null;
                    if ( sess.hasAuthenticationContext())
                    {
                        // Get the challenge from the authentication context
                       
                        NTLanManAuthContext ntlmCtx = (NTLanManAuthContext) sess.getAuthenticationContext();
                        srvChallenge = ntlmCtx.getChallenge();
                    }
                   
                    // Calculate the HMAC of the received blob and compare
                   
                    byte[] srvHmac = v2blob.calculateHMAC( srvChallenge, v2hash);
                    byte[] clientHmac = v2blob.getHMAC();
   
                    if ( clientHmac != null && srvHmac != null && clientHmac.length == srvHmac.length)
                    {
                        int i = 0;
   
                        while ( i < clientHmac.length && clientHmac[i] == srvHmac[i])
                            i++;
                       
                        if ( i != clientHmac.length)
                        {
                            //  Return a logon failure
   
                            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                        }
                    }
   
                    // Setup the Acegi authenticated user
                   
                    AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
                    getAuthenticationComponent().setCurrentUser( mapUserNameToPerson( client.getUserName()));               
                    alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
                   
                    // Store the full user name in the client information, indicate that this is not a guest logon
                   
                    client.setGuest( false);
                   
                    // Indicate that the session is logged on
                   
                    sess.setLoggedOn(true);
                }
                catch ( Exception ex)
                {
                    // Log the error
                   
                    if (ex instanceof AuthenticationException)
                    {
                        logger.debug(ex);
                    }
                    else
                    {
                        logger.error(ex);                       
                    }
                   
                    //  Return a logon failure

                    throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                }
            }
            else
            {
                //  Log a warning, user does not exist
               
                logger.warn("User does not exist, " + client.getUserName());
               
                //  Return a logon failure

                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
        }
        else
        {
            //  Log a warning, authentication component does not support MD4 hashed passwords
           
            logger.warn("Authentication component does not support MD4 password hashes");
           
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
    }
   
    /**
     * Perform an NTLMv2 session key logon
     *
     * @param sess SMBSrvSession
     * @param client ClientInfo
     * @param type3Msg Type3NTLMMessage
     * @exception SMBSrvException
     */
    private final void doNTLMv2SessionKeyLogon(SMBSrvSession sess, ClientInfo client, Type3NTLMMessage type3Msg)
       throws SMBSrvException
    {
        //  Get the type 2 message that contains the challenge sent to the client
      
        Type2NTLMMessage type2Msg = (Type2NTLMMessage) sess.getSetupObject( client.getProcessId());
        sess.removeSetupObject( client.getProcessId());

        // Check if we are using local MD4 password hashes or passthru authentication
      
        if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
        {
            // Get the NTLM logon details
          
            String userName = type3Msg.getUserName();
          
            //  Check for a null logon
          
            if ( userName.length() == 0)
            {
                //  DEBUG
              
                if ( logger.isDebugEnabled())
                    logger.debug("Null logon");
              
                //  Indicate a null logon in the client information
              
                client.setLogonType( ClientInfo.LogonNull);
                return;
            }
          
            //  Get the stored MD4 hashed password for the user, or null if the user does not exist
          
            String md4hash = getNTLMAuthenticator().getMD4HashedPassword(userName);
          
            if ( md4hash != null)
            {
                // Create the value to be encrypted by appending the server challenge and client challenge
                // and applying an MD5 digest
               
                byte[] nonce = new byte[16];
                System.arraycopy( type2Msg.getChallenge(), 0, nonce, 0, 8);
                System.arraycopy( type3Msg.getLMHash(), 0, nonce, 8, 8);
               
                MessageDigest md5 = null;
                byte[] v2challenge = new byte[8];
               
                try
                {
                    //  Create the MD5 digest
                   
                    md5 = MessageDigest.getInstance( "MD5");
                   
                    //  Apply the MD5 digest to the nonce
                   
                    md5.update( nonce);
                    byte[] md5nonce = md5.digest();
                   
                    //  We only want the first 8 bytes
                   
                    System.arraycopy( md5nonce, 0, v2challenge, 0, 8);
                }
                catch ( NoSuchAlgorithmException ex)
                {
                    // Log the error
                   
                    logger.error( ex);
                }
               
                // Generate the local encrypted password using the MD5 generated challenge
              
                byte[] p21 = new byte[21];
                byte[] md4byts = m_md4Encoder.decodeHash(md4hash);
                System.arraycopy(md4byts, 0, p21, 0, 16);
              
                // Generate the local hash of the password
              
                byte[] localHash = null;
              
                try
                {
                    localHash = getEncryptor().doNTLM1Encryption(p21, v2challenge);
                }
                catch (NoSuchAlgorithmException ex)
                {
                    // Log the error
                   
                    logger.error( ex);
                }
              
                // Validate the password
              
                byte[] clientHash = type3Msg.getNTLMHash();

                if ( clientHash != null && localHash != null && clientHash.length == localHash.length)
                {
                    int i = 0;

                    while ( i < clientHash.length && clientHash[i] == localHash[i])
                        i++;
                  
                    if ( i != clientHash.length)
                    {
                        //  Return a logon failure

                        throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
                    }
                }

                // Setup the Acegi authenticated user
              
                try
                {
                    AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
                    getAuthenticationComponent().setCurrentUser( mapUserNameToPerson( userName));               
                    alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
                }
                catch (AuthenticationException e)
                {
                    // Invalid user or max tickets exceeded. Return a logon failure status

                    throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
                }
              
                // Store the full user name in the client information, indicate that this is not a guest logon
              
                client.setUserName( userName.toLowerCase());
                client.setGuest( false);
              
                // Indicate that the session is logged on
              
                sess.setLoggedOn(true);
            }
            else
            {
                //  Log a warning, user does not exist
              
                logger.warn("User does not exist, " + userName);
              
                //  Return a logon failure

                throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
            }
        }
        else
        {
            //  Log a warning, authentication component does not support MD4 hashed passwords
          
            logger.warn("Authentication component does not support MD4 password hashes");
          
            //  Return a logon failure

            throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
    }
   
    /**
     * Perform a hashed password logon using either NTLMv1 or NTLMv2
     *
     * @param sess SMBSrvSession
     * @param reqPkt SMBSrvPacket
     * @exception SMBSrvException
     */
    private final void doHashedPasswordLogon( SMBSrvSession sess, SMBSrvPacket reqPkt)
        throws SMBSrvException
    {
        // Check that the received packet looks like a valid NT session setup andX request

        if (reqPkt.checkPacketIsValid(13, 0) == false)
        {
            throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
        }

        // Extract the session details

        int maxBufSize = reqPkt.getParameter(2);
        int maxMpx = reqPkt.getParameter(3);
        int vcNum = reqPkt.getParameter(4);
        int ascPwdLen = reqPkt.getParameter(7);
        int uniPwdLen = reqPkt.getParameter(8);
        int capabs = reqPkt.getParameterLong(11);

        // Extract the client details from the session setup request

        byte[] buf = reqPkt.getBuffer();

        // Determine if ASCII or unicode strings are being used

        boolean isUni = reqPkt.isUnicode();

        // Extract the password strings

        byte[] ascPwd = reqPkt.unpackBytes(ascPwdLen);
        byte[] uniPwd = reqPkt.unpackBytes(uniPwdLen);

        // Extract the user name string

        String user = reqPkt.unpackString(isUni);

        if (user == null)
        {
            throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
        }

        // Extract the clients primary domain name string

        String domain = "";

        if (reqPkt.hasMoreData())
        {

            // Extract the callers domain name

            domain = reqPkt.unpackString(isUni);

            if (domain == null)
            {
                throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
            }
        }

        // Extract the clients native operating system

        String clientOS = "";

        if (reqPkt.hasMoreData())
        {

            // Extract the callers operating system name

            clientOS = reqPkt.unpackString(isUni);

            if (clientOS == null)
            {
                throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
            }
        }

        // DEBUG

        if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
        {
            logger.debug("NT Session setup from user=" + user + ", password="
                    + (uniPwd != null ? HexDump.hexString(uniPwd) : "none") + ", ANSIpwd="
                    + (ascPwd != null ? HexDump.hexString(ascPwd) : "none") + ", domain=" + domain + ", os=" + clientOS
                    + ", VC=" + vcNum + ", maxBuf=" + maxBufSize + ", maxMpx=" + maxMpx
                    + ", authCtx=" + sess.getAuthenticationContext());
            logger.debug("  MID=" + reqPkt.getMultiplexId() + ", UID=" + reqPkt.getUserId() + ", PID="
                    + reqPkt.getProcessId());
        }

        // Store the client maximum buffer size, maximum multiplexed requests count and client
        // capability flags

        sess.setClientMaximumBufferSize(maxBufSize);
        sess.setClientMaximumMultiplex(maxMpx);
        sess.setClientCapabilities(capabs);

        // Create the client information and store in the session

        ClientInfo client = new AlfrescoClientInfo(user, uniPwd);
        client.setANSIPassword(ascPwd);
        client.setDomain(domain);
        client.setOperatingSystem(clientOS);

        if (sess.hasRemoteAddress())
            client.setClientAddress(sess.getRemoteAddress().getHostAddress());

        // Check if this is a null session logon

        if (user.length() == 0 && domain.length() == 0 && uniPwdLen == 0)
            client.setLogonType(ClientInfo.LogonNull);

        // Authenticate the user using the Unicode password hash, this is either NTLMv1 or NTLMv2 encoded

        boolean isGuest = false;

        if ( uniPwd != null)
        {
            if ( uniPwd.length == 24)
            {
                // NTLMv1 hashed password
               
                doNTLMv1Logon(sess, client);

                //  Debug
               
                if ( logger.isDebugEnabled())
                    logger.debug("Logged on using Hashed/NTLMv1");
            }
            else if ( uniPwd.length > 0)
            {
                // NTLMv2 blob
               
                doNTLMv2Logon( sess, client);

                //  Debug
               
                if ( logger.isDebugEnabled())
                    logger.debug("Logged on using Hashed/NTLMv2");
            }
        }
       
        // Check if the user was logged on as guest
       
        if ( client.isGuest())
        {

            // Guest logon

            isGuest = true;

            // DEBUG

            if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
                logger.debug("User " + user + ", logged on as guest");
        }

        // Create a virtual circuit and allocate a UID to the new circuit

        VirtualCircuit vc = new VirtualCircuit( vcNum, client);
        int uid = sess.addVirtualCircuit( vc);
       
        if ( uid == VirtualCircuit.InvalidUID)
        {
       
           // DEBUG
         
           if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
              logger.debug("Failed to allocate UID for virtual circuit, " + vc);
         
           // Failed to allocate a UID
         
           throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
        }
        else if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
        {
           // DEBUG
         
           logger.debug("Allocated UID=" + uid + " for VC=" + vc);
        }
       
        // Set the guest flag for the client, indicate that the session is logged on

        client.setGuest(isGuest);
        sess.setLoggedOn(true);

        // Build the session setup response SMB

        reqPkt.setParameterCount(3);
        reqPkt.setParameter(0, 0); // No chained response
        reqPkt.setParameter(1, 0); // Offset to chained response
        reqPkt.setParameter(2, isGuest ? 1 : 0);
        reqPkt.setByteCount(0);

        reqPkt.setTreeId(0);
        reqPkt.setUserId(uid);

        // Set the various flags

        int flags = reqPkt.getFlags();
        flags &= ~SMBSrvPacket.FLG_CASELESS;
        reqPkt.setFlags(flags);

        int flags2 = SMBSrvPacket.FLG2_LONGFILENAMES;
        if (isUni)
            flags2 += SMBSrvPacket.FLG2_UNICODE;
        reqPkt.setFlags2(flags2);

        // Pack the OS, dialect and domain name strings.

        int pos = reqPkt.getByteOffset();
        buf = reqPkt.getBuffer();

        if (isUni)
            pos = DataPacker.wordAlign(pos);

        pos = DataPacker.putString("Java", buf, pos, true, isUni);
        pos = DataPacker.putString("Alfresco CIFS Server " + sess.getServer().isVersion(), buf, pos, true, isUni);
        pos = DataPacker.putString(getCIFSConfig().getDomainName(), buf, pos, true, isUni);

        reqPkt.setByteCount(pos - reqPkt.getByteOffset());
    }
}
When I start the server and try \\testA\Alfresco in Windows Explorer here’s what I get:
“Windows cannot access \\testA\Alfresco You do not have permission to access \\testA\Alfresco Contact your network administrator to request access.”
What am I doing wrong?  :oops:

Outcomes