Code Monkey home page Code Monkey logo

nsspi's Introduction

Downloads

The latest release of NSspi is v0.3.1, released 5-Aug-2019.

Version 0.3.1 adds support to obtain an IIdentity/WindowsPrinciple representing the remote connection. This is useful for servers that wish to query the properties on the principle, such as claims.

You can also browse the list of releases.

Introduction

This project provides a C# / .Net interface to the Windows Integrated Authentication API, better known as SSPI (Security Service Provider Interface). This allows a custom client / server system to authenticate users using their existing logon credentials. This allows a developer to provide Single-Sign-On in their application.

Overview

The API provides raw access to authentication tokens so that authentication can be easily integrated into any networking system - you can send the tokens over a socket, a remoting interface, or heck even a serial port if you want; they're just bytes. Clients and servers may exchange encrypted and signed messages, and the server can perform client impersonation.

The project is provided as a .Net 4.0 assembly, but can just as easily be upgraded to .Net 4.5 or later. The solution file can be opened by Visual Studio 2010 SP1, Visual Studio 2012, or later Visual Studio editions.

The SSPI API provides an interface for real authentication protocols, such as Kerberos or NTLM, to be invoked transparently by client and server code in order to perform authentication and message manipulation. These authentication protocols are better known as 'security packages'.

The SSPI API exposes these packages using a common API, and so a program may invoke one or the other with only minor changes in implementation. SSPI also supports the 'negotiate' 'meta' package, that allows a client and server to decide dynamically which real security provider to use, and then itself provides a passthrough interface to the real package.

Usage

Typically, a client acquires some form of a credential, either from the currently logged on user's security context, by acquiring a username and password from the user, or by some other means. The server acquires a credential in a similar manner. Each uses their credentials to identify themselves to each other.

A client and a server each start with uninitialized security contexts. They exchange negotiation and authentication tokens to perform authentication, and if all succeeds, they create a shared security context in the form of a client's context and a server's context. The effectively shared context agrees on the security package to use (kerberos, NTLM), and what parameters to use for message passing. Every new client that authenticates with a server creates a new security context specific to that client-server pairing.

From the software perspective, a client security context initializes itself by exchanging authentication tokens with a server; the server initializes itself by exchanging authentication tokens with the client.

This API provides raw access to the authentication tokens created during the negotiation and authentication process. In this manner, any application can integrate SSPI-based authentication by deciding for themselves how to integrate the tokens into their application protocol.

The project is broken up into 3 chunks:

  • The NSspi library, which provides safe, managed access to the SSPI API.
  • NsspiDemo, a quick demo program to show how to exercise the features of NSspi locally.
  • UI demo programs TestClient and TestServer (that have a common dependency on TestProtocol) that may be run on separate machines, that show how one might integrate SSPI into a custom application.

More information

If you would like to understand the SSPI API, feel free to browse the following references:

MSDN documentation on the SSPI API:
      http://msdn.microsoft.com/en-us/library/windows/desktop/aa374731(v=vs.85).aspx

MSDN article on SSPI along with a sample Managed C++ SSPI library and UI client/servers.
      http://msdn.microsoft.com/en-us/library/ms973911.aspx

Relevant StackOverflow questions:

nsspi's People

Contributors

antiduh avatar matt-sullivan avatar mcgov avatar stefanossendorf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nsspi's Issues

Enforcing Kerberos only (using PackageNames.Kerberos)

I am trying to use PackageNames.Kerberos instead of PackageNames.Negotiate in order to only accept Kerberos authentication.

Ideally, I'd like to do this in a client-agnostic way, where I still return WWW-Authenticate: Negotiate and then I parse the Negotiate <token> header and pass it to AcceptToken, but with PackageNames.Kerberos.

I tried exactly that, but I'm getting Failed to call AcceptSecurityContext. Error Code = '0x80090300' - \"Not enough memory.\"."

Could I get a clarification of the exact use-case for PackageNames.Kerberos and whether what I'm trying to do is supposed to fail by design ? Thanks in advance :)

"Context not yet fully formed" exceptions

From time to time, I can see The context is not yet fully formed. exceptions being thrown when trying to access ServerContext.ContextUserName property.

The property is being accessed (for logging purposes) immediately after AcceptToken is called and successful.

Interestingly, the next thing after that logging call is an ImpersonateClient() call which always works.

Is there any hidden race condition/concurrency I'm perhaps not aware of here?

Here's an illustration of what my code is doing:

var status= ctx.AcceptToken(negotiateToken), out var serverToken);
LogTokenAccepted(ctx) // ctx.ContextUserName is being accessed here
using (ctx.ImpersonateClient()) { ... } // This never fails

Trouble with NTLM Proxy Authentication

I've been trying to implement this library to perform NTLM Proxy Authentication, and am running into a peculiar issue.

The environment setup is:

  1. Both the client machine and the proxy are on the same domain.
  2. The proxy supports Kerberos, NTLM, and Negotiate.

If negotiate is used and selects Kerberos, everything works fine.

However, if I try to force NTLM as the package, the final token gets rejected by the proxy. When testing the code at other sites where NTLM is the only supported option, the handshake also fails in the same way.

My gut instinct is something is wrong with the credential handle or the crypto functions for generating the 3rd token. When I loaded up a token analysis tool, all of the flags and target information looks identical to other apps' tokens which do seem to work.

Initializing ClientContext while impersonating

Hi,

I am trying to use NSSPI to manually facilitate double-hop with Kerberos inside a custom OWIN and IIS based reverse-proxy/API Gateway.

My code looks something like this:

var identity = (WindowsIdentity) context.Request.User.Identity;
using (var impersonation = identity.Impersonate())
{
    var clientCredentials = new ClientCurrentCredential(PackageNames.Kerberos);
    var client = new ClientContext(
        clientCredentials,
        "HTTP/" + backendHostname, // e.g. HTTP/myapi.mydomain.com
        ContextAttrib.MutualAuth |
        ContextAttrib.InitIdentify |
        ContextAttrib.Confidentiality |
        ContextAttrib.ReplayDetect |
        ContextAttrib.SequenceDetect |
        ContextAttrib.Connection |
        ContextAttrib.Delegate
    );
    var clientStatus = client.Init(null, out var tokenBytes);
    var token = Convert.ToBase64String(tokenBytes);
    context.Request.Headers.Append("Negotiate", token);
}

However the client.Init() call fails saying No credentials are available in the security package. Without the impersonation context, I'm able to get a token just fine. The SPNs should be set up correctly, and the app that's doing the impersonation is trusted in AD for Kerberos delegation. The ImpersonationLevel of the impersonated identity is Impersonation not Delegation.

I was hoping someone here might have a better insight into this and maybe see what I might be doing wrong, thanks.

Authentication with AZURE AD fails

Hello,
nsspi works fine for domain joined user / machines.
Recently our company has switched to AZURE AD. All PCs are now 'AzureAdJoined' and all user are now managed.
With this configuration authentication doesn't work anymore. TestServer always fails with LogonDenied (0x8009030c).
How can this be solved?
Thank you.

Delegation after impersonation

Hello,

I have been using the NSSPI lib succesfully to exchange keys invoking server's AcceptToken() until SspiCommon.Status.Ok state reached.

Upon that, I invoke ImpersonateClient() and try to delgate this security context further via WCF (httpTransport.AuthenticationScheme = AuthenticationSchemes.Negotiate;)
DC/Kerberos are all set up, all users have been granted domain Admin priviledges, all users have SPNs so that delegation can be enabled, all computers have delegations enabled.

However, when I invoke WindowsIdentity.GetCurrent().ImpersonationLevel from within Impersonation block I get "Identification" only.

Both server and client context required:
ContextAttrib.MutualAuth |
ContextAttrib.AcceptIdentify |
ContextAttrib.Confidentiality |
ContextAttrib.ReplayDetect |
ContextAttrib.SequenceDetect |
ContextAttrib.Delegate,

Any ideas? Can this work at all? Do I have to dive into SPN management to get this running?

Thanks
Vladimir

GSSAPI support

Hi antiduh, I'm following this in the github. In kerberos we have some steps like 1. Client requesting the TGT from KDC 2. Client getting the TGT 3.Client requesting SGT from KDC by using TGT, to access specific service 4. Client getting the SGT 5. Send SGT to resource server 6. Access the resource. Can you please tell me how do you handle the 3rd and 4th steps in the github.com/antiduh/nsspi/blob/master/TestClient/ClientForm.cs. Thanks.:)

Authorization

Hi,

I understand the authentication part of this solution. How the authorization piece works? If the user is authenticated how can I get the group membership back?

Thank you,
-Zoltan

Strong name

As we use strong-named assemblies in our applications, we cannot reference the Nsspi.dll from the NuGet package. Would it be possible to strong-name future releases?

Alternative credentials; NuGet Package; versioning

Hi Kevin,

Thank you for your project. Great effort

I change some code to be able to logon with another credential (for client and server).
I was planning to do a pull request. But saw you did an implementation yourself.

I want to adapt your implementation to able to use nuget and future releases, but these change are not in a nuget package yet. Also the TestClient (and TestServer) lacks an implementation.

Will you be able to do this in near future?

Regards!

ServerContext does not contain a definition for GetRemoteIdentity

With latest PR changes, I tried to start the TestServer project in visual studio and I got the build error for TestServer project. The error is saying:

"'ServerContext' does not contain a definition for 'GetRemoteIdentity' and no accessible extension method 'GetRemoteIdentity' accepting a first argument of type 'ServerContext' could be found (are you missing a using directive or an assembly reference?)"

Seems like code is not retrieving remoteId as expected on this line: https://github.com/antiduh/nsspi/blob/master/TestServer/ServerForm.cs#L180

NTLM authentication to a proxy/web server using Nsspi Client ??

Hi,

To continue our discussion from the post at

http://stackoverflow.com/questions/17241365/client-server-authentication-using-sspi/24312883?noredirect=1#

As I said I want to achieve the NTLM authentication from my client program to the web server (IIS/Proxy server). I am wondering if it is possible via the nssp library. At the moment the nsspi has both client side and server side code which uses the SSPI to achieve the authentication.

How can I proceed to may be just use the client side api to somehow replicate what browser does in case of NTLM authentication, which would be to just replicate and fill in the required request response headers for the initial and 2 way handshakes?

I was planning to do it on my own, but not sure if I could utilize a standard mechanism to hash the password and achieve the same without using SSPI ? OR it would be better to use the SSPI api.

I have to write this solution in c#.

Update:

As you suggested I decided to use the client side of NSSPI and try to generate tokens and stuff into the request headers. However, the Server sends back the response as WWW-Authenticate again as NTLM. If i do not Base 64 encode the token before putting in authoraization header then i get back
400 (bad request) error.

I am expecting IIS server to return me the challenge based on the token that i send to it in type 2 message.

Thank you
Kuldeep

Nuget package

Hello and thank you for implementing this wrapper,

what do you think of deploying it as a nuget package?

Kind regards
Daniel

v0.3.1 pre-release or release?

Hi,

is version 0.3.1 a Pre-Release (as I can see here: Release-List) or a "full" release? I'm aksing because the nuget-package is a full release and not a pre-release.

Thanks in advance

Support channel binding tokens

The call to InitializeSecurityContext and AcceptSecurityContext can have multiple input token buffers specified. One of these buffer types is the SECBUFFER_CHANNEL_BINDINGS which is a value of SEC_CHANNEL_BINDINGS which is a binding that the caller to SSPI would provide.

Currently the context Init method does not have a way to pass in a bindings value to be used with authentication which means it cannot be used with services that mandate channel binding support. It would be great if this could be added to this library.

Issues (reading registry and other stuff) after impersonation (using NTLM)

Hello,

I have tried nsspi, and it's a very nice component.
However I'm having issues after impersonation:

  • with special folders (MyDocuments)
  • trying to read registry.
  • The existing code which writes a file does not work either.

The server is running under a user profile (Someuser2) which:

  • is part of the Users group.
  • has the "Impersonate a Client AfterAuthentication" User Right (SeImpersonatePrivilege).
    I suppose the problem is with Someuser2.
    I hope you can point me to the right direction...

The client is running under another profile.

The tests are performed in the same workgroup (no domain), and the problem occurs when the client and the server run on the same machine (Win 8.1) or on 2 different machines (server=Win8.1,client=Win2016).

Some stuff works, but:

  • I have added serveral lines of code in ServerForm.impersonateButton_Click() to read the registry and it does not work (exceptions thrown).

  • The code which is is originally in ServerForm.impersonateButton_Click() to write a file does not work anymore either.

  • Of course I did not change anything else in the code.

      private void impersonateButton_Click(object sender, EventArgs e)
      {
      	ImpersonationHandle handle;
    
      	// Get the MyDocuments folder before impersonation: WORKS.
      	Console.WriteLine("MyDoc BEFORE impersonation=" + Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
    
      	using (handle = this.serverContext.ImpersonateClient())
      	{
      		// Get the current UserName AFTER impersonation: WORKS.
      		Console.WriteLine("Starting impersonation: " + Environment.UserName + Environment.NewLine);
    
      		// Get the MyDocuments folder AFTER impersonation: *** DOES NOT WORK ***.
      		// (an empty string is returned)
      		string s = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
      		Console.WriteLine("MyDoc AFTER impersonation=" + s);
    
      		MessageBox.Show("Starting impersonation: " + Environment.UserName);
      		MessageBox.Show("MyDoc AFTER impersonation=" + Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); // EMPTY
    
      		try
      		{
      			Console.WriteLine("GetVal");
    
      			// *** DOES NOT WORK (Exceptions) ***
      			// (Details about the exceptions below)
      			var data = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion",
      				"CommonFilesDir", "Not found");
    
      			Console.WriteLine(data.ToString());
      			int a = 123;
      		}
      		catch (Exception ex)
      		{
      			Console.WriteLine("EX: " + ex.ToString());
      			Console.WriteLine(ex.StackTrace);
      			ex = ex;
      			int a = 123;
      		}
    
      		try
      		{
      			// *** DOES NOT WORK (Exception) ***
      			// (Detail about the exceptions below)
      			FileStream stream = File.Create(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\test.txt");
      			StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
    
      			writer.WriteLine("Hello world.");
    
      			writer.Close();
      			stream.Close();
      		}
      		catch (Exception ex)
      		{
      			Console.WriteLine("EX2: " + ex.ToString());
      			Console.WriteLine(ex.StackTrace);
      			ex = ex;
      			int a = 123;
      		}
      	}
    

`
Here are the details I could get with the Visual Studio debugger:

System.ArgumentException occurred
HResult=-2147024809
Message=Unknown error "1346".
Source=mscorlib
StackTrace:
at System.Diagnostics.Tracing.EventProvider.Register(Guid providerGuid)
InnerException:

(1346 is Either a required impersonation level was not provided, or the provided impersonation level is invalid.)

System.Security.SecurityException occurred
HResult=-2146233078
Message=Requested registry access is not allowed.
Source=mscorlib
StackTrace:
at System.ThrowHelper.ThrowSecurityException(ExceptionResource resource)
InnerException:

(-2146233078 = 0x8013150A = COR_E_SECURITY)

Here are the details in the console:

TestServer
Server: Received ClientToken
Server: Sent ServerToken
Server: Received ClientToken
Server: Sent ServerToken
MyDoc BEFORE impersonation=C:\Users\Someuser2\Documents
Starting impersonation: ggo

MyDoc AFTER impersonation=
GetVal
*** REGISTRY access ***
EX: System.Security.SecurityException: Requested registry access is not allowed.
at System.ThrowHelper.ThrowSecurityException(ExceptionResource resource)
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
at Microsoft.Win32.Registry.GetValue(String keyName, String valueName, Object
defaultValue)
at TestServer.ServerForm.impersonateButton_Click(Object sender, EventArgs e)
The Zone of the assembly that failed was:
MyComputer
System.Security.SecurityException
at System.ThrowHelper.ThrowSecurityException(ExceptionResource resource)
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
at Microsoft.Win32.Registry.GetValue(String keyName, String valueName, Object
defaultValue)
at TestServer.ServerForm.impersonateButton_Click(Object sender, EventArgs e)

*** File creation access ***
EX2: System.IO.IOException: Unknown error "1346".
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, I
nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions o
ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolea
n useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share, Int32 bufferSize)
at System.IO.File.Create(String path)
at TestServer.ServerForm.impersonateButton_Click(Object sender, EventArgs e)
System.IO.IOException
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, I
nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions o
ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolea
n useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share, Int32 bufferSize)
at System.IO.File.Create(String path)
at TestServer.ServerForm.impersonateButton_Click(Object sender, EventArgs e)

Thank you very much,
best regards,
Olivier gg.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.