예제 #1
0
        /// <summary>
        /// accept the authenticate packet, if failed, throw exception.
        /// </summary>
        /// <param name="authenticatePacket">authenciate packet</param>
        /// <exception cref="InvalidOperationException">INVALID message error</exception>
        /// <exception cref="InvalidOperationException">mic of authenticate packet is invalid</exception>
        private void AcceptAuthenticatePacket(NlmpAuthenticatePacket authenticatePacket)
        {
            // save the authenticate to valid the mic.
            this.authenticate = authenticatePacket;

            // retrieve the client authenticate information
            ClientAuthenticateInfomation authenticateInformation =
                RetrieveClientAuthenticateInformation(authenticatePacket);

            // update the version of context
            this.version = authenticateInformation.Version;

            if (authenticateInformation.ClientTime != 0)
            {
                this.systemTime = authenticateInformation.ClientTime;
            }


            byte[] exportedSessionKey = null;
            if (this.nlmpServer.Context.ClientCredential == null)
            {
                if (verifyAuthenticatePacketInDc == null)
                {
                    throw new InvalidOperationException(
                              "In domain environment, the delegate method " +
                              "VerifyAuthenticatePacketInDcMethod should not be null");
                }

                // in domain environment, the authentication is hosted in Active Directory,
                // the response pair MUST be sent to DC to verify [MS-APDS] section 3.1.5
                // DC will return the authentication result and the session key
                DcValidationInfo dcValidationInfo = verifyAuthenticatePacketInDc(
                    authenticatePacket,
                    this.challenge.Payload.ServerChallenge);

                if (dcValidationInfo.status != NtStatus.STATUS_SUCCESS)
                {
                    throw new InvalidOperationException(
                              string.Format(
                                  "verify authentication packet in DC failed, NtStatus value ={0}",
                                  dcValidationInfo.status.ToString()));
                }
                exportedSessionKey = dcValidationInfo.sessionKey;
            }
            else
            {
                // in workgroup environment. the authentication is hosted locally.
                authenticateInformation = VerifyAuthenticatePacketLocally(
                    authenticatePacket,
                    authenticateInformation,
                    out exportedSessionKey);
            }

            // save keys
            this.nlmpServer.Context.ExportedSessionKey = exportedSessionKey;

            // initialize keys and handles
            InitializeKeys(exportedSessionKey);
        }
예제 #2
0
        /// <summary>
        /// get the security token,using the token from server
        /// </summary>
        /// <param name="serverToken">the token from server challenge</param>
        /// <returns>the security token</returns>
        private byte[] GetSecurityToken(
            byte[] serverToken
            )
        {
            if (this.client == null)
            {
                throw new InvalidOperationException("The client is null! You must initialize this field first!");
            }

            // the challenge packet from server
            NlmpChallengePacket challenge = new NlmpChallengePacket(serverToken);

            NegotiateTypes flags = InitializeNegotiateFlags(challenge.Payload.NegotiateFlags);

            // the target info
            ICollection <AV_PAIR> targetInfo = InitializeTargetInfo(challenge.Payload.TargetInfo);

            // responseKeyLM
            byte[] responseKeyLM;

            // lmChallengeResponse
            byte[] lmChallengeResponse;

            // ntChallengeResponse
            byte[] ntChallengeResponse;

            // initialize the challenge response
            InitializeChallengeResponse(
                flags, challenge, targetInfo, out responseKeyLM, out lmChallengeResponse, out ntChallengeResponse);

            // encryptedRandomSessionKey
            byte[] encryptedRandomSessionKey = null;

            // exportedSessionKey
            byte[] exportedSessionKey = null;

            // initialize keys
            InitializeKeys(
                flags, challenge, responseKeyLM, lmChallengeResponse, out encryptedRandomSessionKey,
                out exportedSessionKey);

            // save the exported sessionkey
            this.client.Context.ExportedSessionKey = exportedSessionKey;

            // create challenge packet
            NlmpAuthenticatePacket packet = this.client.CreateAuthenticatePacket(
                flags, NlmpUtility.GetVersion(), lmChallengeResponse, ntChallengeResponse,
                this.currentActiveCredential.DomainName, this.currentActiveCredential.AccountName,
                Environment.MachineName, encryptedRandomSessionKey);

            // initialize the mic of challenge packet
            InitializeChallengeMIC(exportedSessionKey, targetInfo, packet, challenge);

            return(packet.ToBytes());
        }
예제 #3
0
 /// <summary>
 /// valid the authenticate message
 /// </summary>
 /// <param name="authenticate">
 /// the authenticate packet of client, contains the challenge response calculated by client
 /// </param>
 /// <param name="expectedNtChallengeResponse">the nt challenge response calculated by server</param>
 /// <param name="expectedLmChallengeResponse">the lm challenge response calculated by server</param>
 private static void ValidAuthenticateMessage(
     NlmpAuthenticatePacket authenticate,
     byte[] expectedNtChallengeResponse, byte[] expectedLmChallengeResponse)
 {
     if (authenticate.Payload.NtChallengeResponseFields.Len == 0 ||
         !ArrayUtility.CompareArrays <byte>(expectedNtChallengeResponse,
                                            authenticate.Payload.NtChallengeResponse))
     {
         if (authenticate.Payload.LmChallengeResponseFields.Len == 0 ||
             !ArrayUtility.CompareArrays <byte>(expectedLmChallengeResponse,
                                                authenticate.Payload.LmChallengeResponse))
         {
             throw new InvalidOperationException("INVALID message error");
         }
     }
 }
예제 #4
0
        /// <summary>
        /// verify the implicit ntlm session security token.
        /// this api is invoked by protocol need the implicit Ntlm authenticate, such as CifsSdk and the SmbSdk with
        /// none extended session security.
        /// </summary>
        /// <param name="accountName">the user name which determined by the security context of the user initiating the
        /// connection to share</param>
        /// <param name="domainName">the Primary Domain of the user</param>
        /// <param name="serverTime">the time of the server</param>
        /// <param name="caseInsensitivePassword">the NtChallengeResponse</param>
        /// <param name="caseSensitivePassword">the LmChallengeResponse</param>
        /// <returns>success or fail</returns>
        public bool VerifySecurityToken(
            //string workstationName,
            string accountName,
            string domainName,
            ulong serverTime,
            byte[] caseInsensitivePassword,
            byte[] caseSensitivePassword)
        {
            if (string.IsNullOrEmpty(accountName))
            {
                return(false);
            }

            this.systemTime = serverTime;
            NlmpAuthenticatePacket authenticatePacket = new NlmpAuthenticatePacket();

            authenticatePacket.SetNtChallengeResponse(caseSensitivePassword);
            authenticatePacket.SetLmChallengeResponse(caseInsensitivePassword);
            authenticatePacket.SetDomainName(domainName);
            authenticatePacket.SetUserName(accountName);
            authenticatePacket.SetWorkstation(string.Empty);
            authenticatePacket.SetEncryptedRandomSessionKey(new byte[0]);

            try
            {
                this.AcceptAuthenticatePacket(authenticatePacket);
            }
            catch (InvalidOperationException)
            {
                return(false);
            }
            catch (ArgumentException) // caseSensitivePassword or caseInsensitivePassword is not long enough
            {
                return(false);
            }
            return(true);
        }
예제 #5
0
        /// <summary>
        /// initialize the mic of challenge packet
        /// </summary>
        /// <param name="exportedSessionKey">the exported session key</param>
        /// <param name="targetInfo">the target info contains av pairs.</param>
        /// <param name="authenticatePacket">the authenticate packet</param>
        /// <param name="challengePacket">the challenge packet</param>
        private void InitializeChallengeMIC(
            byte[] exportedSessionKey,
            ICollection <AV_PAIR> targetInfo,
            NlmpAuthenticatePacket authenticatePacket,
            NlmpChallengePacket challengePacket
            )
        {
            if (NlmpUtility.AvPairContains(targetInfo, AV_PAIR_IDs.MsvAvTimestamp))
            {
                // update mic with security algorithm
                byte[] mic = null;
                // if connectionless, this.negotiate is null.
                mic = NlmpUtility.GetMic(exportedSessionKey, this.negotiate, challengePacket, authenticatePacket);

                // get payload of packet
                AUTHENTICATE_MESSAGE payload = authenticatePacket.Payload;

                // update mic to payload
                payload.MIC = mic;

                // update the meaningful payload to packet
                authenticatePacket.Payload = payload;
            }
        }
        /// <summary>
        /// copy constructor.
        /// </summary>
        /// <param name="stackPacket">the source packet</param>
        /// <exception cref="ArgumentNullException">the stackPacket.payload.MIC must not be null</exception>
        /// <exception cref="ArgumentException">the stackPacket.payload.MIC.Length is wrong</exception>
        public NlmpAuthenticatePacket(
            NlmpAuthenticatePacket stackPacket
            )
            : base(stackPacket)
        {
            this.payload = stackPacket.payload;

            if (stackPacket.payload.MIC == null)
            {
                throw new ArgumentNullException("stackPacket", "the stackPacket.payload.MIC must not be null");
            }

            if (stackPacket.payload.MIC.Length != NlmpUtility.AUTHENTICATE_MESSAGE_MIC_CONST_SIZE)
            {
                throw new ArgumentException(
                    string.Format("the stackPacket.payload.MIC.Length must be {0}, actually {1}",
                    NlmpUtility.AUTHENTICATE_MESSAGE_MIC_CONST_SIZE,
                    stackPacket.payload.MIC.Length),
                    "stackPacket");
            }

            this.payload.MIC = new byte[stackPacket.payload.MIC.Length];
            Array.Copy(stackPacket.payload.MIC, this.payload.MIC, stackPacket.payload.MIC.Length);

            if (stackPacket.payload.DomainName != null)
            {
                this.payload.DomainName = new byte[stackPacket.payload.DomainName.Length];
                Array.Copy(
                    stackPacket.payload.DomainName,
                    this.payload.DomainName, stackPacket.payload.DomainName.Length);
            }

            if (stackPacket.payload.UserName != null)
            {
                this.payload.UserName = new byte[stackPacket.payload.UserName.Length];
                Array.Copy(
                    stackPacket.payload.UserName, this.payload.UserName, stackPacket.payload.UserName.Length);
            }

            if (stackPacket.payload.Workstation != null)
            {
                this.payload.Workstation = new byte[stackPacket.payload.Workstation.Length];
                Array.Copy(
                    stackPacket.payload.Workstation,
                    this.payload.Workstation, stackPacket.payload.Workstation.Length);
            }

            if (stackPacket.payload.LmChallengeResponse != null)
            {
                this.payload.LmChallengeResponse = new byte[stackPacket.payload.LmChallengeResponse.Length];
                Array.Copy(
                    stackPacket.payload.LmChallengeResponse,
                    this.payload.LmChallengeResponse, stackPacket.payload.LmChallengeResponse.Length);
            }

            if (stackPacket.payload.NtChallengeResponse != null)
            {
                this.payload.NtChallengeResponse = new byte[stackPacket.payload.NtChallengeResponse.Length];
                Array.Copy(
                    stackPacket.payload.NtChallengeResponse,
                    this.payload.NtChallengeResponse, stackPacket.payload.NtChallengeResponse.Length);
            }

            if (stackPacket.payload.EncryptedRandomSessionKey != null)
            {
                this.payload.EncryptedRandomSessionKey =
                    new byte[stackPacket.payload.EncryptedRandomSessionKey.Length];
                Array.Copy(
                    stackPacket.payload.EncryptedRandomSessionKey,
                    this.payload.EncryptedRandomSessionKey, stackPacket.payload.EncryptedRandomSessionKey.Length);
            }
        }
        /// <summary>
        /// this function create an authenticate packet.
        /// after client received the challenge packet from server, client create an authenticate packet to server.
        /// the authenticate packet contains the authentication information of user that is generated by the credential
        /// of user.
        /// this function does not set the mic field of packet. it must be set manually if need.
        /// </summary>
        /// <param name="negotiateFlags">this flags indicates the capabilities that client supports.</param>
        /// <param name="version">
        /// This structure is used for debugging purposes only. In normal (non-debugging) protocol messages, it is 
        /// ignored and does not affect the NTLM message processing.
        /// </param>
        /// <param name="lmChallengeResponse">
        /// An LM_RESPONSE or LMv2_RESPONSE structure that contains the computed LM response to the challenge. If 
        /// NTLM v2 authentication is configured, LmChallengeResponse MUST be an LMv2_RESPONSE structure. Otherwise, 
        /// it MUST be an LM_RESPONSE structure.
        /// </param>
        /// <param name="ntChallengeResponse">
        /// An NTLM_RESPONSE or NTLMv2_RESPONSE structure that contains the computed NT response to the challenge. 
        /// If NTLM v2 authentication is configured, NtChallengeResponse MUST be an NTLMv2_RESPONSE. Otherwise, it 
        /// MUST be an NTLM_RESPONSE structure.
        /// </param>
        /// <param name="domainName">
        /// The domain or computer name hosting the user account. DomainName MUST be encoded in the negotiated 
        /// character set. This param can not be null.
        /// </param>
        /// <param name="userName">
        /// The name of the user to be authenticated. UserName MUST be encoded in the negotiated character set. This 
        /// param can not be null.
        /// </param>
        /// <param name="workstation">
        /// The name of the computer to which the user is logged on. Workstation MUST be encoded in the negotiated 
        /// character set. This param can not be null.
        /// </param>
        /// <param name="encryptedRandomSessionKey">
        /// The client's encrypted random session key. This param can be null.
        /// </param>
        /// <returns>the authenticate packet</returns>
        /// <exception cref="System.ArgumentNullException">
        /// when LM is using, the ntChallengeResponse should be null!
        /// </exception>
        public NlmpAuthenticatePacket CreateAuthenticatePacket(
            NegotiateTypes negotiateFlags,
            VERSION version,
            byte[] lmChallengeResponse,
            byte[] ntChallengeResponse,
            string domainName,
            string userName,
            string workstation,
            byte[] encryptedRandomSessionKey
            )
        {
            #region Parameter check

            if (domainName == null)
            {
                throw new ArgumentNullException("domainName");
            }

            if (userName == null)
            {
                throw new ArgumentNullException("userName");
            }

            if (workstation == null)
            {
                throw new ArgumentNullException("workstation");
            }

            if (NlmpUtility.IsLm(negotiateFlags) && ntChallengeResponse != null)
            {
                throw new ArgumentException(
                    "when LM is using, the ntChallengeResponse should be null!", "ntChallengeResponse");
            }

            #endregion

            NlmpAuthenticatePacket packet = new NlmpAuthenticatePacket();

            // update flags
            packet.SetNegotiateFlags(negotiateFlags);

            // if version required, update version
            if (NlmpUtility.IsVersionRequired(negotiateFlags))
            {
                packet.SetVersion(version);
            }

            // update domain name
            packet.SetDomainName(domainName);

            // update user name
            packet.SetUserName(userName);

            // if version required, update workstation
            if (NlmpUtility.IsVersionRequired(negotiateFlags))
            {
                packet.SetWorkstation(workstation);
            }

            // update lmChallengeResponse
            packet.SetLmChallengeResponse(lmChallengeResponse);

            // update ntChallengeResponse
            packet.SetNtChallengeResponse(ntChallengeResponse);

            // update encryptedRandomSessionKey
            packet.SetEncryptedRandomSessionKey(encryptedRandomSessionKey);

            return packet;
        }
예제 #8
0
        /// <summary>
        /// retrieve the domain name from client. client encode the domain name in the authenticate packet.
        /// </summary>
        /// <param name="authenticatePacket">the authenticate packet contains the domain name</param>
        /// <returns>the authentication information of client</returns>
        private ClientAuthenticateInfomation RetrieveClientAuthenticateInformation(
            NlmpAuthenticatePacket authenticatePacket)
        {
            ClientAuthenticateInfomation authenticateInformation = new ClientAuthenticateInfomation();

            // retrieve the version of client
            if (authenticatePacket.Payload.NtChallengeResponseFields.Len == NTLM_V1_NT_CHALLENGE_RESPONSE_LENGTH)
            {
                authenticateInformation.Version = NlmpVersion.v1;
            }
            else
            {
                authenticateInformation.Version = NlmpVersion.v2;
            }

            // retrieve the client challenge
            if (authenticateInformation.Version == NlmpVersion.v1)
            {
                authenticateInformation.ClientChallenge = BitConverter.ToUInt64(ArrayUtility.SubArray <byte>(
                                                                                    authenticatePacket.Payload.LmChallengeResponse, 0, TIME_CLIENT_CHALLENGE_LENGTH), 0);
            }
            else
            {
                authenticateInformation.ClientChallenge = BitConverter.ToUInt64(
                    ArrayUtility.SubArray <byte>(authenticatePacket.Payload.NtChallengeResponse,
                                                 NTLM_V2_CLIENT_CHALLENGE_OFFSET_IN_NT_CHALLENGE_RESPONSE, TIME_CLIENT_CHALLENGE_LENGTH), 0);
            }

            // retrieve the domain name of client
            if (NlmpUtility.IsUnicode(authenticatePacket.Payload.NegotiateFlags))
            {
                authenticateInformation.DomainName = Encoding.Unicode.GetString(authenticatePacket.Payload.DomainName);
            }
            else
            {
                authenticateInformation.DomainName = Encoding.ASCII.GetString(authenticatePacket.Payload.DomainName);
            }

            // retrieve the user name of client
            if (NlmpUtility.IsUnicode(authenticatePacket.Payload.NegotiateFlags))
            {
                authenticateInformation.UserName = Encoding.Unicode.GetString(authenticatePacket.Payload.UserName);
            }
            else
            {
                authenticateInformation.UserName = Encoding.ASCII.GetString(authenticatePacket.Payload.UserName);
            }

            // retrieve the server name of client
            if (authenticateInformation.Version == NlmpVersion.v2)
            {
                authenticateInformation.ServerName =
                    ArrayUtility.SubArray <byte>(authenticatePacket.Payload.NtChallengeResponse,
                                                 NTLM_V2_SERVER_NAME_OFFSET_IN_NT_CHALLENGE_RESPONSE,
                                                 authenticatePacket.Payload.NtChallengeResponseFields.Len -
                                                 NTLM_V2_SERVER_NAME_OFFSET_IN_NT_CHALLENGE_RESPONSE -
                                                 NTLM_V2_SERVER_NAME_RESERVED_LENGTH_IN_NT_CHALLENGE_RESPONSE);
            }

            // retrieve the time of client
            ICollection <AV_PAIR> targetInfo =
                NlmpUtility.BytesGetAvPairCollection(this.challenge.Payload.TargetInfo);

            // retrieve the time
            authenticateInformation.ClientTime = NlmpUtility.GetTime(targetInfo);

            // if server did not response the timestamp, use the client time stamp
            if (!NlmpUtility.AvPairContains(targetInfo, AV_PAIR_IDs.MsvAvTimestamp) &&
                authenticateInformation.Version == NlmpVersion.v2)
            {
                authenticateInformation.ClientTime = BitConverter.ToUInt64(
                    ArrayUtility.SubArray <byte>(authenticatePacket.Payload.NtChallengeResponse,
                                                 NTLM_V2_TIME_STAMP_OFFSET_IN_NT_CHALLENGE_RESPONSE, TIME_STAMP_LENGTH), 0);
            }

            return(authenticateInformation);
        }
        /// <summary>
        /// verify the implicit ntlm session security token.
        /// this api is invoked by protocol need the implicit Ntlm authenticate, such as CifsSdk and the SmbSdk with
        /// none extended session security.
        /// </summary>
        /// <param name="accountName">the user name which determined by the security context of the user initiating the
        /// connection to share</param>
        /// <param name="domainName">the Primary Domain of the user</param>
        /// <param name="serverTime">the time of the server</param>
        /// <param name="caseInsensitivePassword">the NtChallengeResponse</param>
        /// <param name="caseSensitivePassword">the LmChallengeResponse</param>
        /// <returns>success or fail</returns>
        public bool VerifySecurityToken(
            //string workstationName,
            string accountName,
            string domainName,
            ulong serverTime,
            byte[] caseInsensitivePassword,
            byte[] caseSensitivePassword)
        {
            if (string.IsNullOrEmpty(accountName))
            {
                return false;
            }

            this.systemTime = serverTime;
            NlmpAuthenticatePacket authenticatePacket = new NlmpAuthenticatePacket();
            authenticatePacket.SetNtChallengeResponse(caseSensitivePassword);
            authenticatePacket.SetLmChallengeResponse(caseInsensitivePassword);
            authenticatePacket.SetDomainName(domainName);
            authenticatePacket.SetUserName(accountName);
            authenticatePacket.SetWorkstation(string.Empty);
            authenticatePacket.SetEncryptedRandomSessionKey(new byte[0]);

            try
            {
                this.AcceptAuthenticatePacket(authenticatePacket);
            }
            catch (InvalidOperationException)
            {
                return false;
            }
            catch (ArgumentException) // caseSensitivePassword or caseInsensitivePassword is not long enough
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// Verify the authenticate packet locally
        /// </summary>
        /// <param name="authenticatePacket">actual authenticate packet</param>
        /// <param name="authenticateInformation">expected authenticate information</param>
        /// <param name="exportedSessionKey">exported session key</param>
        /// <returns></returns>
        private ClientAuthenticateInfomation VerifyAuthenticatePacketLocally(
            NlmpAuthenticatePacket authenticatePacket,
            ClientAuthenticateInfomation authenticateInformation,
            out byte[] exportedSessionKey)
        {
            // valid user name
            if (authenticateInformation.UserName.ToUpper() != this.nlmpServer.Context.ClientCredential.AccountName.ToUpper())
            {
                throw new InvalidOperationException(
                    "the user name is invalid!"
                    + " the user name retrieved form authenticate packet is not equal to the context.");
            }

            // calc the basekeys
            byte[] responseKeyLm;
            byte[] expectedNtChallengeResponse;
            byte[] expectedLmChallengeResponse;
            byte[] sessionBaseKey;
            byte[] keyExchangeKey;
            CalculateBaseKeys(
                authenticateInformation.ClientChallenge,
                this.systemTime,
                authenticateInformation.ServerName,
                authenticateInformation.DomainName,
                authenticateInformation.UserName,
                this.nlmpServer.Context.ClientCredential.Password,
                out responseKeyLm, out expectedNtChallengeResponse, out expectedLmChallengeResponse,
                out sessionBaseKey, out keyExchangeKey);

            // valid message
            ValidAuthenticateMessage(authenticatePacket, expectedNtChallengeResponse, expectedLmChallengeResponse);

            // generate keys.
            if (NlmpUtility.IsKeyExch(this.nlmpServer.Context.NegFlg))
            {
                exportedSessionKey = NlmpUtility.RC4(
                    keyExchangeKey, authenticatePacket.Payload.EncryptedRandomSessionKey);
            }
            else
            {
                exportedSessionKey = keyExchangeKey;
            }

            // validate mic
            byte[] messageMic = authenticatePacket.Payload.MIC;
            byte[] zeroMic = new byte[16];
            if (messageMic != null && !ArrayUtility.CompareArrays<byte>(messageMic, zeroMic))
            {
                AUTHENTICATE_MESSAGE payload = authenticatePacket.Payload;
                payload.MIC = zeroMic;
                authenticatePacket.Payload = payload;

                byte[] mic = NlmpUtility.GetMic(exportedSessionKey, this.negotiate, this.challenge, this.authenticate);

                if (!ArrayUtility.CompareArrays<byte>(messageMic, mic))
                {
                    throw new InvalidOperationException("mic of authenticate packet is invalid");
                }
            }
            return authenticateInformation;
        }
        /// <summary>
        /// Construct Nlmp pass-through network logon information structure 
        /// from client NTLM authenticate response message
        /// </summary>
        /// <param name="parameterControl">
        /// A set of bit flags that contain information pertaining to the logon validation processing.
        /// </param>
        /// <param name="nlmpAuthenticatePacket">
        /// nlmp authenticate response packet sent from client machine
        /// </param>
        /// <param name="lmChallenge">
        /// nlmp challenge sent from server to client
        /// </param>
        /// <returns>
        /// Nlmp pass-through network logon information
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when nlmpAuthenticatePacket or lmChallenge is null.
        /// </exception>        
        /// <exception cref="ArgumentException">
        /// Thrown when the length of lmChallenge is not equal to 8 bytes
        /// </exception>
        public static _NETLOGON_LEVEL CreateNlmpNetworkLogonInfo(
            NrpcParameterControlFlags parameterControl,
            NlmpAuthenticatePacket nlmpAuthenticatePacket,
            byte[] lmChallenge
            )
        {
            if (nlmpAuthenticatePacket == null)
            {
                throw new ArgumentNullException("nlmpAuthenticatePacket");
            }

            if (lmChallenge == null)
            {
                throw new ArgumentNullException("lmChallenge");
            }

            // ServerChallenge (8 bytes): A 64-bit value that contains the NTLM challenge.
            // The challenge is a 64-bit nonce. The processing of the
            // ServerChallenge is specified in sections 3.1.5 and 3.2.5.
            if (lmChallenge.Length != NLMP_SERVER_CHALLENGE_LENGTH)
            {
                throw new ArgumentException(
                    "the length of lmChallenge should be 8 bytes",
                    "lmChallenge");
            }

            string domainName;
            string userName;
            string logonWorkStation;

            _NETLOGON_LEVEL netLogonLevel = new _NETLOGON_LEVEL();
            if (nlmpAuthenticatePacket.Payload.DomainName != null)
            {
                domainName = Encoding.Unicode.GetString(nlmpAuthenticatePacket.Payload.DomainName);
            }
            else
            {
                throw new ArgumentException(
                    "DomainName field should not be null",
                    "nlmpAuthenticatePacket");
            }

            if (nlmpAuthenticatePacket.Payload.UserName != null)
            {
                userName = Encoding.Unicode.GetString(nlmpAuthenticatePacket.Payload.UserName);
            }
            else
            {
                throw new ArgumentException(
                    "UserName field should not be null",
                    "nlmpAuthenticatePacket");
            }

            if (nlmpAuthenticatePacket.Payload.Workstation != null)
            {
                logonWorkStation = Encoding.Unicode.GetString(nlmpAuthenticatePacket.Payload.Workstation);
            }
            else
            {
                throw new ArgumentException(
                    "WorkStation field should not be null",
                    "nlmpAuthenticatePacket");
            }

            //Identity: A NETLOGON_LOGON_IDENTITY_INFO structure, as specified in section MS-NRPC 2.2.1.4.15,
            //that contains information about the logon identity.
            _NETLOGON_LOGON_IDENTITY_INFO identityInfo = NrpcUtility.CreateNetlogonIdentityInfo(
                parameterControl,
                domainName,
                userName,
                logonWorkStation);

            netLogonLevel.LogonNetwork = new _NETLOGON_NETWORK_INFO[1];
            netLogonLevel.LogonNetwork[0].Identity = identityInfo;
            netLogonLevel.LogonNetwork[0].LmChallenge = new LM_CHALLENGE();
            netLogonLevel.LogonNetwork[0].LmChallenge.data = lmChallenge;
            netLogonLevel.LogonNetwork[0].LmChallengeResponse =
                NrpcUtility.CreateString(nlmpAuthenticatePacket.Payload.LmChallengeResponse);
            netLogonLevel.LogonNetwork[0].NtChallengeResponse =
                NrpcUtility.CreateString(nlmpAuthenticatePacket.Payload.NtChallengeResponse);

            return netLogonLevel;
        }
        /// <summary>
        /// retrieve the domain name from client. client encode the domain name in the authenticate packet.
        /// </summary>
        /// <param name="authenticatePacket">the authenticate packet contains the domain name</param>
        /// <returns>the authentication information of client</returns>
        private ClientAuthenticateInfomation RetrieveClientAuthenticateInformation(
            NlmpAuthenticatePacket authenticatePacket)
        {
            ClientAuthenticateInfomation authenticateInformation = new ClientAuthenticateInfomation();

            // retrieve the version of client
            if (authenticatePacket.Payload.NtChallengeResponseFields.Len == NTLM_V1_NT_CHALLENGE_RESPONSE_LENGTH)
            {
                authenticateInformation.Version = NlmpVersion.v1;
            }
            else
            {
                authenticateInformation.Version = NlmpVersion.v2;
            }

            // retrieve the client challenge
            if (authenticateInformation.Version == NlmpVersion.v1)
            {
                authenticateInformation.ClientChallenge = BitConverter.ToUInt64(ArrayUtility.SubArray<byte>(
                    authenticatePacket.Payload.LmChallengeResponse, 0, TIME_CLIENT_CHALLENGE_LENGTH), 0);
            }
            else
            {
                authenticateInformation.ClientChallenge = BitConverter.ToUInt64(
                    ArrayUtility.SubArray<byte>(authenticatePacket.Payload.NtChallengeResponse,
                    NTLM_V2_CLIENT_CHALLENGE_OFFSET_IN_NT_CHALLENGE_RESPONSE, TIME_CLIENT_CHALLENGE_LENGTH), 0);
            }

            // retrieve the domain name of client
            if (NlmpUtility.IsUnicode(authenticatePacket.Payload.NegotiateFlags))
            {
                authenticateInformation.DomainName = Encoding.Unicode.GetString(authenticatePacket.Payload.DomainName);
            }
            else
            {
                authenticateInformation.DomainName = Encoding.ASCII.GetString(authenticatePacket.Payload.DomainName);
            }

            // retrieve the user name of client
            if (NlmpUtility.IsUnicode(authenticatePacket.Payload.NegotiateFlags))
            {
                authenticateInformation.UserName = Encoding.Unicode.GetString(authenticatePacket.Payload.UserName);
            }
            else
            {
                authenticateInformation.UserName = Encoding.ASCII.GetString(authenticatePacket.Payload.UserName);
            }

            // retrieve the server name of client
            if (authenticateInformation.Version == NlmpVersion.v2)
            {
                authenticateInformation.ServerName =
                    ArrayUtility.SubArray<byte>(authenticatePacket.Payload.NtChallengeResponse,
                     NTLM_V2_SERVER_NAME_OFFSET_IN_NT_CHALLENGE_RESPONSE,
                     authenticatePacket.Payload.NtChallengeResponseFields.Len -
                     NTLM_V2_SERVER_NAME_OFFSET_IN_NT_CHALLENGE_RESPONSE -
                     NTLM_V2_SERVER_NAME_RESERVED_LENGTH_IN_NT_CHALLENGE_RESPONSE);
            }

            // retrieve the time of client
            ICollection<AV_PAIR> targetInfo =
                NlmpUtility.BytesGetAvPairCollection(this.challenge.Payload.TargetInfo);

            // retrieve the time
            authenticateInformation.ClientTime = NlmpUtility.GetTime(targetInfo);

            // if server did not response the timestamp, use the client time stamp
            if (!NlmpUtility.AvPairContains(targetInfo, AV_PAIR_IDs.MsvAvTimestamp)
                && authenticateInformation.Version == NlmpVersion.v2)
            {
                authenticateInformation.ClientTime = BitConverter.ToUInt64(
                    ArrayUtility.SubArray<byte>(authenticatePacket.Payload.NtChallengeResponse,
                    NTLM_V2_TIME_STAMP_OFFSET_IN_NT_CHALLENGE_RESPONSE, TIME_STAMP_LENGTH), 0);
            }

            return authenticateInformation;
        }
        /// <summary>
        /// accept the authenticate packet, if failed, throw exception.
        /// </summary>
        /// <param name="authenticatePacket">authenciate packet</param>
        /// <exception cref="InvalidOperationException">INVALID message error</exception>
        /// <exception cref="InvalidOperationException">mic of authenticate packet is invalid</exception>
        private void AcceptAuthenticatePacket(NlmpAuthenticatePacket authenticatePacket)
        {
            // save the authenticate to valid the mic.
            this.authenticate = authenticatePacket;

            // retrieve the client authenticate information
            ClientAuthenticateInfomation authenticateInformation =
                RetrieveClientAuthenticateInformation(authenticatePacket);

            // update the version of context
            this.version = authenticateInformation.Version;

            if (authenticateInformation.ClientTime != 0)
            {
                this.systemTime = authenticateInformation.ClientTime;
            }

            byte[] exportedSessionKey = null;
            if (this.nlmpServer.Context.ClientCredential == null)
            {
                if (verifyAuthenticatePacketInDc == null)
                {
                    throw new InvalidOperationException(
                        "In domain environment, the delegate method " +
                        "VerifyAuthenticatePacketInDcMethod should not be null");
                }

                // in domain environment, the authentication is hosted in Active Directory,
                // the response pair MUST be sent to DC to verify [MS-APDS] section 3.1.5
                // DC will return the authentication result and the session key
                DcValidationInfo dcValidationInfo = verifyAuthenticatePacketInDc(
                    authenticatePacket,
                    this.challenge.Payload.ServerChallenge);

                if (dcValidationInfo.status != NtStatus.STATUS_SUCCESS)
                {
                    throw new InvalidOperationException(
                        string.Format(
                        "verify authentication packet in DC failed, NtStatus value ={0}",
                        dcValidationInfo.status.ToString()));
                }
                exportedSessionKey = dcValidationInfo.sessionKey;
            }
            else
            {
                // in workgroup environment. the authentication is hosted locally.
                authenticateInformation = VerifyAuthenticatePacketLocally(
                    authenticatePacket,
                    authenticateInformation,
                    out exportedSessionKey);
            }

            // save keys
            this.nlmpServer.Context.ExportedSessionKey = exportedSessionKey;

            // initialize keys and handles
            InitializeKeys(exportedSessionKey);
        }
 /// <summary>
 /// valid the authenticate message
 /// </summary>
 /// <param name="authenticate">
 /// the authenticate packet of client, contains the challenge response calculated by client
 /// </param>
 /// <param name="expectedNtChallengeResponse">the nt challenge response calculated by server</param>
 /// <param name="expectedLmChallengeResponse">the lm challenge response calculated by server</param>
 private static void ValidAuthenticateMessage(
     NlmpAuthenticatePacket authenticate,
     byte[] expectedNtChallengeResponse, byte[] expectedLmChallengeResponse)
 {
     if (authenticate.Payload.NtChallengeResponseFields.Len == 0
         || !ArrayUtility.CompareArrays<byte>(expectedNtChallengeResponse,
         authenticate.Payload.NtChallengeResponse))
     {
         if (authenticate.Payload.LmChallengeResponseFields.Len == 0
             || !ArrayUtility.CompareArrays<byte>(expectedLmChallengeResponse,
             authenticate.Payload.LmChallengeResponse))
         {
             throw new InvalidOperationException("INVALID message error");
         }
     }
 }
        /// <summary>
        /// this function create an authenticate packet.
        /// after client received the challenge packet from server, client create an authenticate packet to server.
        /// the authenticate packet contains the authentication information of user that is generated by the credential
        /// of user.
        /// this function does not set the mic field of packet. it must be set manually if need.
        /// </summary>
        /// <param name="negotiateFlags">this flags indicates the capabilities that client supports.</param>
        /// <param name="version">
        /// This structure is used for debugging purposes only. In normal (non-debugging) protocol messages, it is
        /// ignored and does not affect the NTLM message processing.
        /// </param>
        /// <param name="lmChallengeResponse">
        /// An LM_RESPONSE or LMv2_RESPONSE structure that contains the computed LM response to the challenge. If
        /// NTLM v2 authentication is configured, LmChallengeResponse MUST be an LMv2_RESPONSE structure. Otherwise,
        /// it MUST be an LM_RESPONSE structure.
        /// </param>
        /// <param name="ntChallengeResponse">
        /// An NTLM_RESPONSE or NTLMv2_RESPONSE structure that contains the computed NT response to the challenge.
        /// If NTLM v2 authentication is configured, NtChallengeResponse MUST be an NTLMv2_RESPONSE. Otherwise, it
        /// MUST be an NTLM_RESPONSE structure.
        /// </param>
        /// <param name="domainName">
        /// The domain or computer name hosting the user account. DomainName MUST be encoded in the negotiated
        /// character set. This param can not be null.
        /// </param>
        /// <param name="userName">
        /// The name of the user to be authenticated. UserName MUST be encoded in the negotiated character set. This
        /// param can not be null.
        /// </param>
        /// <param name="workstation">
        /// The name of the computer to which the user is logged on. Workstation MUST be encoded in the negotiated
        /// character set. This param can not be null.
        /// </param>
        /// <param name="encryptedRandomSessionKey">
        /// The client's encrypted random session key. This param can be null.
        /// </param>
        /// <returns>the authenticate packet</returns>
        /// <exception cref="System.ArgumentNullException">
        /// when LM is using, the ntChallengeResponse should be null!
        /// </exception>
        public NlmpAuthenticatePacket CreateAuthenticatePacket(
            NegotiateTypes negotiateFlags,
            VERSION version,
            byte[] lmChallengeResponse,
            byte[] ntChallengeResponse,
            string domainName,
            string userName,
            string workstation,
            byte[] encryptedRandomSessionKey
            )
        {
            #region Parameter check

            if (domainName == null)
            {
                throw new ArgumentNullException("domainName");
            }

            if (userName == null)
            {
                throw new ArgumentNullException("userName");
            }

            if (workstation == null)
            {
                throw new ArgumentNullException("workstation");
            }

            if (NlmpUtility.IsLm(negotiateFlags) && ntChallengeResponse != null)
            {
                throw new ArgumentException(
                          "when LM is using, the ntChallengeResponse should be null!", "ntChallengeResponse");
            }

            #endregion

            NlmpAuthenticatePacket packet = new NlmpAuthenticatePacket();

            // update flags
            packet.SetNegotiateFlags(negotiateFlags);

            // if version required, update version
            if (NlmpUtility.IsVersionRequired(negotiateFlags))
            {
                packet.SetVersion(version);
            }

            // update domain name
            packet.SetDomainName(domainName);

            // update user name
            packet.SetUserName(userName);

            // if version required, update workstation
            if (NlmpUtility.IsVersionRequired(negotiateFlags))
            {
                packet.SetWorkstation(workstation);
            }

            // update lmChallengeResponse
            packet.SetLmChallengeResponse(lmChallengeResponse);

            // update ntChallengeResponse
            packet.SetNtChallengeResponse(ntChallengeResponse);

            // update encryptedRandomSessionKey
            packet.SetEncryptedRandomSessionKey(encryptedRandomSessionKey);

            return(packet);
        }
예제 #16
0
        /// <summary>
        /// Verify the authenticate packet locally
        /// </summary>
        /// <param name="authenticatePacket">actual authenticate packet</param>
        /// <param name="authenticateInformation">expected authenticate information</param>
        /// <param name="exportedSessionKey">exported session key</param>
        /// <returns></returns>
        private ClientAuthenticateInfomation VerifyAuthenticatePacketLocally(
            NlmpAuthenticatePacket authenticatePacket,
            ClientAuthenticateInfomation authenticateInformation,
            out byte[] exportedSessionKey)
        {
            // valid user name
            if (authenticateInformation.UserName.ToUpper() != this.nlmpServer.Context.ClientCredential.AccountName.ToUpper())
            {
                throw new InvalidOperationException(
                          "the user name is invalid!"
                          + " the user name retrieved form authenticate packet is not equal to the context.");
            }

            // calc the basekeys
            byte[] responseKeyLm;
            byte[] expectedNtChallengeResponse;
            byte[] expectedLmChallengeResponse;
            byte[] sessionBaseKey;
            byte[] keyExchangeKey;
            CalculateBaseKeys(
                authenticateInformation.ClientChallenge,
                this.systemTime,
                authenticateInformation.ServerName,
                authenticateInformation.DomainName,
                authenticateInformation.UserName,
                this.nlmpServer.Context.ClientCredential.Password,
                out responseKeyLm, out expectedNtChallengeResponse, out expectedLmChallengeResponse,
                out sessionBaseKey, out keyExchangeKey);

            // valid message
            ValidAuthenticateMessage(authenticatePacket, expectedNtChallengeResponse, expectedLmChallengeResponse);

            // generate keys.
            if (NlmpUtility.IsKeyExch(this.nlmpServer.Context.NegFlg))
            {
                exportedSessionKey = NlmpUtility.RC4(
                    keyExchangeKey, authenticatePacket.Payload.EncryptedRandomSessionKey);
            }
            else
            {
                exportedSessionKey = keyExchangeKey;
            }

            // validate mic
            byte[] messageMic = authenticatePacket.Payload.MIC;
            byte[] zeroMic    = new byte[16];
            if (messageMic != null && !ArrayUtility.CompareArrays <byte>(messageMic, zeroMic))
            {
                AUTHENTICATE_MESSAGE payload = authenticatePacket.Payload;
                payload.MIC = zeroMic;
                authenticatePacket.Payload = payload;

                byte[] mic = NlmpUtility.GetMic(exportedSessionKey, this.negotiate, this.challenge, this.authenticate);

                if (!ArrayUtility.CompareArrays <byte>(messageMic, mic))
                {
                    throw new InvalidOperationException("mic of authenticate packet is invalid");
                }
            }
            return(authenticateInformation);
        }
        /// <summary>
        /// initialize the mic of challenge packet
        /// </summary>
        /// <param name="exportedSessionKey">the exported session key</param>
        /// <param name="targetInfo">the target info contains av pairs.</param>
        /// <param name="authenticatePacket">the authenticate packet</param>
        /// <param name="challengePacket">the challenge packet</param>
        private void InitializeChallengeMIC(
            byte[] exportedSessionKey,
            ICollection<AV_PAIR> targetInfo,
            NlmpAuthenticatePacket authenticatePacket,
            NlmpChallengePacket challengePacket
            )
        {
            if (NlmpUtility.AvPairContains(targetInfo, AV_PAIR_IDs.MsvAvTimestamp))
            {
                // update mic with security algorithm
                byte[] mic = null;
                // if connectionless, this.negotiate is null.
                mic = NlmpUtility.GetMic(exportedSessionKey, this.negotiate, challengePacket, authenticatePacket);

                // get payload of packet
                AUTHENTICATE_MESSAGE payload = authenticatePacket.Payload;

                // update mic to payload
                payload.MIC = mic;

                // update the meaningful payload to packet
                authenticatePacket.Payload = payload;
            }
        }
        /// <summary>
        /// copy constructor.
        /// </summary>
        /// <param name="stackPacket">the source packet</param>
        /// <exception cref="ArgumentNullException">the stackPacket.payload.MIC must not be null</exception>
        /// <exception cref="ArgumentException">the stackPacket.payload.MIC.Length is wrong</exception>
        public NlmpAuthenticatePacket(
            NlmpAuthenticatePacket stackPacket
            )
            : base(stackPacket)
        {
            this.payload = stackPacket.payload;

            if (stackPacket.payload.MIC == null)
            {
                throw new ArgumentNullException("stackPacket", "the stackPacket.payload.MIC must not be null");
            }

            if (stackPacket.payload.MIC.Length != NlmpUtility.AUTHENTICATE_MESSAGE_MIC_CONST_SIZE)
            {
                throw new ArgumentException(
                          string.Format("the stackPacket.payload.MIC.Length must be {0}, actually {1}",
                                        NlmpUtility.AUTHENTICATE_MESSAGE_MIC_CONST_SIZE,
                                        stackPacket.payload.MIC.Length),
                          "stackPacket");
            }

            this.payload.MIC = new byte[stackPacket.payload.MIC.Length];
            Array.Copy(stackPacket.payload.MIC, this.payload.MIC, stackPacket.payload.MIC.Length);

            if (stackPacket.payload.DomainName != null)
            {
                this.payload.DomainName = new byte[stackPacket.payload.DomainName.Length];
                Array.Copy(
                    stackPacket.payload.DomainName,
                    this.payload.DomainName, stackPacket.payload.DomainName.Length);
            }

            if (stackPacket.payload.UserName != null)
            {
                this.payload.UserName = new byte[stackPacket.payload.UserName.Length];
                Array.Copy(
                    stackPacket.payload.UserName, this.payload.UserName, stackPacket.payload.UserName.Length);
            }

            if (stackPacket.payload.Workstation != null)
            {
                this.payload.Workstation = new byte[stackPacket.payload.Workstation.Length];
                Array.Copy(
                    stackPacket.payload.Workstation,
                    this.payload.Workstation, stackPacket.payload.Workstation.Length);
            }

            if (stackPacket.payload.LmChallengeResponse != null)
            {
                this.payload.LmChallengeResponse = new byte[stackPacket.payload.LmChallengeResponse.Length];
                Array.Copy(
                    stackPacket.payload.LmChallengeResponse,
                    this.payload.LmChallengeResponse, stackPacket.payload.LmChallengeResponse.Length);
            }

            if (stackPacket.payload.NtChallengeResponse != null)
            {
                this.payload.NtChallengeResponse = new byte[stackPacket.payload.NtChallengeResponse.Length];
                Array.Copy(
                    stackPacket.payload.NtChallengeResponse,
                    this.payload.NtChallengeResponse, stackPacket.payload.NtChallengeResponse.Length);
            }

            if (stackPacket.payload.EncryptedRandomSessionKey != null)
            {
                this.payload.EncryptedRandomSessionKey =
                    new byte[stackPacket.payload.EncryptedRandomSessionKey.Length];
                Array.Copy(
                    stackPacket.payload.EncryptedRandomSessionKey,
                    this.payload.EncryptedRandomSessionKey, stackPacket.payload.EncryptedRandomSessionKey.Length);
            }
        }