public void ComputeNtlmV2(string targetName, bool unverifiedTargetName, byte[] channelBinding) { var targetInfo = new NtlmTargetInfo(); int avFlags = 0; // If the CHALLENGE_MESSAGE contains a TargetInfo field if (challenge.TargetInfo != null) { challenge.TargetInfo.CopyTo(targetInfo); if (targetInfo.Flags.HasValue) { avFlags = targetInfo.Flags.Value; } // If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an MsvAvTimestamp present, the client SHOULD provide a MIC. if (challenge.TargetInfo?.Timestamp != null) { // If there is an AV_PAIR structure (section 2.2.2.1) with the AvId field set to MsvAvFlags, then in the Value field, set bit 0x2 to 1. // Else add an AV_PAIR structure and set the AvId field to MsvAvFlags and the Value field bit 0x2 to 1. targetInfo.Flags = avFlags |= 0x2; // Temporarily set the MIC to Z16. Mic = Z16; } // If ClientSuppliedTargetName (section 3.1.1.2) is not NULL if (targetName != null) { // If UnverifiedTargetName (section 3.1.1.2) is TRUE, then in AvId field = MsvAvFlags set 0x00000004 bit. if (unverifiedTargetName) { targetInfo.Flags = avFlags |= 0x4; } // Add an AV_PAIR structure and set the AvId field to MsvAvTargetName and the Value field to ClientSuppliedTargetName without // terminating NULL. targetInfo.TargetName = targetName; } else { // Else add an AV_PAIR structure and set the AvId field to MsvAvTargetName and the Value field to an empty string without terminating NULL. targetInfo.TargetName = string.Empty; } // The client SHOULD send the channel binding AV_PAIR: // If the ClientChannelBindingsUnhashed (section 3.1.1.2) is not NULL if (channelBinding != null) { // Add an AV_PAIR structure and set the AvId field to MsvAvChannelBindings and the Value field to MD5_HASH(ClientChannelBindingsUnhashed). targetInfo.ChannelBinding = NtlmUtils.MD5(channelBinding); } else { // Else add an AV_PAIR structure and set the AvId field to MsvAvChannelBindings and the Value field to Z(16). targetInfo.ChannelBinding = Z16; } } var encodedTargetInfo = targetInfo.Encode((Flags & NtlmFlags.NegotiateUnicode) != 0); // Note: For NTLMv2, the sessionBaseKey is the same as the keyExchangeKey. NtlmUtils.ComputeNtlmV2(challenge, Domain, UserName, Password, encodedTargetInfo, clientChallenge, Timestamp, out var ntChallengeResponse, out var lmChallengeResponse, out var keyExchangeKey); NtChallengeResponse = ntChallengeResponse; LmChallengeResponse = lmChallengeResponse; if ((Flags & NtlmFlags.NegotiateKeyExchange) != 0 && (Flags & (NtlmFlags.NegotiateSign | NtlmFlags.NegotiateSeal)) != 0) { ExportedSessionKey = NtlmUtils.NONCE(16); EncryptedRandomSessionKey = NtlmUtils.RC4K(keyExchangeKey, ExportedSessionKey); } else { ExportedSessionKey = keyExchangeKey; EncryptedRandomSessionKey = null; } // If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an MsvAvTimestamp present, the client SHOULD provide a MIC. if ((avFlags & 0x2) != 0) { Mic = NtlmUtils.HMACMD5(ExportedSessionKey, NtlmUtils.ConcatenationOf(negotiate.Encode(), challenge.Encode(), Encode())); } }
public NtlmAuthenticateMessage(NtlmNegotiateMessage negotiate, NtlmChallengeMessage challenge, string userName, string password, string domain, string workstation) : base(3) { if (negotiate == null) { throw new ArgumentNullException(nameof(negotiate)); } if (challenge == null) { throw new ArgumentNullException(nameof(challenge)); } if (userName == null) { throw new ArgumentNullException(nameof(userName)); } if (password == null) { throw new ArgumentNullException(nameof(password)); } clientChallenge = NtlmUtils.NONCE(8); this.negotiate = negotiate; this.challenge = challenge; if (!string.IsNullOrEmpty(domain)) { Domain = domain; } else if ((challenge.Flags & NtlmFlags.TargetTypeDomain) != 0) { // The server is domain-joined, so the TargetName will be the domain. Domain = challenge.TargetName; } else if (challenge.TargetInfo != null) { // The server is not domain-joined, so the TargetName will be the machine name of the server. Domain = challenge.TargetInfo.DomainName; } Workstation = workstation; UserName = userName; Password = password; // Use only the features supported by both the client and server. Flags = negotiate.Flags & challenge.Flags; // If the client and server both support NEGOTIATE_UNICODE, disable NEGOTIATE_OEM. if ((Flags & NtlmFlags.NegotiateUnicode) != 0) { Flags &= ~NtlmFlags.NegotiateOem; } // TODO: throw if Unicode && Oem are both unset? // If the client and server both support NEGOTIATE_EXTENDED_SESSIONSECURITY, disable NEGOTIATE_LM_KEY. if ((Flags & NtlmFlags.NegotiateExtendedSessionSecurity) != 0) { Flags &= ~NtlmFlags.NegotiateLanManagerKey; } // Disable NEGOTIATE_KEY_EXCHANGE if neither NEGOTIATE_SIGN nor NEGOTIATE_SEAL are also present. if ((Flags & NtlmFlags.NegotiateKeyExchange) != 0 && (Flags & (NtlmFlags.NegotiateSign | NtlmFlags.NegotiateSeal)) == 0) { Flags &= ~NtlmFlags.NegotiateKeyExchange; } // If we had RequestTarget in our initial NEGOTIATE_MESSAGE, include it again in this message(?) if ((negotiate.Flags & NtlmFlags.RequestTarget) != 0) { Flags |= NtlmFlags.RequestTarget; } // If NEGOTIATE_VERSION is set, grab the OSVersion from our original negotiate message. if ((Flags & NtlmFlags.NegotiateVersion) != 0) { OSVersion = negotiate.OSVersion ?? OSVersion; } }
public NtlmChallengeMessage(NtlmFlags flags, Version osVersion = null) : base(2) { serverChallenge = NtlmUtils.NONCE(8); OSVersion = osVersion; Flags = flags; }