Ejemplo n.º 1
0
        /// <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>
        /// 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.");
        }