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

org.filesys.server.auth.passthru.PassthruAuthenticator Maven / Gradle / Ivy

Go to download

Java file server with SMB, FTP/FTPS and NFS support, virtual filesystems, database filesystems

There is a newer version: 1.4.0
Show newest version
/*
 * Copyright (C) 2006-2013 Alfresco Software Limited.
 * Copyright (C) GK Spencer
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see .
 */

package org.filesys.server.auth.passthru;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;

import org.filesys.server.auth.*;
import org.filesys.server.auth.ntlm.*;
import org.filesys.server.auth.spnego.NegTokenInit;
import org.filesys.server.auth.spnego.OID;
import org.filesys.server.auth.spnego.SPNEGO;
import org.filesys.server.config.InvalidConfigurationException;
import org.filesys.smb.Protocol;
import org.filesys.smb.server.*;
import org.filesys.util.DataPacker;
import org.springframework.extensions.config.ConfigElement;
import org.filesys.debug.Debug;
import org.filesys.server.SessionListener;
import org.filesys.server.SrvSession;
import org.filesys.server.auth.spnego.NegTokenTarg;
import org.filesys.server.config.ServerConfiguration;
import org.filesys.server.core.NoPooledMemoryException;
import org.filesys.server.core.ShareType;
import org.filesys.server.core.SharedDevice;
import org.filesys.smb.Capability;
import org.filesys.smb.SMBStatus;
import org.filesys.util.HexDump;

/**
 * Passthru Authenticator Class
 *
 * 

* Authenticate users accessing the CIFS server by validating the user against a domain controller * or other server on the network. * * @author GKSpencer */ public class PassthruAuthenticator extends SMBAuthenticator implements SessionListener { // Constants public final static int DefaultSessionTmo = 5000; // 5 seconds public final static int MinSessionTmo = 2000; // 2 seconds public final static int MaxSessionTmo = 30000; // 30 seconds public final static int MinCheckInterval = 10; // 10 seconds public final static int MaxCheckInterval = 15 * 60; // 15 minutes // Passthru keep alive interval public final static long PassthruKeepAliveInterval = 60000L; // 60 seconds // 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.FlagNegotiateUnicode; // Passthru servers used to authenticate users private PassthruServers m_passthruServers; // SMB server private SMBServer m_server; // Sessions that are currently in the negotiate/session setup state private Hashtable m_sessions; /** * Default Constructor * *

Default to user mode security with encrypted password support. */ public PassthruAuthenticator() { // Allocate the session table m_sessions = new Hashtable(); // Enable extended security session setup setExtendedSecurity(true); } @Override public ShareStatus authenticateShareConnect(ClientInfo client, SharedDevice share, String sharePwd, SrvSession sess) { // If the server is in share mode security allow the user access if (getAccessMode() == AuthMode.SHARE) return ShareStatus.WRITEABLE; // Check if the IPC$ share is being accessed if (share.getType() == ShareType.ADMINPIPE) return ShareStatus.WRITEABLE; // Check if the user is allowed to access the specified shared device // // If a user does not have access to the requested share the connection will still be allowed but any attempts // to access files or search directories will result in a 'no access rights' error being returned to the client. UserAccount user = null; if (client != null) user = getUserDetails(client.getUserName()); if (user == null) { // Check if the guest account is enabled return allowGuest() ? ShareStatus.WRITEABLE : ShareStatus.NO_ACCESS; } else if (user.hasShare(share.getName()) == false) return ShareStatus.NO_ACCESS; // Allow user to access this share return ShareStatus.WRITEABLE; } /** * Authenticate a session setup by a user * * @param client ClientInfo * @param sess SrvSession * @param alg int * @return AuthStatus */ public AuthStatus authenticateUser(ClientInfo client, SrvSession sess, int alg) { // The null session will only be allowed to connect to the IPC$ named pipe share. if (client.isNullSession()) { // Debug if (hasDebug()) Debug.println("Null CIFS logon allowed"); return AuthStatus.AUTHENTICATED; } // Check if this is a guest logon AuthStatus authSts = AuthStatus.DISALLOW; if (client.isGuest() || client.getUserName().equalsIgnoreCase(getGuestUserName())) { // Check if guest logons are allowed if (allowGuest() == false) return AuthStatus.DISALLOW; // Get a guest authentication token doGuestLogon(client, sess); // Indicate logged on as guest authSts = AuthStatus.GUEST_LOGON; // DEBUG if (hasDebug()) Debug.println("Authenticated user " + client.getUserName() + " sts=" + authSts.name()); // Return the guest status return authSts; } // Find the active authentication session details for the server session PassthruDetails passDetails = (PassthruDetails) m_sessions.get(sess.getUniqueId()); if (passDetails != null) { try { // Authenticate the user by passing the hashed password to the authentication server // using the session that has already been setup. AuthenticateSession authSess = passDetails.getAuthenticateSession(); authSess.doSessionSetup(client.getUserName(), client.getANSIPassword(), client.getPassword()); // Check if the user has been logged on as a guest if (authSess.isGuest()) { // Check if the local server allows guest access if (allowGuest() == true) { // Get a guest authentication token doGuestLogon(client, sess); // Allow the user access as a guest authSts = AuthStatus.GUEST_LOGON; // Debug if (hasDebug()) Debug.println("Passthru authenticate user=" + client.getUserName() + ", GUEST"); } } else { // Allow the user full access to the server authSts = AuthStatus.AUTHENTICATED; // Debug if (hasDebug()) Debug.println("Passthru authenticate user=" + client.getUserName() + ", FULL"); } } catch (Exception ex) { // Debug Debug.println(ex.getMessage()); } // Keep the authentication session if the user session is an SMB session, else close the session now if ((sess instanceof SMBSrvSession) == false) { // Remove the passthru session from the active list m_sessions.remove(sess.getUniqueId()); // Close the passthru authentication session try { // Close the authentication session AuthenticateSession authSess = passDetails.getAuthenticateSession(); authSess.CloseSession(); // DEBUG if (hasDebug()) Debug.println("Closed auth session, sessId=" + authSess.getSessionId()); } catch (Exception ex) { // Debug Debug.println("Passthru error closing session (auth user) " + ex.getMessage()); } } } else { // DEBUG if (hasDebug()) Debug.println(" No PassthruDetails for " + sess.getUniqueId()); } // Return the authentication status return authSts; } /** * Return an authentication context for the new session * * @return AuthContext */ public AuthContext getAuthContext(SMBSrvSession sess) { // Make sure the SMB server listener is installed if (m_server == null && sess instanceof SMBSrvSession) { SMBSrvSession smbSess = (SMBSrvSession) sess; m_server = smbSess.getSMBServer(); // Install the server listener m_server.addSessionListener(this); } // Open a connection to the authentication server, use normal session setup AuthContext authCtx = null; try { AuthenticateSession authSess = m_passthruServers.openSession(); if (authSess != null) { // Create an entry in the active sessions table for the new session PassthruDetails passDetails = new PassthruDetails(sess, authSess); m_sessions.put(sess.getUniqueId(), passDetails); // Use the challenge key returned from the authentication server authCtx = new NTLanManAuthContext(authSess.getEncryptionKey()); sess.setAuthenticationContext(authCtx); // DEBUG if (hasDebug()) Debug.println("Passthru sessId=" + authSess.getSessionId() + ", auth ctx=" + authCtx); } } catch (Exception ex) { // Debug Debug.println("Passthru error getting challenge " + ex.getMessage()); } // Return the authentication context return authCtx; } @Override public void processSessionSetup(SMBSrvSession sess, SMBSrvPacket reqPkt) throws SMBSrvException { // Get the associated parser SMBV1Parser parser = (SMBV1Parser) reqPkt.getParser(); // Check that the received packet looks like a valid NT session setup andX request if (parser.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 (parser.getParameterCount() == 13) { // Process the standard password session setup super.processSessionSetup(sess, reqPkt); return; } // Extract the session details int maxBufSize = parser.getParameter(2); int maxMpx = parser.getParameter(3); int vcNum = parser.getParameter(4); int secBlobLen = parser.getParameter(7); int capabs = parser.getParameterLong(10); // Extract the client details from the session setup request int dataPos = parser.getByteOffset(); byte[] buf = parser.getBuffer(); // Determine if ASCII or unicode strings are being used boolean isUni = parser.isUnicode(); // Make a note of the security blob position int secBlobPos = dataPos; // Extract the clients primary domain name string dataPos += secBlobLen; parser.setPosition(dataPos); String domain = ""; if (parser.hasMoreData()) { // Extract the callers domain name domain = parser.unpackString(isUni); if (domain == null) throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv); } // Extract the clients native operating system String clientOS = ""; if (parser.hasMoreData()) { // Extract the callers operating system name clientOS = parser.unpackString(isUni); if (clientOS == null) throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv); } // 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 ClientInfo client = ClientInfo.createInfo("", null); client.setDomain(domain); client.setOperatingSystem(clientOS); client.setLogonType(ClientInfo.LogonType.Normal); // 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(parser.getProcessId()); // Get the current sesion setup object, or null Object setupObj = sess.getSetupObject(client.getProcessId(), SetupObjectType.Type2Message); // Process the security blob byte[] respBlob = null; boolean isNTLMSSP = false; 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++; if (idx == NTLM.Signature.length) isNTLMSSP = true; } // Process the security blob if (isNTLMSSP == true) { // DEBUG if (hasDebug()) Debug.println("NT Session setup NTLMSSP, MID=" + parser.getMultiplexId() + ", UID=" + parser.getUserId() + ", PID=" + parser.getProcessId()); // Process an NTLMSSP security blob respBlob = doNtlmsspSessionSetup(sess, client, buf, secBlobPos, secBlobLen, isUni); } else { // Process an SPNEGO security blob respBlob = doSpnegoSessionSetup(sess, client, buf, secBlobPos, secBlobLen, isUni); } } catch (SMBSrvException ex) { // Cleanup any stored context sess.removeAllSetupObjects(client.getProcessId()); // Rethrow the exception throw ex; } // Debug if (Debug.EnableInfo && sess.hasDebug(SMBSrvSession.Dbg.NEGOTIATE)) { if (respBlob == null) Debug.println("[SMB] User " + client.getUserName() + " logged on " + (client != null ? " (type " + client.getLogonTypeString() + ")" : "")); else Debug.println("[SMB] Two stage logon (" + (isNTLMSSP ? "NTLMSSP" : "SPNEGO") + ")"); } // 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; // 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 SMBSrvPacket respPkt = reqPkt; boolean loggedOn = false; if (isNTLMSSP == true || sess.hasSetupObject(client.getProcessId(), SetupObjectType.Type2Message) || setupObj != null) { // NTLMSSP has two stages, if there is a stored setup object then indicate more processing required if (sess.hasSetupObject(client.getProcessId(), SetupObjectType.Type2Message)) parser.setLongErrorCode(SMBStatus.NTMoreProcessingRequired); else { parser.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 parser.setParameterCount(4); int reqLen = respLen + 100; // allow for strings if (reqLen > parser.getAvailableLength()) { try { // Allocate a new buffer for the response respPkt = sess.getPacketPool().allocatePacket(parser.getByteOffset() + reqLen, reqPkt); // Create a parser for the new response packet respPkt.setParser( SMBSrvPacket.Version.V1); parser = (SMBV1Parser) respPkt.getParser(); } catch (NoPooledMemoryException ex) { // DEBUG if (Debug.EnableDbg && hasDebug()) Debug.println("Authenticator failed to allocate packet from pool, reqSiz=" + (parser.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 parser.setParameter(0, 0xFF); // No chained response parser.setParameter(1, 0); // Offset to chained response parser.setParameter(2, 0); // Action parser.setParameter(3, respLen); } else { // Build a completed session setup response parser.setLongErrorCode(SMBStatus.NTSuccess); // Build the session setup response SMB parser.setParameterCount(12); parser.setParameter(0, 0xFF); // No chained response parser.setParameter(1, 0); // Offset to chained response parser.setParameter(2, SMBSrvSession.DefaultBufferSize); parser.setParameter(3, SMBSrvSession.NTMaxMultiplexed); parser.setParameter(4, 0); // virtual circuit number parser.setParameterLong(5, 0); // session key parser.setParameter(7, respLen); // security blob length parser.setParameterLong(8, 0); // reserved parser.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) { // Check for virtual circuit zero, disconnect any other sessions from this client if (vcNum == 0 && hasSessionCleanup()) { // Disconnect other sessions from this client, cleanup any open files/locks/oplocks int discCnt = sess.disconnectClientSessions(); // DEBUG if (discCnt > 0 && Debug.EnableInfo && sess.hasDebug(SMBSrvSession.Dbg.NEGOTIATE)) Debug.println("[SMB] Disconnected " + discCnt + " existing sessions from client, sess=" + sess); } // Clear any stored session setup object for the logon sess.removeAllSetupObjects(client.getProcessId()); // Create a virtual circuit for the new logon VirtualCircuit vc = new VirtualCircuit(vcNum, client); uid = sess.addVirtualCircuit(vc); if (uid == VirtualCircuit.InvalidID) { // DEBUG if (hasDebug() && sess.hasDebug(SMBSrvSession.Dbg.NEGOTIATE)) Debug.println("Failed to allocate UID for virtual circuit, " + vc); // Failed to allocate a UID throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos); } else if (hasDebug() && sess.hasDebug(SMBSrvSession.Dbg.NEGOTIATE)) { // DEBUG Debug.println("Allocated UID=" + uid + " for VC=" + vc); } } // Common session setup response parser.setCommand(parser.getCommand()); parser.setByteCount(0); parser.setTreeId(0); parser.setUserId(uid); // Set the various flags int flags = parser.getFlags(); flags &= ~SMBV1.FLG_CASELESS; parser.setFlags(flags); int flags2 = SMBV1.FLG2_LONGFILENAMES + SMBV1.FLG2_EXTENDEDSECURITY + SMBV1.FLG2_LONGERRORCODE; if (isUni) flags2 += SMBV1.FLG2_UNICODE; parser.setFlags2(flags2); // Pack the security blob int pos = parser.getByteOffset(); buf = parser.getBuffer(); if (respBlob != null) { System.arraycopy(respBlob, 0, buf, pos, respBlob.length); pos += respBlob.length; } // Pack the OS, dialect and domain name strings if (isUni) pos = DataPacker.wordAlign(pos); pos = DataPacker.putString("Java", buf, pos, true, isUni); pos = DataPacker.putString("Java File Server " + sess.getServer().isVersion(), buf, pos, true, isUni); pos = DataPacker.putString(sess.getSMBServer().getSMBConfiguration().getDomainName(), buf, pos, true, isUni); parser.setByteCount(pos - parser.getByteOffset()); } @Override public AuthStatus processSecurityBlob(SMBSrvSession sess, ClientInfo client, SecurityBlob secBlob) throws SMBSrvException { return AuthStatus.DISALLOW; } /** * Process an NTLMSSP security blob * * @param sess SMBSrvSession * @param client ClientInfo * @param secbuf byte[] * @param secpos int * @param seclen int * @param unicode boolean * @return byte[] * @exception SMBSrvException SMB error occurred */ private final byte[] doNtlmsspSessionSetup(SMBSrvSession sess, ClientInfo client, byte[] secbuf, int secpos, int seclen, boolean unicode) throws SMBSrvException { // Determine the NTLmSSP message type NTLMMessage.Type msgType = NTLMMessage.isNTLMType(secbuf, secpos); byte[] respBlob = null; if (msgType == NTLMMessage.Type.Invalid) { // DEBUG if (hasDebug()) { Debug.println("Invalid NTLMSSP token received"); Debug.println(" Token=" + HexDump.hexString(secbuf, secpos, seclen, " ")); } // Return a logon failure status throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } // Check for a type 1 NTLMSSP message else if (msgType == NTLMMessage.Type.Negotiate) { // 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 = (NTLanManAuthContext) getAuthContext(sess); if (ntlmCtx == null) throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); // Build a type2 message to send back to the client, containing the challenge String domain = sess.getSMBServer().getServerName(); List tList = new ArrayList(); tList.add(new StringTargetInfo(TargetInfo.Type.DOMAIN, domain)); tList.add(new StringTargetInfo(TargetInfo.Type.SERVER, sess.getServerName())); tList.add(new StringTargetInfo(TargetInfo.Type.DNS_DOMAIN, domain)); tList.add(new StringTargetInfo(TargetInfo.Type.FULL_DNS, domain)); ntlmFlags = NTLM.FlagChallengeAccept + NTLM.FlagRequestTarget + NTLM.FlagNegotiateNTLM + NTLM.FlagNegotiateUnicode + NTLM.FlagKeyExchange + NTLM.FlagTargetInfo + 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, SetupObjectType.Type2Message); // Set the response blob using the type 2 message respBlob = type2Msg.getBytes(); } else if (msgType == NTLMMessage.Type.Authenticate) { // 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(), SetupObjectType.Type2Message)) { // Clear the setup object sess.removeAllSetupObjects(client.getProcessId()); // Return a logon failure throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } // Determine if the client sent us NTLMv1 or NTLMv2 if (type3Msg.hasFlag(NTLM.Flag128Bit) && type3Msg.hasFlag(NTLM.FlagNegotiateExtSecurity)) { // Debug if (hasDebug()) Debug.println("Received NTLMSSP/NTLMv2, not supported"); // Return a logon failure throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } else { // Looks like an NTLMv1 blob doNTLMv1Logon(sess, client, type3Msg); // Debug if (hasDebug()) Debug.println("Logged on using NTLMSSP/NTLMv1"); } } // Return the response blob return respBlob; } /** * Perform an NTLMv1 logon using the NTLMSSP type3 message * * @param sess SMBSrvSession * @param client ClientInfo * @param type3Msg Type3NTLMMessage * @exception SMBSrvException SMB error occurred */ private final void doNTLMv1Logon(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.removeSetupObject(client.getProcessId(), SetupObjectType.Type2Message); // Get the NTLM logon details String userName = type3Msg.getUserName(); // Check for a null logon if (userName.length() == 0) { // DEBUG if (hasDebug()) Debug.println("Null logon"); // Indicate a null logon in the client information client.setLogonType(ClientInfo.LogonType.Null); return; } // Find the active authentication session details for the server session PassthruDetails passDetails = (PassthruDetails) m_sessions.get(sess.getUniqueId()); if (passDetails != null) { try { // Authenticate the user by passing the hashed password to the authentication server // using the session that has already been setup. AuthenticateSession authSess = passDetails.getAuthenticateSession(); authSess.doSessionSetup(userName, type3Msg.getLMHash(), type3Msg.getNTLMHash()); // Check if the user has been logged on as a guest if (authSess.isGuest()) { // Check if the local server allows guest access if (allowGuest() == true) { // Get a guest authentication token doGuestLogon(client, sess); // Debug if (hasDebug()) Debug.println("Passthru authenticate user=" + userName + ", GUEST"); } } else { // Indicate that the client is logged on client.setLogonType(ClientInfo.LogonType.Normal); // Debug if (hasDebug()) Debug.println("Passthru authenticate user=" + userName + ", FULL"); } // Update the client details client.setDomain(type3Msg.getDomain()); client.setUserName(userName); } catch (Exception ex) { // Debug Debug.println(ex.getMessage()); // Indicate logon failure throw new SMBSrvException(SMBStatus.NTErr, SMBStatus.NTLogonFailure); } finally { // Remove the passthru session from the active list m_sessions.remove(sess.getUniqueId()); // Close the passthru authentication session try { // Close the authentication session AuthenticateSession authSess = passDetails.getAuthenticateSession(); authSess.CloseSession(); // DEBUG if (hasDebug()) Debug.println("Closed auth session, sessId=" + authSess.getSessionId()); } catch (Exception ex) { // Debug Debug.println("Passthru error closing session (auth user) " + ex.getMessage()); } } } else { // DEBUG if (hasDebug()) Debug.println(" No PassthruDetails for " + sess.getUniqueId()); // Indicate logon failure throw new SMBSrvException(SMBStatus.NTErr, SMBStatus.NTLogonFailure); } } /** * Process an SPNEGO security blob * * @param sess SMBSrvSession * @param client ClientInfo * @param secbuf byte[] * @param secpos int * @param seclen int * @param unicode boolean * @return byte[] * @exception SMBSrvException SMB error occurred */ 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(), SetupObjectType.Type2Message)) { // 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 if (hasDebug()) Debug.println("Passthru error on session startup: " + ex.getMessage()); // Return a logon failure status throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } // 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 SPNEGO.Result spnegoSts = SPNEGO.Result.AcceptCompleted; if (sess.hasSetupObject(client.getProcessId(), SetupObjectType.Type2Message)) spnegoSts = SPNEGO.Result.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 if (hasDebug()) Debug.println("Passthru error on session startup: " + ex.getMessage()); // Return a logon failure status throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } // 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 SPNEGO.Result spnegoSts = SPNEGO.Result.AcceptCompleted; if (sess.hasSetupObject(client.getProcessId(), SetupObjectType.Type2Message)) spnegoSts = SPNEGO.Result.AcceptIncomplete; // Package the NTLMSSP response in an SPNEGO response negTarg = new NegTokenTarg(spnegoSts, OID.NTLMSSP, ntlmsspRespBlob); } else { // Debug if (hasDebug()) { Debug.println("No matching authentication OID found"); Debug.println(" " + negToken.toString()); } // No valid authentication mechanism throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } } else { // Unknown SPNEGO token type if (hasDebug()) { Debug.println("Unknown SPNEGO token type"); } // Return a logon failure status throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } // Generate the NegTokenTarg blob byte[] respBlob = null; try { // Generate the response blob respBlob = negTarg.encode(); } catch (IOException ex) { // Debug if (hasDebug()) { Debug.println("Failed to encode NegTokenTarg: " + ex.getMessage()); } // Failed to build response blob throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied); } // Return the SPNEGO response blob return respBlob; } /** * Initialzie the authenticator * * @param config ServerConfiguration * @param params ConfigElement * @exception InvalidConfigurationException Error initializing the authenticator */ public void initialize(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException { // Call the base class super.initialize(config, params); // Check if session cleanup should be disabled, when a session setup request is received // on virtual circuit zero if (params.getChild("disableSessionCleanup") != null) { // Disable session cleanup setSessionCleanup(false); // Debug if (Debug.EnableInfo && hasDebug()) Debug.println("[SMB] Disabled session cleanup (for virtual circuit zero logons)"); } // Check if the passthru session protocol order has been specified ConfigElement protoOrder = params.getChild("protocolOrder"); if (protoOrder != null) { // Parse the protocol order list StringTokenizer tokens = new StringTokenizer(protoOrder.getValue(), ","); int primaryProto = Protocol.None; int secondaryProto = Protocol.None; // There should only be one or two tokens if (tokens.countTokens() > 2) throw new InvalidConfigurationException("Invalid protocol order list, " + protoOrder.getValue()); // Get the primary protocol if (tokens.hasMoreTokens()) { // Parse the primary protocol String primaryStr = tokens.nextToken(); if (primaryStr.equalsIgnoreCase("TCPIP")) primaryProto = Protocol.NativeSMB; else if (primaryStr.equalsIgnoreCase("NetBIOS")) primaryProto = Protocol.TCPNetBIOS; else throw new InvalidConfigurationException("Invalid protocol type, " + primaryStr); // Check if there is a secondary protocol, and validate if (tokens.hasMoreTokens()) { // Parse the secondary protocol String secondaryStr = tokens.nextToken(); if (secondaryStr.equalsIgnoreCase("TCPIP") && primaryProto != Protocol.NativeSMB) secondaryProto = Protocol.NativeSMB; else if (secondaryStr.equalsIgnoreCase("NetBIOS") && primaryProto != Protocol.TCPNetBIOS) secondaryProto = Protocol.TCPNetBIOS; else throw new InvalidConfigurationException("Invalid secondary protocol, " + secondaryStr); } } // Set the protocol order used for passthru authentication sessions AuthSessionFactory.setProtocolOrder(primaryProto, secondaryProto); // DEBUG if (hasDebug()) Debug.println("Protocol order primary=" + Protocol.asString(primaryProto) + ", secondary=" + Protocol.asString(secondaryProto)); } // Check if the offline check interval has been specified ConfigElement checkInterval = params.getChild("offlineCheckInterval"); if (checkInterval != null) { try { // Validate the check interval value int offlineCheck = Integer.parseInt(checkInterval.getValue()); // Range check the value if (offlineCheck < MinCheckInterval || offlineCheck > MaxCheckInterval) throw new InvalidConfigurationException("Invalid offline check interval, valid range is " + MinCheckInterval + " to " + MaxCheckInterval); // Set the offline check interval for offline passthru servers m_passthruServers = new PassthruServers(offlineCheck); // DEBUG if (hasDebug()) Debug.println("Using offline check interval of " + offlineCheck + " seconds"); } catch (NumberFormatException ex) { throw new InvalidConfigurationException("Invalid offline check interval specified"); } } else { // Create the passthru server list with the default offline check interval m_passthruServers = new PassthruServers(); } // Enable passthru servers debugging, if enabled for the authenticator if (hasDebug()) m_passthruServers.setDebug(true); // Check if the session timeout has been specified ConfigElement sessTmoElem = params.getChild("Timeout"); if (sessTmoElem != null) { try { // Validate the session timeout value int sessTmo = Integer.parseInt(sessTmoElem.getValue()); // Range check the timeout if (sessTmo < MinSessionTmo || sessTmo > MaxSessionTmo) throw new InvalidConfigurationException("Invalid session timeout, valid range is " + MinSessionTmo + " to " + MaxSessionTmo); // Set the session timeout for connecting to an authentication server m_passthruServers.setConnectionTimeout(sessTmo); } catch (NumberFormatException ex) { throw new InvalidConfigurationException("Invalid timeout value specified"); } } // Check if a server name has been specified String srvList = null; ConfigElement srvNamesElem = params.getChild("Server"); if (srvNamesElem != null && srvNamesElem.getValue().length() > 0) { // Check if the server name was already set if (srvList != null) throw new InvalidConfigurationException("Set passthru server via local server or specify name"); // Get the passthru authenticator server name srvList = srvNamesElem.getValue(); } // If the passthru server name has been set initialize the passthru connection if (srvList != null) { // Initialize using a list of server names/addresses m_passthruServers.setServerList(srvList); } else { // Get the domain/workgroup name String domainName = null; ConfigElement domNameElem = params.getChild("Domain"); if (domNameElem != null && domNameElem.getValue().length() > 0) { // Check if the authentication server has already been set, ie. server name was also // specified if (srvList != null) throw new InvalidConfigurationException("Specify server or domain name for passthru authentication"); domainName = domNameElem.getValue(); } // If the domain name has been set initialize the passthru connection if (domainName != null) { // Initialize using the domain try { m_passthruServers.setDomain(domainName); } catch (IOException ex) { throw new InvalidConfigurationException("Failed to set domain, " + ex.getMessage()); } } } // Check if we have an authentication server if (m_passthruServers.getTotalServerCount() == 0) throw new InvalidConfigurationException("No valid authentication servers found for passthru"); // Install the SMB server listener so we receive callbacks when sessions are // opened/closed on the SMB server SMBServer smbServer = (SMBServer) config.findServer("SMB"); if (smbServer != null) smbServer.addSessionListener(this); } /** * Return the server capability flags * * @return int */ public int getServerCapabilities() { return Capability.V1Unicode + Capability.V1RemoteAPIs + Capability.V1NTSMBs + Capability.V1NTFind + Capability.V1NTStatus + Capability.V1LargeFiles + Capability.V1LargeRead + Capability.V1LargeWrite + Capability.V1ExtendedSecurity + Capability.V1InfoPassthru + Capability.V1Level2Oplocks; } /** * Close the authenticator, perform cleanup */ public void closeAuthenticator() { // Close the passthru authentication server list if (m_passthruServers != null) m_passthruServers.shutdown(); } /** * SMB server session closed notification * * @param sess SrvSession */ public void sessionClosed(SrvSession sess) { // Check if there is an active session to the authentication server for this local // session PassthruDetails passDetails = (PassthruDetails) m_sessions.get(sess.getUniqueId()); if (passDetails != null) { // Remove the passthru session from the active list m_sessions.remove(sess.getUniqueId()); // Close the passthru authentication session try { // Close the authentication session AuthenticateSession authSess = passDetails.getAuthenticateSession(); authSess.CloseSession(); // DEBUG if (hasDebug()) Debug.println("Closed auth session, sessId=" + authSess.getSessionId()); } catch (Exception ex) { // Debug if (hasDebug()) Debug.println("Passthru error closing session (closed) " + ex.getMessage()); } } } /** * SMB server session created notification * * @param sess SrvSession */ public void sessionCreated(SrvSession sess) { } /** * User successfully logged on notification * * @param sess SrvSession */ public void sessionLoggedOn(SrvSession sess) { // Check if there is an active session to the authentication server for this local // session PassthruDetails passDetails = (PassthruDetails) m_sessions.get(sess.getUniqueId()); if (passDetails != null) { // Remove the passthru session from the active list m_sessions.remove(sess.getUniqueId()); // Close the passthru authentication session try { // Close the authentication session AuthenticateSession authSess = passDetails.getAuthenticateSession(); authSess.CloseSession(); // DEBUG if (hasDebug()) Debug.println("Closed auth session, sessId=" + authSess.getSessionId()); } catch (Exception ex) { // Debug if (hasDebug()) Debug.println("Passthru error closing session (logon) " + ex.getMessage()); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy