/// <summary> /// Decodes the request stub, and fills the fields of the class /// </summary> /// <param name="sessionContext">The session context of the request received</param> /// <param name="requestStub">The request stub got from RPCE layer</param> internal override void Decode(NrpcServerSessionContext sessionContext, byte[] requestStub) { using (RpceInt3264Collection outParamList = RpceStubDecoder.ToParamList( RpceStubHelper.GetPlatform(), NrpcRpcStubFormatString.TypeFormatString, new RpceStubExprEval[] { new RpceStubExprEval(NrpcRpcAdapter.logon__NETLOGON_DELTA_USERExprEval_0000) }, NrpcRpcStubFormatString.ProcFormatString, NrpcRpcStubFormatString.ProcFormatStringOffsetTable[(int)Opnum], false, requestStub)) { LogonServer = Marshal.PtrToStringUni(outParamList[0]); ComputerName = Marshal.PtrToStringUni(outParamList[1]); Authenticator = TypeMarshal.ToNullableStruct<_NETLOGON_AUTHENTICATOR>(outParamList[2]); ReturnAuthenticator = TypeMarshal.ToNullableStruct<_NETLOGON_AUTHENTICATOR>(outParamList[3]); LogonLevel = (_NETLOGON_LOGON_INFO_CLASS)outParamList[4].ToInt32(); LogonInformation = TypeMarshal.ToNullableStruct<_NETLOGON_LEVEL>(outParamList[5], LogonLevel, null, null); ValidationLevel = (_NETLOGON_VALIDATION_INFO_CLASS)outParamList[6].ToInt32(); ExtraFlags = TypeMarshal.ToNullableStruct<uint>(outParamList[9]); } }
/// <summary> /// The NetrLogonSamLogonWithFlags method Supported in windows_xpwindows_server_2003, /// windows_vista, windows_server_2008, windows_7, and /// windows_server_7. handles logon requests for the SAM /// accounts. Opnum: 45 /// </summary> /// <param name="LogonServer"> /// The custom RPC binding handle, as specified in section /// . /// </param> /// <param name="ComputerName"> /// The Unicode string that contains the NetBIOS name of /// the client computer calling this method. /// </param> /// <param name="Authenticator"> /// A pointer to a NETLOGON_AUTHENTICATOR structure, as /// specified in section , that contains the client authenticator. /// </param> /// <param name="ReturnAuthenticator"> /// A pointer to a NETLOGON_AUTHENTICATOR structure, as /// specified in section , that contains the server return /// authenticator. /// </param> /// <param name="LogonLevel"> /// A NETLOGON_LOGON_INFO_CLASS structure, as specified /// in section , that specifies the type of logon information /// passed in the LogonInformation parameter. /// </param> /// <param name="LogonInformation"> /// A pointer to a NETLOGON_LEVEL structure, as specified /// in section , that describes the logon request information. /// </param> /// <param name="ValidationLevel"> /// A NETLOGON_VALIDATION_INFO_CLASS enumerated type, as /// specified in section , that contains the validation /// level requested by the client. /// </param> /// <param name="ValidationInformation"> /// A pointer to a NETLOGON_VALIDATION structure, as specified /// in section , that describes the user validation information /// returned to the client. The type of the NETLOGON_VALIDATION /// used is determined by the value of the ValidationLevel /// parameter. /// </param> /// <param name="Authoritative"> /// A pointer to a char value representing a Boolean condition. /// FALSE is indicated by the value 0x00; TRUE SHOULD be /// indicated by the value 0x01 and MAY also be indicated /// by any nonzero value. Windows uses the value of 0x01 /// as the representation of TRUE and 0x00 for FALSE. This /// Boolean value indicates whether the validation information /// is final. This field is necessary because the request /// might be forwarded through multiple servers. A value /// of TRUE indicates that the validation information is /// final and MUST remain unchanged. /// </param> /// <param name="ExtraFlags"> /// A pointer to a set of bit flags that specify delivery /// settings. A flag is TRUE (or set) if its value is equal /// to 1. The value is constructed from zero or more bit /// flags from the following table. /// </param> public NtStatus NetrLogonSamLogonWithFlags( string LogonServer, string ComputerName, _NETLOGON_AUTHENTICATOR? Authenticator, ref _NETLOGON_AUTHENTICATOR? ReturnAuthenticator, _NETLOGON_LOGON_INFO_CLASS LogonLevel, _NETLOGON_LEVEL? LogonInformation, _NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, out _NETLOGON_VALIDATION? ValidationInformation, out byte? Authoritative, ref uint? ExtraFlags) { const ushort opnum = 45; byte[] requestStub; byte[] responseStub; Int3264[] paramList; int retVal; SafeIntPtr pLogonServer = Marshal.StringToHGlobalUni(LogonServer); SafeIntPtr pComputerName = Marshal.StringToHGlobalUni(ComputerName); SafeIntPtr pAuthenticator = TypeMarshal.ToIntPtr(Authenticator); SafeIntPtr pReturnAuthenticatorIn = TypeMarshal.ToIntPtr(ReturnAuthenticator); SafeIntPtr pLogonInformation = TypeMarshal.ToIntPtr(LogonInformation, LogonLevel, null, null); SafeIntPtr pExtraFlags = TypeMarshal.ToIntPtr(ExtraFlags); paramList = new Int3264[] { pLogonServer, pComputerName, pAuthenticator, pReturnAuthenticatorIn, (uint)LogonLevel, pLogonInformation, (uint)ValidationLevel, IntPtr.Zero, IntPtr.Zero, pExtraFlags, 0 // retVal }; requestStub = RpceStubEncoder.ToBytes( RpceStubHelper.GetPlatform(), NrpcRpcStubFormatString.TypeFormatString, new RpceStubExprEval[] { new RpceStubExprEval(logon__NETLOGON_DELTA_USERExprEval_0000) }, NrpcRpcStubFormatString.ProcFormatString, NrpcRpcStubFormatString.ProcFormatStringOffsetTable[opnum], true, paramList); rpceClientTransport.Call(opnum, requestStub, rpceTimeout, out responseStub); using (RpceInt3264Collection outParamList = RpceStubDecoder.ToParamList( RpceStubHelper.GetPlatform(), NrpcRpcStubFormatString.TypeFormatString, new RpceStubExprEval[] { new RpceStubExprEval(logon__NETLOGON_DELTA_USERExprEval_0000) }, NrpcRpcStubFormatString.ProcFormatString, NrpcRpcStubFormatString.ProcFormatStringOffsetTable[opnum], true, responseStub, paramList)) { IntPtr pReturnAuthenticatorOut = outParamList[3]; ReturnAuthenticator = TypeMarshal.ToNullableStruct<_NETLOGON_AUTHENTICATOR>(pReturnAuthenticatorOut); IntPtr pValidationInformation = outParamList[7]; ValidationInformation = TypeMarshal.ToNullableStruct<_NETLOGON_VALIDATION>( pValidationInformation, ValidationLevel, null, null); Authoritative = TypeMarshal.ToNullableStruct<byte>(outParamList[8]); ExtraFlags = TypeMarshal.ToNullableStruct<uint>(outParamList[9]); retVal = outParamList[10].ToInt32(); } pLogonServer.Dispose(); pComputerName.Dispose(); pAuthenticator.Dispose(); pReturnAuthenticatorIn.Dispose(); pLogonInformation.Dispose(); pExtraFlags.Dispose(); return (NtStatus)retVal; }
/// <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> /// Verify response data structure for NTLM is NetlogonValidationSamInfo2 or NetlogonValidationSamInfo4 /// </summary> /// <param name="validationLevel">request validation data structure level</param> /// <param name="logonLevel">request logon level</param> private void VerifyNTLMDataStructure( _NETLOGON_VALIDATION_INFO_CLASS validationLevel, _NETLOGON_LOGON_INFO_CLASS logonLevel) { if (validationLevel == _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo) { // //Verify MS-APDS requirment:MS-APDS_R100136 // Site.CaptureRequirementIfAreEqual <_NETLOGON_VALIDATION_INFO_CLASS>( _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo, validationLevel, 100136, @"If there is a successful match[the local copy of password matches with the one sent in the request ], the domain controller MUST return data with ValidationInformation containing a reference to NETLOGON_VALIDATION_SAM_INFO ([MS-NRPC] section 2.2.1.4.11), if the ValidationLevel in the request is NetlogonValidationSamInfo."); } if (validationLevel == _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo2) { // //Verify MS-APDS requirment:MS-APDS_R136 //this capture function is invoked when the return value is success, //It means that the local copy of password must match with the one sent in the request // Site.CaptureRequirementIfAreEqual <_NETLOGON_VALIDATION_INFO_CLASS>( _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo2, validationLevel, 136, @"If the local copy of password matches with the one sent in the request, and the ValidationLevel in the request is NetlogonValidationSamInfo2, then domain controller MUST return data with ValidationInformation containing a reference to NETLOGON_VALIDATION_SAM_INFO2."); } if (validationLevel == _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4) { // //Verify MS-APDS requirment:MS-APDS_R135 // Site.CaptureRequirementIfAreEqual <_NETLOGON_VALIDATION_INFO_CLASS>( _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4, validationLevel, 135, @"If the local copy of password matches with the one sent in the request, and the ValidationLevel in the request is NetlogonValidationSamInfo4, then domain controller MUST return data with ValidationInformation containing a reference to NETLOGON_VALIDATION_SAM_INFO4."); // //Verify MS-APDS requirment:MS-APDS_R122 // Site.CaptureRequirementIfAreEqual <_NETLOGON_VALIDATION_INFO_CLASS>( _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4, validationLevel, 122, @"The DC MUST send the NETLOGON_VALIDATION_SAM_INFO4 structures back to the NTLM server"); if (logonLevel == _NETLOGON_LOGON_INFO_CLASS.NetlogonNetworkInformation) { // //Configured in PTFConfig file,default SHOULD to true and MAY to false. // string isR120Implemented = "true"; // //Check OS version // if (isWindows) { // //Verify MS-APDS requirment:MS-APDS_R100120 // Site.CaptureRequirementIfAreEqual <int>( 6, (int)validationLevel, 100120, @"In Windows, in NTLM Network logon, NETLOGON_VALIDATION_SAM_INFO4 data structures are returned by the server."); if (null == isR120Implemented) { Site.Properties.Add("R120Implemented", Boolean.TrueString); isR120Implemented = Boolean.TrueString; } } if (null != isR120Implemented) { bool implSigns = Boolean.Parse(isR120Implemented); bool isSatisfied = ((int)_NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4 == 6); // //Verify MS-APDS requirment:MS-APDS_R120 // Site.CaptureRequirementIfAreEqual <Boolean>( implSigns, isSatisfied, 120, string.Format(@"In NTLM Network logon, NETLOGON_VALIDATION_SAM_INFO4 data structures SHOULD be returned by the server. This requirement is {0} implemented.", implSigns? "":"not")); } } } }
/// <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); }
/// <summary> /// Helper function to verify all the server end-point requirements for Digest Validation /// when valid credentials are provided. /// </summary> /// <param name="digestResponseStructure">The digest response structure as DIGEST_VALIDATION_RESP.</param> /// <param name="validationlevel">The validation level for digest validation.</param> /// <param name="digestResponseLength">The length of digest response.</param> private void VerifyMessageSyntaxDigestValidCredential( DIGEST_VALIDATION_RESP digestResponseStructure, _NETLOGON_VALIDATION_INFO_CLASS validationlevel, uint digestResponseLength) { // // Verify R95 // Site.CaptureRequirementIfAreEqual <uint>( // The expected MessageType V2 is equal to 0x0000000a. (uint)MessageType.V2, //digestResponseStructure.messageType, (uint)digestResponseStructure.MessageType, 95, @"'MessageType' filed in the DIGEST_VALIDATION_RESP Message is a 32-bit unsigned integer that MUST specify the Digest validation message type."); // // Verify R96 // Site.CaptureRequirementIfAreEqual <uint>( // The expected MessageType V2 is equal to 0x0000000a. (uint)MessageType.V2, (uint)digestResponseStructure.MessageType, 96, @"The value of the 'MessageType' field in the DIGEST_VALIDATION_RESP Message MUST be 0x00000000A."); // // Verify R97 // Site.CaptureRequirementIfAreEqual <ushort>( // The expected Version V1 is equal to 0x0001. (ushort)Version.V1, (ushort)digestResponseStructure.Version, 97, @"'Version' filed in the DIGEST_VALIDATION_RESP Message is a 16-bit unsigned integer that MUST specify the version of the Digest validation protocol."); // // Verify R98 // Site.CaptureRequirementIfAreEqual <ushort>( // The expected Version V1 is equal to 0x0001. (ushort)Version.V1, (ushort)digestResponseStructure.Version, 98, @"The value of 'Version' field in the DIGEST_VALIDATION_RESP Message MUST be 0x0001."); // // Verify R249 // Site.CaptureRequirementIfAreEqual <uint>( // The expected Pad2 equals 0. 0, (uint)digestResponseStructure.Pad2, 249, @"[For 'Pad2' filed in the DIGEST_VALIDATION_RESP Message] The value of this member MUST be set to zero when sent."); // The length of expected session key. ushort currentSessionKeyLength = (ushort)(digestResponseStructure.SessionKey.Length + //Marshal.SizeOf(digestResponseStructure.SessionKey_NULL_terminator)); Marshal.SizeOf((byte)digestResponseStructure.SessionKey_NULL_terminator)); // // Verify R101 // Site.CaptureRequirementIfAreEqual <ushort>( currentSessionKeyLength, digestResponseStructure.SessionKeyLength, 101, @"'SessionKeyLength' field in the DIGEST_VALIDATION_RESP Message is a 16-bit unsigned integer that MUST specify the number of bytes of the 'SessionKey field' in the DIGEST_VALIDATION_RESP message plus a terminating null character."); // // Verify R252 // Site.CaptureRequirementIfAreEqual <uint>( // The length of digest session buff. 33, digestResponseStructure.SessionKeyLength, 252, @"For the value in SessionKeyLength field of the DIGEST_VALIDATION_RESP Message]It MUST be equal to 33."); // // Verify R254 // Site.CaptureRequirementIfAreEqual <uint>( // The expected Pad3 equals 0. 0, (uint)digestResponseStructure.Pad3, 254, @"[For 'Pad3' filed in the DIGEST_VALIDATION_RESP Message] The value of this member MUST be set to zero when sent."); // // Verify R102 // Site.CaptureRequirementIfAreEqual <uint>( (uint)digestResponseStructure.AuthData.Length, digestResponseStructure.AuthDataSize, 102, @"'AuthDataSize' field in the DIGEST_VALIDATION_RESP Message is a 32-bit unsigned integer that MUST specify the number of bytes of the 'AuthData' field in this( DIGEST_VALIDATION_RESP ) message."); // // Verify R103 // Site.CaptureRequirementIfAreEqual <ushort>( (ushort)digestResponseStructure.AccountName.Length, digestResponseStructure.AcctNameSize, 103, @"'AcctNameSize' field in the DIGEST_VALIDATION_RESP Message is a 16-bit unsigned integer that MUST specify the number of bytes of the AccountName field in this( DIGEST_VALIDATION_RESP ) message."); // // Verify R104 // Site.CaptureRequirementIfAreEqual <ushort>( // The expected Reserved1 V1 is equal to 0. (ushort)Reserved1.V1, (ushort)digestResponseStructure.Reserved1, 104, @"The value of this member['Reserved1' field in the DIGEST_VALIDATION_RESP Message] MUST be set to zero when sent."); // // Verify R106 // Site.CaptureRequirementIfAreEqual <uint>( digestResponseLength, digestResponseStructure.MessageSize, 106, @"'MessageSize' field in the DIGEST_VALIDATION_RESP Message is a 32-bit unsigned integer that MUST specify the number of bytes in the entire DIGEST_VALIDATION_RESP message."); // // Verify R107 // Site.CaptureRequirementIfAreEqual <uint>( // The expected Reserved3 V1 is equal to 0. (uint)Resp_Reserved3.V1, (uint)digestResponseStructure.Reserved3, 107, @"'Reserved3' field in the DIGEST_VALIDATION_RESP Message is a 32-bit unsigned integer field reserved for future use , the value of this member MUST be set to zero when sent."); // // Verify R109 // Site.CaptureRequirementIfAreEqual <string>( digestSessionKey.ToString(), Encoding.ASCII.GetString(digestResponseStructure.SessionKey), 109, @"'SessionKey' field in the DIGEST_VALIDATION_RESP Message is a 32-byte buffer that MUST contain the Digest SessionKey."); // Get the max length of session key. int maxSessionKeyLength = 32; // // Verify R110 // Judge whether the size of SessionKeyLength in response structure equals the actual session key length. Boolean isSessionkeyVerified = ((digestResponseStructure.SessionKey.Length <= maxSessionKeyLength) && (digestResponseStructure.SessionKeyLength == digestResponseStructure.SessionKey.Length + Marshal.SizeOf((byte)digestResponseStructure.SessionKey_NULL_terminator))); Site.Log.Add( LogEntryKind.Comment, "R110, sessionKey length value: {0}, max session length value: {1}", digestResponseStructure.SessionKey.Length.ToString(), maxSessionKeyLength.ToString()); Site.CaptureRequirementIfIsTrue( isSessionkeyVerified, 110, @"The size of the 'SessionKey' buffer in the DIGEST_VALIDATION_RESP Message is specified by the 'SessionKeyLength' field."); // // Verify R257 // Site.CaptureRequirementIfAreEqual <uint>( // If the validation status is success, the essionKey NULL terminator must be 0. 0, (uint)digestResponseStructure.SessionKey_NULL_terminator, 257, @"For the 'SessionKey NULL terminator' field in the DIGEST_VALIDATION_RESP Message, the value MUST be set to 0."); // // Verify R111 // Site.CaptureRequirementIfAreEqual <ulong>( // The expected Pad1 V1 is equal to 0. (ulong)Pad1.V1, (ulong)digestResponseStructure.Pad1, 111, @"'Pad1' field in the DIGEST_VALIDATION_RESP Message is an unused 64-bit unsigned integer. The value of this member MUST be set to zero when sent."); // Judge whether AuthData in response contains PAC. Boolean isAuthDataContainPAC = digestResponseStructure.AuthData.Length > 0 && digestResponseStructure.AuthDataSize == (uint)digestResponseStructure.AuthData.Length; // // Verify R113 // Site.Log.Add( LogEntryKind.Comment, "R113, expected value: {0}, actual value: {1}, the authdata size is {2}.", "true", isAuthDataContainPAC, digestResponseStructure.AuthDataSize.ToString()); // If the validation status is success, the judge variable isAuthDataContainPAC must be true. Site.CaptureRequirementIfIsTrue( isAuthDataContainPAC, 113, @"'AuthData' field in the DIGEST_VALIDATION_RESP Message MUST contain a PACTYPE structure ([MS-PAC])."); // // Verify R100113 // Site.Log.Add(LogEntryKind.Comment, "R100113, expected value: {0}, actual value: {1}, the authdata size is {2}.", "true", isAuthDataContainPAC, digestResponseStructure.AuthDataSize.ToString()); // If the validation status is success, the judge variable isAuthDataContainPAC must be true. Site.CaptureRequirementIfIsTrue( isAuthDataContainPAC, 100113, @"The length of the PACTYPE structure MUST be specified by the 'AuthDataSize' field."); //Get the string of NetBIOS name of user account. string[] str1 = sutDomainName.Split('.'); string NetBIOSUserName = str1[0].ToLower() + "\\" + currentUserName.ToLower() + "\0"; //The string of NetBIOS name of user account in response structure. string actualUserName = Encoding.Unicode.GetString(digestResponseStructure.AccountName).ToLower(); // // Verify R115 // Site.CaptureRequirementIfAreEqual <string>( NetBIOSUserName, actualUserName, 115, @"The length of 'AccountName' [in the DIGEST_VALIDATION_RESP Message] MUST be specified by the 'AcctNameSize' field."); // // Verify R116 // Site.CaptureRequirementIfAreEqual <ushort>( digestResponseStructure.AcctNameSize, (ushort)digestResponseStructure.AccountName.Length, 116, @"The length of 'AccountName'[in the DIGEST_VALIDATION_RESP Message] MUST be specified by the 'AcctNameSize' field."); // // Verify R100171 // Site.CaptureRequirementIfAreEqual <uint>( (uint)Status.Success, digestResponseStructure.Status, 171, @"The DIGEST_VALIDATION_RESP message MUST contain the status of authentication validation"); // // Verify R187 // Site.Log.Add(LogEntryKind.Comment, "R187, whether response structure is null."); // The DIGEST_VALIDATION_RESP structure is a contiguous structure. // If the validation status is success, the response buffer must be not null. Site.CaptureRequirementIfIsNotNull( digestResponseStructure, 187, @"The Digest validation response message DIGEST_VALIDATION_RESP MUST be packed as a contiguous buffer"); // // Verify R189 // Site.CaptureRequirementIfAreEqual <_NETLOGON_VALIDATION_INFO_CLASS>( _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationGenericInfo2, validationlevel, 189, @"The Digest validation response message is sent by using the generic pass-through mechanism, as specified in [MS-NRPC] section 3.2.4.1."); // // Verify R172 //For [MS-APDS] section 2.2.3.2: The AuthData field MUST contain a PACTYPE structure ([MS-PAC] section 2.3). //It means the AuthData field contains the authorization information. //So AuthData field which specify the number of bytes of the AuthData field AuthDataSize is more than 0. // Site.CaptureRequirementIfIsTrue( digestResponseStructure.AuthDataSize > 0, 172, "On success of authentication, the DIGEST_VALIDATION_RESP message MUST also contain the user's authorization information<19>"); // // Verify R100185 //For [MS-APDS] section 2.2.3.2: The AuthData field MUST contain a PACTYPE structure ([MS-PAC] section 2.3). //It means the AuthData field contains the authorization information. //So AuthData field which specify the number of bytes of the AuthData field AuthDataSize is more than 0. Site.CaptureRequirementIfIsTrue( digestResponseStructure.AuthDataSize > 0, 100185, @"If validation is successful, authorization information for the user's account (the PAC) MUST be sent back to the Digest server."); }
/// <summary> /// The NetrLogonSamLogonWithFlags method Supported in windows_xpwindows_server_2003, /// windows_vista, windows_server_2008, windows_7, and /// windows_server_7. handles logon requests for the SAM /// accounts. Opnum: 45 /// </summary> /// <param name="logonServer"> /// The custom RPC binding handle. /// </param> /// <param name="computerName"> /// The Unicode string that contains the NetBIOS name of /// the client computer calling this method. /// </param> /// <param name="authenticator"> /// A pointer to a NETLOGON_AUTHENTICATOR structure, /// that contains the client authenticator. /// </param> /// <param name="returnAuthenticator"> /// A pointer to a NETLOGON_AUTHENTICATOR structure, /// that contains the server return /// authenticator. /// </param> /// <param name="logonLevel"> /// A NETLOGON_LOGON_INFO_CLASS structure, /// that specifies the type of logon information /// passed in the LogonInformation parameter. /// </param> /// <param name="logonInformation"> /// A pointer to a NETLOGON_LEVEL structure, /// that describes the logon request information. /// </param> /// <param name="validationLevel"> /// A NETLOGON_VALIDATION_INFO_CLASS enumerated type, /// that contains the validation /// level requested by the client. /// </param> /// <param name="validationInformation"> /// A pointer to a NETLOGON_VALIDATION structure, /// that describes the user validation information /// returned to the client. The type of the NETLOGON_VALIDATION /// used is determined by the value of the ValidationLevel /// parameter. /// </param> /// <param name="authoritative"> /// A pointer to a char value representing a Boolean condition. /// FALSE is indicated by the value 0x00; TRUE SHOULD be /// indicated by the value 0x01 and MAY also be indicated /// by any nonzero value. Windows uses the value of 0x01 /// as the representation of TRUE and 0x00 for FALSE. This /// Boolean value indicates whether the validation information /// is final. This field is necessary because the request /// might be forwarded through multiple servers. A value /// of TRUE indicates that the validation information is /// final and MUST remain unchanged. /// </param> /// <param name="extraFlags"> /// A pointer to a set of bit flags that specify delivery /// settings. A flag is TRUE (or set) if its value is equal /// to 1. The value is constructed from zero or more bit /// flags from the following table. /// </param> /// <returns> /// The method returns 0x00000000 on success; /// otherwise, it returns a nonzero error code. /// </returns> public NtStatus NetrLogonSamLogonWithFlags( string logonServer, string computerName, _NETLOGON_AUTHENTICATOR? authenticator, ref _NETLOGON_AUTHENTICATOR? returnAuthenticator, _NETLOGON_LOGON_INFO_CLASS logonLevel, _NETLOGON_LEVEL? logonInformation, _NETLOGON_VALIDATION_INFO_CLASS validationLevel, out _NETLOGON_VALIDATION? validationInformation, out byte? authoritative, ref NrpcNetrLogonSamLogonExtraFlags? extraFlags) { uint? flags = (uint?)extraFlags; NtStatus status = rpc.NetrLogonSamLogonWithFlags( logonServer, computerName, authenticator, ref returnAuthenticator, logonLevel, logonInformation, validationLevel, out validationInformation, out authoritative, ref flags); context.ConnectionStatus = status; extraFlags = (NrpcNetrLogonSamLogonExtraFlags?)flags; return status; }
/// <summary> /// The NetrLogonSamLogonEx method Supported in windows_2000_server, /// windows_xp, windows_server_2003, windows_vista, windows_server_2008, /// windows_7, and windows_server_7. provides an extension /// to NetrLogonSamLogon that accepts an extra flags parameter /// and uses Secure RPC ([MS-RPCE]) instead of /// Netlogon authenticators. This method handles logon /// requests for the SAM accounts and allows for generic /// pass-through authentication. /// For more information about fields and structures /// used by Netlogon pass-through methods. /// Opnum: 39 /// </summary> /// <param name="contextHandle"> /// A primitive RPC handle that identifies a particular /// client/server binding. /// </param> /// <param name="logonServer"> /// The null-terminated Unicode string that contains the /// NetBIOS name of the server that will handle the logon /// request. /// </param> /// <param name="computerName"> /// The null-terminated Unicode string that contains the /// NetBIOS name of the client computer sending the logon /// request. /// </param> /// <param name="logonLevel"> /// A NETLOGON_LOGON_INFO_CLASS structure, /// that specifies the type of the logon information /// passed in the LogonInformation parameter. /// </param> /// <param name="logonInformation"> /// A pointer to a NETLOGON_LEVEL structure, /// that describes the logon request information. /// </param> /// <param name="validationLevel"> /// A NETLOGON_VALIDATION_INFO_CLASS enumerated type, /// that contains the validation /// level requested by the client. /// </param> /// <param name="validationInformation"> /// A pointer to a NETLOGON_VALIDATION structure, /// that describes the user validation information /// returned to the client. The type of the NETLOGON_VALIDATION /// used is determined by the value of the ValidationLevel /// parameter. /// </param> /// <param name="authoritative"> /// A pointer to a char value that represents a Boolean /// condition. FALSE is indicated by the value 0x00, and /// TRUE SHOULDwindows uses the value 0x01 as the representation /// of TRUE and 0x00 for FALSE. be indicated by the value /// 0x01 and MAY also be indicated by any nonzero value. /// This Boolean value indicates whether the validation /// information is final. This field is necessary because /// the request might be forwarded through multiple servers. /// The value TRUE indicates that the validation information /// is final and MUST remain unchanged. The Authoritative /// parameter indicates whether the response to this call /// is final or if the same request can be sent to another /// server. The value SHOULD be set to FALSE if the server /// encounters a transient error, and the client can resend /// the request to another server. If the same request /// is known to fail in all subsequent requests, the server /// SHOULD return TRUE. /// </param> /// <param name="extraFlags"> /// A pointer to a set of bit flags that specify delivery /// settings. A flag is TRUE (or set) if its value is equal /// to 1. Output flags MUST be the same as input. The value /// is constructed from zero or more bit flags from the /// following table. /// </param> /// <returns> /// The method returns 0x00000000 on success; /// otherwise, it returns a nonzero error code. /// </returns> public NtStatus NetrLogonSamLogonEx( IntPtr contextHandle, string logonServer, string computerName, _NETLOGON_LOGON_INFO_CLASS logonLevel, _NETLOGON_LEVEL? logonInformation, _NETLOGON_VALIDATION_INFO_CLASS validationLevel, out _NETLOGON_VALIDATION? validationInformation, out byte? authoritative, ref NrpcNetrLogonSamLogonExtraFlags? extraFlags) { uint? flags = (uint?)extraFlags; NtStatus status = rpc.NetrLogonSamLogonEx( contextHandle, logonServer, computerName, logonLevel, logonInformation, validationLevel, out validationInformation, out authoritative, ref flags); context.ConnectionStatus = status; extraFlags = (NrpcNetrLogonSamLogonExtraFlags?)flags; return status; }
/// <summary> /// The NetrLogonSamLogon method This method was used in /// windows_nt_4_0. It was superseded by the NetrLogonSamLogonWithFlags /// method in windows_2000_server, windows_xp, /// windows_server_2003, windows_vista, and windows_server_2008, /// windows_7, and windows_server_7. is a predecessor to /// the NetrLogonSamLogonWithFlags method. All /// parameters of this method have the same meanings as /// the identically named parameters of the NetrLogonSamLogonWithFlags /// method. Opnum: 2 /// </summary> /// <param name="logonServer"> /// LogonServer parameter. /// </param> /// <param name="computerName"> /// ComputerName parameter. /// </param> /// <param name="authenticator"> /// Authenticator parameter. /// </param> /// <param name="returnAuthenticator"> /// ReturnAuthenticator parameter. /// </param> /// <param name="logonLevel"> /// LogonLevel parameter. /// </param> /// <param name="logonInformation"> /// LogonInformation parameter. /// </param> /// <param name="validationLevel"> /// ValidationLevel parameter. /// </param> /// <param name="validationInformation"> /// ValidationInformation parameter. /// </param> /// <param name="authoritative"> /// Authoritative parameter. /// </param> /// <returns> /// The method returns 0x00000000 on success; /// otherwise, it returns a nonzero error code. /// </returns> public NtStatus NetrLogonSamLogon( string logonServer, string computerName, _NETLOGON_AUTHENTICATOR? authenticator, ref _NETLOGON_AUTHENTICATOR? returnAuthenticator, _NETLOGON_LOGON_INFO_CLASS logonLevel, _NETLOGON_LEVEL? logonInformation, _NETLOGON_VALIDATION_INFO_CLASS validationLevel, out _NETLOGON_VALIDATION? validationInformation, out byte? authoritative) { NtStatus status = rpc.NetrLogonSamLogon( logonServer, computerName, authenticator, ref returnAuthenticator, logonLevel, logonInformation, validationLevel, out validationInformation, out authoritative); //ConnectionStatus: A 4-byte value that contains the most recent //connection status return value (section 3.4.5.3.1) last returned //during secure channel establishment or by a method requiring //session key establishment (section 3.1.4.6). context.ConnectionStatus = status; return status; }
/// <summary> /// Decrypt a _NETLOGON_VALIDATION structure based on /// validationInformation parameter in Nrpc NetrlogonSamLogonXXX API. /// </summary> /// <param name="validationInfoClass"> /// Validation Info Class /// </param> /// <param name="validationInformation"> /// Validation info returned from NetrlogonSamLogonXXX API /// </param> /// <returns>Decrypted _NETLOGON_VALIDATION data structure</returns> /// <exception cref="InvalidOperationException"> /// Thrown when the method is called before establishing a NRPC secure channel. /// </exception>> /// <exception cref="ArgumentException"> /// Thrown when input invalid validationInformation argument /// </exception>> public _NETLOGON_VALIDATION DecryptNetlogonValidation( _NETLOGON_VALIDATION_INFO_CLASS validationInfoClass, _NETLOGON_VALIDATION validationInformation) { ValidateSecureChannelExists(); switch (validationInfoClass) { case _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationGenericInfo: case _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationGenericInfo2: //If the LogonLevel is NetlogonGenericInformation, //then encrypt<104> the LogonData member in the //NETLOGON_GENERIC_INFO structure. if (validationInformation.ValidationGeneric2 == null || validationInformation.ValidationGeneric2.Length != 1) { throw new ArgumentException("validationInformation is invalid.", "validationInformation"); } if (validationInformation.ValidationGeneric2[0].ValidationData == null) { throw new ArgumentException("validationInformation is invalid.", "validationInformation"); } validationInformation.ValidationGeneric2[0].ValidationData = NrpcUtility.DecryptBuffer( (context.NegotiateFlags & NrpcNegotiateFlags.SupportsAESAndSHA2) == NrpcNegotiateFlags.SupportsAESAndSHA2, context.SessionKey, validationInformation.ValidationGeneric2[0].ValidationData); break; //If the LogonLevel is NetlogonNetworkInformation or //NetlogonNetworkTransitiveInformation, then encrypt the //UserSessionKey and the first two elements of the //ExpansionRoom array in the NETLOGON_VALIDATION_SAM_INFO //(section 2.2.1.4.11) or in the NETLOGON_VALIDATION_SAM_INFO2 //(section 2.2.1.4.12) structure. case _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo: if (validationInformation.ValidationSam == null || validationInformation.ValidationSam.Length != 1) { throw new ArgumentException("validationInformation is invalid.", "validationInformation"); } validationInformation.ValidationSam[0].UserSessionKey = DecryptUserSessionKey(validationInformation.ValidationSam[0].UserSessionKey); validationInformation.ValidationSam[0].ExpansionRoom = DecryptExpansionRoom(validationInformation.ValidationSam[0].ExpansionRoom); break; case _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo2: if (validationInformation.ValidationSam2 == null || validationInformation.ValidationSam2.Length != 1) { throw new ArgumentException("validationInformation is invalid.", "validationInformation"); } validationInformation.ValidationSam2[0].UserSessionKey = DecryptUserSessionKey(validationInformation.ValidationSam2[0].UserSessionKey); validationInformation.ValidationSam2[0].ExpansionRoom = DecryptExpansionRoom(validationInformation.ValidationSam2[0].ExpansionRoom); break; case _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationSamInfo4: case _NETLOGON_VALIDATION_INFO_CLASS.NetlogonValidationUasInfo: // no encrypted field break; default: throw new ArgumentException("validationInfoClass is invalid.", "validationInfoClass"); } return validationInformation; }