/// <summary> /// Initialize an instance of NrpcClientSecurityContext class. /// By calling this constructor, the class will setup a new secure /// channel between client and server. /// </summary> /// <param name="domainName"> /// The NRPC domain name. /// </param> /// <param name="serverName"> /// The NRPC server name. /// </param> /// <param name="credential"> /// The credential to setup the secure channel. /// </param> /// <param name="requestConfidentiality"> /// A Boolean setting that indicates that the caller is requiring /// encryption of messages so that they cannot be read while in transit. /// Requesting this service results in Netlogon encrypting the message. /// </param> /// <param name="clientCapabilities"> /// The client capability. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when domainName, serverName or credential is null. /// </exception> public NrpcClientSecurityContext( string domainName, string serverName, MachineAccountCredential credential, bool requestConfidentiality, NrpcNegotiateFlags clientCapabilities) { if (string.IsNullOrWhiteSpace(domainName)) { throw new ArgumentNullException("domainName cannot be null or empty"); } if (string.IsNullOrWhiteSpace(serverName)) { throw new ArgumentNullException("serverName cannot be null or empty"); } if (credential == null) { throw new ArgumentNullException("credential cannot be null"); } this.nrpc = NrpcClient.CreateNrpcClient(domainName); this.nrpc.Context.PrimaryName = serverName; this.nrpc.Context.SealSecureChannel = requestConfidentiality; this.nrpc.Context.NegotiateFlags = clientCapabilities; this.credential = credential; this.secureChannelType = _NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel; }
/// <summary> /// Initializes a new instance of the NrpcCustomClientSecurityContext class. By calling this constructor, /// the class will setup a new secure channel between client and server. /// </summary> /// <param name="domainName">The NRPC domain name.</param> /// <param name="serverName"> The NRPC server name.</param> /// <param name="credential">The credential to setup the secure channel.</param> /// <param name="requestConfidentiality">A Boolean setting that indicates that the caller is requiring /// encryption of messages so that they cannot be read while in transit. /// Requesting this service results in Netlogon encrypting the message.</param> /// <param name="clientCapabilities">The client capability.</param> public NrpcCustomClientSecurityContext( string domainName, string serverName, MachineAccountCredential credential, bool requestConfidentiality, NrpcNegotiateFlags clientCapabilities) : base(domainName, serverName, credential, requestConfidentiality, clientCapabilities) { }
/// <summary> /// Initialize method is not used for NRPC SSPI.<para/> /// NRPC SSPI will negotiate security context in its own RPC call. /// </summary> /// <param name="inToken"> /// A token returned from server SSPI; /// if it's set to null, indicates to initialize a new client token. /// </param> /// <exception cref="SspiException"> /// Thrown when server returned token is invalid. /// </exception> public override void Initialize(byte[] inToken) { if (inToken == null) { //Initialize a new token. if (nrpc.Context.SessionKey == null) { //Negotiate a session key. NrpcNegotiateFlags clientCapabilities = nrpc.Context.NegotiateFlags; ushort[] nrpcTcpEndpoints = NrpcUtility.QueryNrpcTcpEndpoint(nrpc.Context.PrimaryName); if (nrpcTcpEndpoints == null || nrpcTcpEndpoints.Length == 0) { throw new InvalidOperationException("Server doesn't support NRPC protocol."); } nrpc.BindOverTcp( nrpc.Context.PrimaryName, nrpcTcpEndpoints[0], null, timeout); nrpc.NetrServerReqChallenge(credential.MachineName); nrpc.NetrServerAuthenticate3( credential.AccountName, this.secureChannelType, ref clientCapabilities, credential.Password); } NL_AUTH_MESSAGE nlAuthMessage = nrpc.CreateNlAuthMessage(); this.token = ArrayUtility.ConcatenateArrays( BitConverter.GetBytes((uint)nlAuthMessage.MessageType), BitConverter.GetBytes((uint)nlAuthMessage.Flags), nlAuthMessage.Buffer); this.needContinueProcessing = true; } else { this.token = null; this.needContinueProcessing = false; NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE(); int offset = 0; nlAuthMessage.MessageType = (MessageType_Values)BitConverter.ToInt32(inToken, offset); offset += sizeof(int); // cannot call: Marshal.SizeOf(nlAuthMessage.MessageType); nlAuthMessage.Flags = (NL_AUTH_MESSAGE_Flags_Value)BitConverter.ToUInt32(inToken, offset); offset += sizeof(NL_AUTH_MESSAGE_Flags_Value); nlAuthMessage.Buffer = ArrayUtility.SubArray(inToken, offset, inToken.Length - offset); if (!nrpc.ValidateNlAuthMessage(nlAuthMessage)) { //validate server returned token failed. throw new SspiException("Server returned token is invalid."); } } }
/// <summary> /// Initialize an instance of NrpcClientSecurityContext class. /// By calling this constructor, the class will setup a new secure /// channel between client and server. /// </summary> /// <param name="domainName"> /// The NRPC domain name. /// </param> /// <param name="serverName"> /// The NRPC server name. /// </param> /// <param name="credential"> /// The credential to setup the secure channel. /// </param> /// <param name="requestConfidentiality"> /// A Boolean setting that indicates that the caller is requiring /// encryption of messages so that they cannot be read while in transit. /// Requesting this service results in Netlogon encrypting the message. /// </param> /// <param name="clientCapabilities"> /// The client capability. /// </param> /// <param name="secureChannelType"> /// the type of secure channel to use in a logon transaction. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when domainName, serverName or credential is null. /// </exception> public NrpcClientSecurityContext( string domainName, string serverName, MachineAccountCredential credential, bool requestConfidentiality, NrpcNegotiateFlags clientCapabilities, _NETLOGON_SECURE_CHANNEL_TYPE secureChannelType ) : this(domainName, serverName, credential, requestConfidentiality, clientCapabilities) { this.secureChannelType = secureChannelType; }
/// <summary> /// The NetrServerAuthenticate3 method mutually authenticates /// the client and the server and establishes the session /// key to be used for the secure channel message protection /// between the client and the server. Supported in windows_2000_server, /// windows_xp, windows_server_2003, windows_vista, windows_server_2008, /// windows_7, and windows_server_7. It is called after /// the NetrServerReqChallenge method. /// Opnum: 26 /// </summary> /// <param name="primaryName"> /// The custom RPC binding handle. /// </param> /// <param name="accountName"> /// A null-terminated Unicode string that identifies the /// name of the account that contains the secret key (password) /// that is shared between the client and the server. /// In windows, all machine account /// names are the name of the machine with a $ (dollar /// sign) appended. If there is a period at the end of /// the account name, it is ignored during processing. /// </param> /// <param name="secureChannelType"> /// A NETLOGON_SECURE_CHANNEL_TYPE enumerated value, /// that indicates the type of the /// secure channel being established by this call. /// </param> /// <param name="computerName"> /// A null-terminated Unicode string that contains the NetBIOS /// name of the client computer calling this method. /// </param> /// <param name="clientCredential"> /// A pointer to a NETLOGON_CREDENTIAL structure, /// that contains the supplied client credentials. /// </param> /// <param name="serverCredential"> /// A pointer to a NETLOGON_CREDENTIAL structure, /// that contains the returned server credentials. /// </param> /// <param name="negotiateFlags"> /// A pointer to a 32-bit set of bit flags that indicate /// features supported. As input, the set of flags are /// those requested by the client and SHOULD be the same /// as ClientCapabilities. As output, they are the bit-wise /// AND of the client's requested capabilities and the /// server's ServerCapabilities. /// </param> /// <param name="accountRid"> /// A pointer that receives the RID of the account specified /// by the AccountName parameter. ([MS-ADTS] /// describes how this RID is assigned at account creation /// time.) This value is stored in the AccountRid ADM element /// within the ClientSessionInfo table. /// </param> /// <returns> /// The method returns 0x00000000 on success; /// otherwise, it returns a nonzero error code. /// </returns> public NtStatus NetrServerAuthenticate3( string primaryName, string accountName, _NETLOGON_SECURE_CHANNEL_TYPE secureChannelType, string computerName, _NETLOGON_CREDENTIAL? clientCredential, out _NETLOGON_CREDENTIAL? serverCredential, ref NrpcNegotiateFlags? negotiateFlags, out uint? accountRid) { context.PrimaryName = primaryName; context.AccountName = accountName; context.SecureChannelType = secureChannelType; context.ClientComputerName = computerName; if (clientCredential != null) { context.StoredCredential = clientCredential.Value.data; } if (negotiateFlags != null) { context.NegotiateFlags = (NrpcNegotiateFlags)negotiateFlags.Value; } uint? flags = (uint?)negotiateFlags; NtStatus status = rpc.NetrServerAuthenticate3( primaryName, accountName, secureChannelType, computerName, clientCredential, out serverCredential, ref flags, out accountRid); context.ConnectionStatus = status; if (status == NtStatus.STATUS_SUCCESS) { negotiateFlags = (NrpcNegotiateFlags?)flags; if (negotiateFlags != null) { context.NegotiateFlags = (NrpcNegotiateFlags)negotiateFlags.Value; } if (accountRid != null) { context.AccountRid = accountRid.Value; } if (context.SessionKey == null && context.SharedSecret != null && context.ClientChallenge != null && context.ServerChallenge != null) { if ((context.NegotiateFlags & NrpcNegotiateFlags.SupportsAESAndSHA2) != 0) { context.SessionKey = NrpcUtility.ComputeSessionKey( NrpcComputeSessionKeyAlgorithm.HMACSHA256, context.SharedSecret, context.ClientChallenge, context.ServerChallenge); } else if ((context.NegotiateFlags & NrpcNegotiateFlags.SupportsStrongKeys) != 0) { context.SessionKey = NrpcUtility.ComputeSessionKey( NrpcComputeSessionKeyAlgorithm.MD5, context.SharedSecret, context.ClientChallenge, context.ServerChallenge); } else { context.SessionKey = NrpcUtility.ComputeSessionKey( NrpcComputeSessionKeyAlgorithm.DES, context.SharedSecret, context.ClientChallenge, context.ServerChallenge); } } } return status; }
/// <summary> /// Call NetrServerAuthenticate3, get necessary information from context. /// </summary> /// <param name="accountName"> /// A null-terminated Unicode string that identifies the name of /// the account that contains the secret key (password) that is shared /// between the client and the server. /// </param> /// <param name="secureChannelType"> /// A NETLOGON_SECURE_CHANNEL_TYPE enumerated value, /// that indicates the type of the /// secure channel being established by this call. /// </param> /// <param name="clientCapabilities"> /// A pointer to a 32-bit set of bit flags in little-endian format that /// indicate features supported. As input, the set of flags are those /// requested by the client and SHOULD be the same as ClientCapabilities. /// As output, they are the bit-wise AND of the client's requested capabilities /// and the server's ServerCapabilities. /// </param> /// <param name="sharedSecret"> /// An even-numbered sequence of bytes, with no embedded zero values, /// that is a plain-text secret (password) shared between the client and the server. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when account or sharedSecret is null. /// </exception> /// <exception cref="InvalidOperationException"> /// Thrown when server returned an error code or /// server returned credential validation failed. /// </exception> public void NetrServerAuthenticate3( string accountName, _NETLOGON_SECURE_CHANNEL_TYPE secureChannelType, ref NrpcNegotiateFlags clientCapabilities, string sharedSecret) { if (accountName == null) { throw new ArgumentNullException("accountName"); } if (sharedSecret == null) { throw new ArgumentNullException("sharedSecret"); } context.SharedSecret = sharedSecret; NrpcComputeSessionKeyAlgorithm computeSessionKeyAlgorithm; NrpcComputeNetlogonCredentialAlgorithm computeNetlogonCredentialAlgorithm; if ((clientCapabilities & NrpcNegotiateFlags.SupportsAESAndSHA2) == NrpcNegotiateFlags.SupportsAESAndSHA2) { computeSessionKeyAlgorithm = NrpcComputeSessionKeyAlgorithm.HMACSHA256; computeNetlogonCredentialAlgorithm = NrpcComputeNetlogonCredentialAlgorithm.AES128; } else if ((clientCapabilities & NrpcNegotiateFlags.SupportsStrongKeys) == NrpcNegotiateFlags.SupportsStrongKeys) { computeSessionKeyAlgorithm = NrpcComputeSessionKeyAlgorithm.MD5; computeNetlogonCredentialAlgorithm = NrpcComputeNetlogonCredentialAlgorithm.DESECB; } else { computeSessionKeyAlgorithm = NrpcComputeSessionKeyAlgorithm.DES; computeNetlogonCredentialAlgorithm = NrpcComputeNetlogonCredentialAlgorithm.DESECB; } context.SessionKey = NrpcUtility.ComputeSessionKey( computeSessionKeyAlgorithm, context.SharedSecret, context.ClientChallenge, context.ServerChallenge); _NETLOGON_CREDENTIAL clientCredential = new _NETLOGON_CREDENTIAL(); clientCredential.data = NrpcUtility.ComputeNetlogonCredential( computeNetlogonCredentialAlgorithm, context.ClientChallenge, context.SessionKey); _NETLOGON_CREDENTIAL? serverCredential; NrpcNegotiateFlags? flags = clientCapabilities; uint? accountRid; NtStatus status = NetrServerAuthenticate3( context.PrimaryName, accountName, secureChannelType, context.ClientComputerName, clientCredential, out serverCredential, ref flags, out accountRid); if (status != NtStatus.STATUS_SUCCESS) { NrpcUtility.ThrowExceptionOnStatus("NetrServerAuthenticate3", (int)status); } if (serverCredential == null || !ArrayUtility.CompareArrays( serverCredential.Value.data, NrpcUtility.ComputeNetlogonCredential( computeNetlogonCredentialAlgorithm, context.ServerChallenge, context.SessionKey))) { NrpcUtility.ThrowExceptionOnValidationFail("NetrServerAuthenticate3"); } }
/// <summary> /// The NetrServerAuthenticate2 method This method was used /// in windows_nt_3_5 and windows_nt_4_0. In windows_2000_server, /// windows_xp, windows_server_2003, windows_vista, windows_server_2008, /// windows_7, and windows_server_7, it was superseded /// by the NetrServerAuthenticate3 method. is /// a predecessor to the NetrServerAuthenticate3 method. /// All parameters of this method /// have the same meanings as the identically named parameters /// of the NetrServerAuthenticate3 method. Opnum: 15 /// </summary> /// <param name="primaryName"> /// PrimaryName parameter. /// </param> /// <param name="accountName"> /// AccountName parameter. /// </param> /// <param name="secureChannelType"> /// SecureChannelType parameter. /// </param> /// <param name="computerName"> /// ComputerName parameter. /// </param> /// <param name="clientCredential"> /// ClientCredential parameter. /// </param> /// <param name="serverCredential"> /// ServerCredential parameter. /// </param> /// <param name="negotiateFlags"> /// NegotiateFlags parameter. /// </param> /// <returns> /// The method returns 0x00000000 on success; /// otherwise, it returns a nonzero error code. /// </returns> public NtStatus NetrServerAuthenticate2( string primaryName, string accountName, _NETLOGON_SECURE_CHANNEL_TYPE secureChannelType, string computerName, _NETLOGON_CREDENTIAL? clientCredential, out _NETLOGON_CREDENTIAL? serverCredential, ref NrpcNegotiateFlags? negotiateFlags) { uint? flags = (uint?)negotiateFlags; NtStatus status = rpc.NetrServerAuthenticate2( primaryName, accountName, secureChannelType, computerName, clientCredential, out serverCredential, ref flags); context.ConnectionStatus = status; negotiateFlags = (NrpcNegotiateFlags?)flags; return status; }