/// <summary> /// This method constructs the digest request structure which is required to send from protocol client to protocol server. /// </summary> /// <param name="digestType">It indicates the DigestType field of the DIGEST_VALIDATION_REQ</param> /// <param name="algType">It indicates the AlgType field of the DIGEST_VALIDATION_REQ </param> /// <param name="ignoredFields">It indicates the fields that should be ignored by DC in DIGEST_VALIDATION_REQ </param> /// <param name="digestReq">The request structure for digest validation</param> private void GetDigestRequest( DigestType_Values digestType, AlgType_Values algType, IgnoredFields ignoredFields, out DIGEST_VALIDATION_REQ digestReq) { // create a temporary payload structure DIGEST_VALIDATION_REQ_Payload reqLoad = new DIGEST_VALIDATION_REQ_Payload(); reqLoad.AccountName = currentUserName; reqLoad.Algorithm = acceptedAlgType; reqLoad.Authzid = ""; reqLoad.Nonce = GenerateNonce(); reqLoad.CNonce = reqLoad.Nonce; reqLoad.Hentity = ""; reqLoad.NonceCount = nonceCountString; reqLoad.QOP = ""; reqLoad.Realm = sutDomainName; reqLoad.URI = "/"; reqLoad.Username = currentUserName; string servicename = serverName; string response = string.Empty; if (digestType == DigestType_Values.Basic) { reqLoad.Method = httpRequestMethodString; // calling RFC 2617 algorithms for calculating H(A1) and Response, when HTTP is involved. string strHA1 = DigestCalcHA1HTTP( reqLoad.Algorithm, reqLoad.Username, reqLoad.Realm, currentPassword, reqLoad.Nonce, reqLoad.CNonce, false); digestSessionKey = strHA1; response = DigestCalcResponseHTTP( strHA1, reqLoad.Nonce, reqLoad.NonceCount, reqLoad.CNonce, reqLoad.QOP, reqLoad.Method, reqLoad.URI, reqLoad.Hentity, false); } else if (digestType == DigestType_Values.SASL) { reqLoad.Method = saslRequestMethodString; // calling RFC 2617 algorithms for calculating H(A1) and Response, when SASL is involved. string strHA1 = DigestCalcHA1SASL( reqLoad.Authzid, reqLoad.Username, reqLoad.Realm, currentPassword, reqLoad.Nonce, reqLoad.CNonce, false); digestSessionKey = strHA1; response = DigestCalcResponseSASL( strHA1, reqLoad.Nonce, reqLoad.NonceCount, reqLoad.CNonce, reqLoad.QOP, reqLoad.URI, false, false); } string basicDigestCredentialString = "Digest username="******",realm=" + reqLoad.Realm + ",nonce=" + reqLoad.Nonce + ",uri=" + reqLoad.URI + ",cnonce=" + reqLoad.CNonce + ",nc=" + reqLoad.NonceCount + ",algorithm=" + reqLoad.Algorithm + ",response=" + response + ",qop=" + reqLoad.QOP + ",charset=" + encodingCodePageString + ",service-name=" + servicename; DpspResponse dpspResponse = DpspBasicResponse.Decode(basicDigestCredentialString); DIGEST_VALIDATION_REQ digestValidationReq = ApdsUtility.CreateDigestValidationRequestPacket( clientComputerName, string.Empty, string.Empty, reqLoad.Method, digestType, acceptedAlgType, dpspResponse); digestValidationReq.CharsetType = CharsetType_Values.ISO8859_1; digestValidationReq.AlgType = algType; digestValidationReq.Flags = (DIGEST_VALIDATION_FLAGS)5; digestValidationReq.Reserved3 = (DIGEST_VALIDATION_REQ_Reserved3_Values)ignoredFields.reserved3; digestValidationReq.Reserved4 = (Reserved4_Values)ignoredFields.reserved4; digestReq = digestValidationReq; }
/// <summary> /// Send Digest request message to DC, and validate the credentials. /// </summary> /// <param name="isValidationSuccess">Indicates whether the validation from server will be success.</param> /// <param name="digestType">Indicates the DigestType field of the request.</param> /// <param name="algType">Indicates the AlgType field of the request.</param> /// <param name="ignoredFields">It indicates the fields that should be ignored by DC in DIGEST_VALIDATION_REQ.</param> /// <returns>Indicates the result status of DC response.</returns> public Status GenerateDigestRequest( Boolean isValidationSuccess, AccountInformation accountInfo, DigestType_Values digestType, AlgType_Values algType, IgnoredFields ignoredFields) { DIGEST_VALIDATION_REQ digestReq; _NETLOGON_LOGON_INFO_CLASS logonLevel = _NETLOGON_LOGON_INFO_CLASS.NetlogonGenericInformation; _NETLOGON_VALIDATION_INFO_CLASS validationLevel = _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationGenericInfo2; //Compute the password GenerateCurrentCredentials(accountInfo); //Get Digest Request GetDigestRequest(digestType, algType, ignoredFields, out digestReq); //Create digest validation logon info _NETLOGON_LEVEL netlogonLevel = ApdsUtility.CreateDpspLogonInfo( NrpcParameterControlFlags.AllowLogonWithComputerAccount, digestReq); //Create Secure Channel EstablishSecureChannel(); //Client calls EncryptNetlogonLevel _NETLOGON_LEVEL encryptedLogonLevel = nrpcClient.EncryptNetlogonLevel( (_NETLOGON_LOGON_INFO_CLASS)logonLevel, netlogonLevel); //Client calls NetrLogonSamLogonEx _NETLOGON_VALIDATION?validationInfomation; byte?authoritative; NrpcNetrLogonSamLogonExtraFlags?extraFlags = NrpcNetrLogonSamLogonExtraFlags.None; result = nrpcClient.NetrLogonSamLogonEx( nrpcClient.Handle, sutComputerName, clientComputerName, logonLevel, encryptedLogonLevel, validationLevel, out validationInfomation, out authoritative, ref extraFlags); // Whether this method use pass-through mechanism or not. bool isPassThroughMethod = false; // //The Digest validation protocol SHOULD use the generic pass-through mechanism. //For generic pass-through, the LogonLevel is 4(NetlogonGenericInformation) as defined in [MS-NRPC] section 3.2.4.1. //So when the LogonLevel is 4, we can say that NRPC pass-through authentication method is used. // if ((int)logonLevel == 4) { isPassThroughMethod = true; } // //Verify MS-APDS requirment:MS-APDS_R5 // Site.CaptureRequirementIfIsTrue( isPassThroughMethod, 5, @"For domain support, authentication protocols MUST use an NRPC pass-through authentication ([MS-NRPC] section 3.2) method with parameters determined by the authentication protocol being used[to return the response structures to member server]."); // //Verify MS-APDS requirment:MS-APDS_R15 // Site.CaptureRequirementIfAreEqual <int>( 5, (int)validationLevel, 15, @"Digest response messages MUST be encoded as opaque blobs and transported by the generic pass-through capability of Netlogon."); // //Configured in PTFConfig file,default SHOULD to true and MAY to false. // string isR169Implemented = "true"; // //Check OS version // if (isWindows) { // //Verify MS-APDS requirment:MS-APDS_R100169 // Site.CaptureRequirementIfAreEqual <int>( 5, (int)validationLevel, 100169, @"In Windows, the Digest validation protocol uses the generic pass-through mechanism."); if (null == isR169Implemented) { Site.Properties.Add("R169Implemented", Boolean.TrueString); isR169Implemented = Boolean.TrueString; } } if (null != isR169Implemented) { bool implSigns = Boolean.Parse(isR169Implemented); bool isSatisfied = ((int)_NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationGenericInfo2 == 5); // //Verify MS-APDS requirment:MS-APDS_R169 // Site.CaptureRequirementIfAreEqual <Boolean>( implSigns, isSatisfied, 169, string.Format(@"The Digest validation protocol SHOULD use the generic pass-through mechanism. This requirement is {0} implemented.", implSigns ? "" : "not")); } byte[] buf; bool isDigestResStructure = true; DIGEST_VALIDATION_RESP digestValidationResponse = new DIGEST_VALIDATION_RESP(); // Judge whether the digest response is exist, validationData is response point. // if validationData equals 0, no response return from NRPC. if (validationInfomation.Value.ValidationGeneric2 == null) { isDigestResStructure = false; } else { // decrypt validation info buf = NrpcUtility.DecryptBuffer( (nrpcClient.Context.NegotiateFlags & NrpcNegotiateFlags.SupportsAESAndSHA2) == NrpcNegotiateFlags.SupportsAESAndSHA2, nrpcClient.Context.SessionKey, validationInfomation.Value.ValidationGeneric2[0].ValidationData); _NETLOGON_VALIDATION decryptValidationInfo = new _NETLOGON_VALIDATION(); decryptValidationInfo.ValidationGeneric2 = new _NETLOGON_VALIDATION_GENERIC_INFO2[1]; decryptValidationInfo.ValidationGeneric2[0].ValidationData = buf; digestValidationResponse = ApdsUtility.ConvertDataToDigestValidationResponse(decryptValidationInfo); VerifyMessageSyntaxDigestValidCredential(digestValidationResponse, validationLevel, (uint)buf.Length); } if (result != NtStatus.STATUS_SUCCESS) { // //Verify MS-APDS requirment:MS-APDS_R292 // Site.CaptureRequirementIfIsFalse( isDigestResStructure, 292, "[If unsuccessful]It[DC] MUST NOT send back the DIGEST_VALIDATION_RESP message."); VerifyMessageSyntxDigestInvalidResp((uint)result, digestValidationResponse.AuthDataSize); } return((Status)result); }
/// <summary> /// Send KERB_VERIFY_PAC_REQUEST message to DC through generic pass-through mechanism /// </summary> /// <param name="serverSignature">Server Signature in the privilege attribute certificate (PAC)</param> /// <param name="kdcSignature">Key Distribution Center (KDC) Signature in the PAC</param> /// <returns></returns> public Status GenerateKerberosValidationRequest( PAC_SIGNATURE_DATA serverSignature, PAC_SIGNATURE_DATA kdcSignature) { _NETLOGON_LOGON_INFO_CLASS logonLevel = _NETLOGON_LOGON_INFO_CLASS.NetlogonGenericInformation; _NETLOGON_VALIDATION_INFO_CLASS validationLevel = _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationGenericInfo2; //Get Kerberos Request KERB_VERIFY_PAC_REQUEST kerberosReq = ApdsUtility.CreateKerbVerifyPacRequest(serverSignature, kdcSignature); //Create digest validation logon info _NETLOGON_LEVEL netlogonLevel = ApdsUtility.CreatePacLogonInfo( NrpcParameterControlFlags.AllowLogonWithComputerAccount, PrimaryDomainDnsName, DomainAdministratorName, PDCNetbiosName, kerberosReq); //Create Secure Channel EstablishSecureChannel(); //Client calls EncryptNetlogonLevel _NETLOGON_LEVEL encryptedLogonLevel = nrpcClient.EncryptNetlogonLevel( (_NETLOGON_LOGON_INFO_CLASS)logonLevel, netlogonLevel); //Client calls NetrLogonSamLogonEx _NETLOGON_VALIDATION?validationInfomation; byte?authoritative; NrpcNetrLogonSamLogonExtraFlags?extraFlags = NrpcNetrLogonSamLogonExtraFlags.None; result = nrpcClient.NetrLogonSamLogonEx( nrpcClient.Handle, sutComputerName, clientComputerName, logonLevel, encryptedLogonLevel, validationLevel, out validationInfomation, out authoritative, ref extraFlags); // Whether this method use pass-through mechanism or not. bool isPassThroughMethod = false; //Kerberos PAC validation SHOULD use the generic pass-through mechanism ([MS-NRPC] section 3.2.4.1). //For generic pass-through, the LogonLevel is 4(NetlogonGenericInformation) as defined in [MS-NRPC] section 3.2.4.1. //So when the LogonLevel is 4, we can say that NRPC pass-through authentication method is used. if ((int)logonLevel == 4) { isPassThroughMethod = true; } Site.CaptureRequirementIfIsTrue( isPassThroughMethod, 5, @"For domain support, authentication protocols MUST use an NRPC pass-through authentication ([MS-NRPC] section 3.2) method with parameters determined by the authentication protocol being used[to return the response structures to member server]."); Site.CaptureRequirementIfAreEqual <int>( 5, (int)validationLevel, 402, @"The encoded data SHOULD be sent by using the generic pass-through mechanism ([MS-NRPC] section 3.2.4.1)."); if (result == NtStatus.STATUS_SUCCESS) { Site.CaptureRequirementIfIsNull(validationInfomation.Value.ValidationGeneric2[0].ValidationData, 403, @"If the checksum is verified, the DC MUST return STATUS_SUCCESS. There is no return message."); } return((Status)result); }
/// <summary> /// Send NTLM request message to DC for validation. /// </summary> /// <param name="logonLevel">Indicates the logon level.</param> /// <param name="accountInfo">Indicates whether this account is valid in DC validation.</param> /// <param name="isAccessCheckSuccess">Indicates whether the access checked success.</param> /// <param name="validationLevel">Indicates the validation level.</param> /// <returns>Indicates the result status of DC response.</returns> public Status NTLMLogon( _NETLOGON_LOGON_INFO_CLASS logonLevel, AccountInformation accountInfo, Boolean isAccessCheckSuccess, _NETLOGON_VALIDATION_INFO_CLASS validationLevel) { //Compute the password GenerateCurrentCredentials(accountInfo); //Create Secure Channel EstablishSecureChannel(); _NETLOGON_LEVEL netLogonLevel = new _NETLOGON_LEVEL(); if (logonLevel == _NETLOGON_LOGON_INFO_CLASS.NetlogonInteractiveInformation) { //Fill InteractiveInfo netLogonLevel = ApdsUtility.CreateNlmpInteractiveLogonInfo( NrpcParameterControlFlags.AllowLogonWithComputerAccount, sutTrustDomainName, currentUserName, currentPassword, sutComputerName); } else if (logonLevel == _NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkInformation) { //Fill Netlogon_Network_Info if (accountInfo == AccountInformation.ManagedServiceAccount) { netLogonLevel = nrpcClient.CreateNetlogonLevel( _NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkInformation, NrpcParameterControlFlags.AllowLogonWithComputerAccount, sutDomainName, // managed service account is created in the local domain currentUserName, currentPassword); } else { netLogonLevel = nrpcClient.CreateNetlogonLevel( _NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkInformation, NrpcParameterControlFlags.AllowLogonWithComputerAccount, sutTrustDomainName, currentUserName, currentPassword); } } _NETLOGON_LEVEL encryptedLogonLevel = nrpcClient.EncryptNetlogonLevel( logonLevel, netLogonLevel); byte?authoritative; _NETLOGON_VALIDATION? validationInformation = new _NETLOGON_VALIDATION(); NrpcNetrLogonSamLogonExtraFlags?extraFlags = NrpcNetrLogonSamLogonExtraFlags.None; result = nrpcClient.NetrLogonSamLogonEx( nrpcClient.Handle, sutComputerName, clientComputerName, logonLevel, encryptedLogonLevel, validationLevel, out validationInformation, out authoritative, ref extraFlags); // Whether this method use pass-through mechanism or not. bool isPassThroughMethod = false; // //For NTLM pass-through, the LogonLevel is 1(NetlogonInteractiveInformation) or 2(NetlogonNetworkInformation) //based on Interactive Logono and Network Logon,as defined in [MS-APDS] section 3.1.5.1 and section 3.1.5.2. //So when the LogonLevel is 1 or 2, we can say that NRPC pass-through authentication method is used. // if ((int)logonLevel == 1 || (int)logonLevel == 2) { isPassThroughMethod = true; } // //Verify MS-APDS requirment:MS-APDS_R5 // Site.CaptureRequirementIfIsTrue( isPassThroughMethod, 5, @"For domain support, authentication protocols MUST use an NRPC pass-through authentication ([MS-NRPC] section 3.2) method with parameters determined by the authentication protocol being used[to return the response structures to member server]."); // //Verify MS-APDS requirment:MS-APDS_R118 //if isPassThroughMethod is true, this requirement can be verified. // Site.CaptureRequirementIfIsTrue( isPassThroughMethod, 118, @"NT LAN Manager (NTLM) interactive logon and network logon MUST receive the authentication response sequence by contacting the DC using an NRPC pass-through authentication method ."); if (result == NtStatus.STATUS_SUCCESS) { if (validationLevel == _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4) { VerifyNTLMDataStructure(validationLevel, logonLevel); VerifyMessageSyntxNTLMValidV4Resp( validationInformation.Value.ValidationSam4[0].UserSessionKey.data[0].data, validationInformation.Value.ValidationSam4[0].EffectiveName.ToString()); } else if (validationLevel == _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo2) { VerifyNTLMDataStructure(validationLevel, logonLevel); } else if (validationLevel == _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo) { VerifyNTLMDataStructure(validationLevel, logonLevel); } } return((Status)result); }