30 March, 2010

Своя личная NTLM authentication в remoting (Часть №1)

Задача поставлена, теперь как бы ее так реализовать с использованием того-же NegotiateStream но уже поверх remoting. Сперва наперво должно прийти понимание, что наш NegotiateStream будет работать только с некоторыми стримами: NetworkStream или PipeStream. FileStream, MemoryStream не годятся категорически. Можно еще нагородить что-то свое, но это довольно долго. Сперва напишем интерфейс общения клиента и сервера.


Пусть он будет такой:

public interface IAuthenticationNTLM : IDisposable
{
  Stream Stream { get; }
 
  void BeginOperation();
  object EndOperation();
}

А протокол по этому интерфейсу пусть будет такой: клиент берет Stream и создает свою копию NegotiateStream затем вызывает BeginOperation и AuthenticateAsClient для своего NegotiateStream а в конце EndOperation и получает объект к которому он так стремился.

using (var negotiateStream = new NegotiateStream(new WriteCopyStream(ntlmAuthentication.Stream)))
{
  ntlmAuthentication.BeginOperation();
  negotiateStream.AuthenticateAsClient(credential, "", ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Delegation);
  obj = ntlmAuthentication.EndOperation();
}

Обратите внимание на некий WriteCopyStream - это класс прокладка, задача которой иззолировать клиентские врутненние классы NegotiateStream от попыток remoting передать их на сторону сервера, что приводит к гарантированному exception.

private sealed class WriteCopyStream : Stream
{
  private readonly Stream myStream;
 
  public WriteCopyStream([NotNull] Stream stream)
  {
    if (stream == null)
      throw new ArgumentNullException("stream");
    myStream = stream;
  }
 
  public override bool CanRead { get { return myStream.CanRead; } }
  public override bool CanSeek { get { return myStream.CanSeek; } }
  public override bool CanWrite { get { return myStream.CanWrite; } }
  public override long Length { get { return myStream.Length; } }
  public override long Position { get { return myStream.Position; } set { myStream.Position = value; } }
 
  public override void Flush()
  {
    myStream.Flush();
  }
 
  public override long Seek(long offset, SeekOrigin origin)
  {
    return myStream.Seek(offset, origin);
  }
 
  public override void SetLength(long value)
  {
    myStream.SetLength(value);
  }
 
  public override int Read(byte[] buffer, int offset, int count)
  {
    return myStream.Read(buffer, offset, count);
  }
 
  public override void Write(byte[] buffer, int offset, int count)
  {
    myStream.Write((byte[]) buffer.Clone(), offset, count);
  }
}

С следующей части расскажу о том как устроен сервер...

No comments: