antiduh / nsspi Goto Github PK
View Code? Open in Web Editor NEWA C# / .Net interface to the Win32 SSPI authentication API
License: BSD 2-Clause "Simplified" License
A C# / .Net interface to the Win32 SSPI authentication API
License: BSD 2-Clause "Simplified" License
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 :)
There's a similar/related implemented in https://github.com/Waffle/waffle, maybe worth explaining the differences?
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.:)
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?
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.
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
Hello and thank you for implementing this wrapper,
what do you think of deploying it as a nuget package?
Kind regards
Daniel
Hello,
I have tried nsspi, and it's a very nice component.
However I'm having issues after impersonation:
The server is running under a user profile (Someuser2) which:
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.
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!
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
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
Hi,
To continue our discussion from the post at
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
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
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
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.
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.
I've been trying to implement this library to perform NTLM Proxy Authentication, and am running into a peculiar issue.
The environment setup is:
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.
Also thank you for including good examples.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.