/// <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> /// 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 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> /// 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> /// 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; }