08 March, 2010

NTLM Authentication просто и со вкусом

Я довольно долго искал простой и стандартный путь для того чтобы произвести аутентификацию пользователя (текущий пользователь - вы не знаете его пароля и пользователь заданный при помощи NetworkCredential) на удаленном компьютере. Хотел уже было все написать ручками через AcquireCredentialsHandle/InitializeSecurityContext/AcceptSecurityContext/CompleteAuthToken, но совсем неожиданно нашел NegotiateStream - который и занимается данным непростым процессом. По сему поводу написал маленький примерчик (пример работает только в пределах одного компьютера, если хотите большего, то замените NamedPipeXxxStream на Socket).


namespace AuthTest
{
  public static class Constants
  {
    public const string PipeName = "AuthTest";
  }
}
using System;
using System.IO.Pipes;
using System.Net;
using System.Net.Security;
using System.Security.Principal;
 
namespace AuthTest.Client
{
  internal class Program
  {
    private static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("AuthTest Client Copyright (C) 2010 Mikhail Pilin");
        using (var stream = new NamedPipeClientStream(Constants.PipeName))
        {
          stream.Connect();
          using (var negotiateStream = new NegotiateStream(stream, true))
            negotiateStream.AuthenticateAsClient(
              GetCredential(args),
              "",
              ProtectionLevel.EncryptAndSign,
              TokenImpersonationLevel.Delegation);
        }
 
        Console.WriteLine("Done");
      }
      catch (Exception e)
      {
        Console.Error.WriteLine(e);
      }
    }
 
    private static NetworkCredential GetCredential(string[] args)
    {
      switch (args.Length)
      {
      case 0:
        return (NetworkCredential) CredentialCache.DefaultCredentials;
      case 1:
        return new NetworkCredential(args[0], "");
      case 2:
        return new NetworkCredential(args[0], args[1]);
      case 3:
        return new NetworkCredential(args[0], args[1], args[2]);
      default:
        throw new ArgumentException();
      }
    }
  }
}
using System;
using System.IO.Pipes;
using System.Net.Security;
using System.Security.Principal;
 
namespace AuthTest.Server
{
  internal class Program
  {
    private static void Main()
    {
      try
      {
        Console.WriteLine("AuthTest Server Copyright (C) 2010 Mikhail Pilin");
        using (var stream = new NamedPipeServerStream(Constants.PipeName))
        {
          stream.WaitForConnection();
          using (var negotiateStream = new NegotiateStream(stream, true))
          {
            negotiateStream.AuthenticateAsServer();
            var remoteIdentity = (WindowsIdentity) negotiateStream.RemoteIdentity;
            Console.WriteLine("Name: {0}", remoteIdentity.Name);
            Console.WriteLine("User: {0}", remoteIdentity.User);
            Console.WriteLine("AuthenticationType: {0}", remoteIdentity.AuthenticationType);
            Console.WriteLine("ImpersonationLevel: {0}", remoteIdentity.ImpersonationLevel);
            Console.WriteLine("IsAuthenticated: {0}", remoteIdentity.IsAuthenticated);
            Console.WriteLine("IsAnonymous: {0}", remoteIdentity.IsAnonymous);
            Console.WriteLine("IsSystem: {0}", remoteIdentity.IsSystem);
            Console.WriteLine("IsGuest: {0}", remoteIdentity.IsGuest);
          }
        }
        Console.WriteLine("Done");
      }
      catch (Exception e)
      {
        Console.Error.WriteLine(e);
      }
    }
  }
}

No comments: