/// <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> /// Validate NL_AUTH_MESSAGE when the server receives token from client /// MessageType is not set to 0x00000000. /// contains at least one domain name and one computer name /// </summary> /// <param name="inToken">token from client</param> /// <returns>True if validate pass; otherwise, false</returns> /// <exception cref="ArgumentException">Thrown when length of inToken is not large enough.</exception> private bool ValidateNlAuthMessage(byte[] inToken) { if (inToken.Length <= (sizeof(MessageType_Values) + sizeof(NL_AUTH_MESSAGE_Flags_Value))) { throw new ArgumentException("The token is invalid", "inToken"); } NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE(); // convert inToken to a NL_AUTH_MESSAGE structure int offset = 0; nlAuthMessage.MessageType = (MessageType_Values)BitConverter.ToInt32(inToken, offset); offset += sizeof(MessageType_Values); 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); // check message type if (nlAuthMessage.MessageType != MessageType_Values.NegotiateRequest) { return false; } // check domain name and computer name, must present both string domainName = null; string computerName = null; List<string> nameList = new List<string>(); foreach (string name in Encoding.ASCII.GetString(nlAuthMessage.Buffer).Split(NULL)) { if (!string.IsNullOrEmpty(name)) { nameList.Add(name); } } if (nameList.Count == 0) { return false; } int index = 0; if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.NetbiosOemDomainName) != 0 && index < nameList.Count) { domainName = nameList[index++]; } if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.NetbiosOemComputerName) != 0 && index < nameList.Count) { computerName = nameList[index++]; } if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.DnsCompressedDomainName) != 0 && index < nameList.Count) { domainName = Rfc1035Utility.FromCompressedUtf8String(Encoding.ASCII.GetBytes(nameList[index++])); } if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.DnsCompressedHostName) != 0 && index < nameList.Count) { computerName = Rfc1035Utility.FromCompressedUtf8String(Encoding.ASCII.GetBytes(nameList[index++])); } if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.NetbiosCompressedComputerName) != 0 && index < nameList.Count) { computerName = Rfc1035Utility.FromCompressedUtf8String(Encoding.ASCII.GetBytes(nameList[index++])); } if (domainName == null || computerName == null) { return false; } return true; }
/// <summary> /// Create an instance of NL_AUTH_MESSAGE. /// </summary> /// <returns>Created NL_AUTH_MESSAGE structure.</returns> /// <exception cref="InvalidOperationException"> /// Thrown when the method is called before establishing a NRPC secure channel. /// </exception> public NL_AUTH_MESSAGE CreateNlAuthMessage() { const char NULL = '\0'; ValidateSecureChannelExists(); NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE(); nlAuthMessage.MessageType = MessageType_Values.NegotiateRequest; if (context.DomainName.Contains(".")) { // context.DomainName is a DNS name. nlAuthMessage.Flags = NL_AUTH_MESSAGE_Flags_Value.NetbiosOemComputerName | NL_AUTH_MESSAGE_Flags_Value.DnsCompressedDomainName; nlAuthMessage.Buffer = ArrayUtility.ConcatenateArrays( Encoding.ASCII.GetBytes(context.ClientComputerName + NULL), Rfc1035Utility.ToCompressedUtf8String(context.DomainName)); } else { // context.DomainName is a NetBIOS name. nlAuthMessage.Flags = NL_AUTH_MESSAGE_Flags_Value.NetbiosOemDomainName | NL_AUTH_MESSAGE_Flags_Value.NetbiosOemComputerName; nlAuthMessage.Buffer = ArrayUtility.ConcatenateArrays( Encoding.ASCII.GetBytes(context.DomainName + NULL), Encoding.ASCII.GetBytes(context.ClientComputerName + NULL)); } return nlAuthMessage; }
/// <summary> /// Create an instance of NL_AUTH_MESSAGE. /// </summary> /// <returns>Created NL_AUTH_MESSAGE structure</returns> private NL_AUTH_MESSAGE CreateNlAuthMessage() { NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE(); nlAuthMessage.MessageType = MessageType_Values.NegotiateResponse; nlAuthMessage.Flags = (NL_AUTH_MESSAGE_Flags_Value)0; nlAuthMessage.Buffer = BitConverter.GetBytes(NULL); return nlAuthMessage; }
/// <summary> /// Validate NL_AUTH_MESSAGE when the client receives the return token. /// </summary> /// <param name="nlAuthMessage">NL_AUTH_MESSAGE token.</param> /// <returns>True if validate pass; otherwise, false.</returns> /// <exception cref="InvalidOperationException"> /// Thrown when the method is called before establishing a NRPC secure channel. /// </exception> public bool ValidateNlAuthMessage(NL_AUTH_MESSAGE nlAuthMessage) { ValidateSecureChannelExists(); //When the client receives the return token, it verifies that: //the NL_AUTH_MESSAGE token is at least 12 bytes in length, and //the MessageType is set to 1. if (nlAuthMessage.MessageType == MessageType_Values.NegotiateResponse) { //NL_AUTH_MESSAGE is always at least 12 bytes. //the client initializes ClientSequenceNumber to 0, //which is used to detect out-of-order messages. context.SequenceNumber = 0; return true; } else { return false; } }