30

I'm working on a client-server application and I want the client to authenticate itself to the server using the user's logon credentials, but I don't want the user to have to type in their user name and password. I certainly don't want to be responsible for securely handling passwords. I only need the user to prove to me that they are who they say they are, and then my server can go ahead and grant/deny commands as it pleases.

My users are part of a domain, and so I want to be able to use the logon credentials they created when they logged in.

I'm not using any sort of web services, nor do I want to. I control both the client and server software, and both are written in pure C# and use good ol' sockets for getting work done.

I'd prefer to do this with pure C#/.Net, but I'm open to using unsafe C# and pinvokes to win32 API if it means that I'll get the job done.

I've read a little bit about SSPI in windows, but I'm kind of feeling around in the dark since this sort of application development is new to me.

Does anybody know how to do this? Is SSPI the way? How does one use SSPI from within C#? Is there a .Net-native way so that my code can remain portable?

antiduh
  • 11,853
  • 4
  • 43
  • 66
  • Well have you looked at using Windows Authentication? This type of authentication will use the user's domain username to validate access to the server. the connection string should look something like this **Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;** – Mo Patel Jun 19 '14 at 11:22
  • I don't think there is a .NET native way to do this. There is however a sample from MS demonstrating how to use SSPI. It involves creating a managed C++ which handles SSPI and provides interface to .NET. To be frank, I wasn't able to run it on Windows 8.1 and didn't look into debugging it, but it may be a good read for you. http://msdn.microsoft.com/en-us/library/ms973911.aspx I do recommend that you rethink your decision on using plain sockets when you have simpler solutions in .NET. – Nikola Radosavljević Jun 19 '14 at 11:53
  • 2
    To clarify: As WCF can do it without using IIS/ASP.NET but plain sockets I should also be able to do so. The WCF source code (available through the reference source license) is messy and hard to follow. – jgauffin Jun 19 '14 at 13:15

5 Answers5

29

Update:

SSPI is the right approach for this. The API isn't too hard to use, but does require a decent-sized project to wrap into C#.

In the process of researching the necessary bits to solve this question, I wrote a project to provide SSPI in .Net. Below I describe the basics of interfacing with the Windows SSPI API so that anybody may replicate my results. If you find yourself wanting to use SSPI in .Net, I may suggest you use the project I created to solve this:

NSspi - A .Net interface to the SSPI API

SSPI provides you raw byte arrays containing authentication tokens that you then decide how to transmit - be it over a socket with binary-formatted messages, a custom XML channel, .Net Remoting, some form of WCF, heck, even a serial port. You get to decide how to deal with them. With SSPI a server can authenticate clients, securely identify the client, and even perform basic message handling procedures like encryption/signing using the security context established with the client.

The SSPI API is documented here: SSPI API overview

Specifically take a look at the following functions:

The typical workflow is that each side will initialize their credentials using AcquireCredentialsHandle. The authentication cycle then starts and progresses as follows:

  • The client invokes InitializeSecurityContext, providing no input tokens, which returns output tokens in the form of a byte array. ISC returns 'ContinueNeeded' to indicate that the authentication cycle is not complete.
  • The client sends the tokens to the server by whichever means it desires.
  • The server feeds the received tokens as input to AcceptSecurityContext and produces its own output tokens. ASC also returns 'ContinueNeeded' to indicate that the authentication cycle is not complete.
  • The server then sends its output tokens to the client.
  • The client provides the servers tokens as input to InitializeSecurityContext, which returns new output tokens.
  • The client sends his new output tokens to the server.
  • ...

This cycle continues until the client sees InitializeSecurityContext return 'OK' and the server sees AcceptSecurityContext return 'OK'. Each function may return 'OK' and still provide an output token (as indicated by a non-null return), to indicate that it still has to send data to the other side. This is how the client knows that its half is done but the server's is still incomplete; and vice versa if the server completes before the client. Which side completes first (returns 'OK') depends on the specific security package being used under the hood by SSPI, and any SSPI consumer should be aware of this.

The information above should be enough for anybody to being interfacing with the SSPI system in order to provide 'Windows Integrated Authentication' in their application and replicate my results.

Below is my earlier answer as I learned how to invoke the SSPI API.


I had forgotten about this question, and coincidentally returned to this problem a few days ago on a whim. I do need to solve this problem in a year or two though :)

It is possible in .Net, and I am currently developing a .Net SSPI wrapper that I intend to publish.

I'm basing my work off of some SSPI samples from Microsoft I found.

The sample contains a C++/CLI managed assembly that implements the necessary parts of the SSPI API (in the folder Microsoft\Samples\Security\SSPI\SSPI extracted from the REMSSPI.exe file). They then have two UIs, a client application and a server application, both written in C# that make use of this API to perform SSPI authentication.

The UIs make use of the .Net remoting facility to tie it all together, but if I understand the SSPI API correctly, the only information that the client and server need to exchange consists of byte[]s containing security context token data, which can easily be integrated into whatever communications infrastructure you want; in my case, a binary protocol of my own design.

Some notes on getting the sample to work - they have the 'SSPI' library source, which best compiles under VS 2005, though I've gotten it to work under 2008; 2010 or above would require some rework since they use language constructs that were deprecated. You may also need to modify header files that are part of your platform SDK, because they make use of const pointer assignments to unconst variables, and I don't know a better way to make the compiler happy (I've never used C++/CLI before).

They do include a compiled SSPI dll in the Microsoft\Samples\Security\SSPI\bin folder. To get the client/server binaries to work, you have to copy that dll to their bin directory, else the fail assembly resolution.

So to summarize:

  • Go here to download the REMSSPI.exe sample self-extracting zip.
  • Extract the REMSSPI.exe file (twice..)
  • Microsoft\Samples\Security\SSPI\
    • bin\ - contains compiled dll Microsoft.Samples.Security.SSPI.dll
    • SSPI\ - contains source to dll
    • Sample\ - contains UI source code
      • bin\ - Contains build UI samples. Copy the SSPI.dll file here and run ControlPanel.Client.exe and ControlPanel.Server.exe
antiduh
  • 11,853
  • 4
  • 43
  • 66
  • Yeah. I've found that sample too. But would prefer a solution without a DLL. If you can p/invoke those API calls and get a ready solution you'll be awarded with the points. – jgauffin Jun 19 '14 at 18:23
  • 1
    That DLL is a managed .Net DLL, just written in C++/CLI. I'm writing my own version of it in C# for my own reasons, but I could just as easily use that one. – antiduh Jun 19 '14 at 18:38
  • 1
    ahh ok. nice. please publish it :) – jgauffin Jun 19 '14 at 19:01
  • 1
    I think we're confused. Microsoft.Samples.Security.SSPI.dll is a Managed .Net DLL, written in C++/CLI. You can get the binary from the sample's download. I'm writing my own version of that DLL in C# that I intend to publish, but you could probably just use Microsoft.Samples.Security.SSPI.dll as is. – antiduh Jun 19 '14 at 19:33
  • Have you seen the `NegotiateStream` in .NET? – jgauffin Jun 24 '14 at 06:45
  • @jgauffin - I hadn't. It looks pretty close to what we need, though I don't like that I can't specify any of the SSPI context attributes (mutual auth, replay detection, out-of-sequence detection, integrity, identify). According to one blog post, it'll degrade the connection to NTLM if it can't support mutual auth, which seems like the insecure thing to do. Hrrmm. – antiduh Jun 24 '14 at 15:09
  • @jgauffin - for what it's worth, I have a working prototype available. Anonymous svn at https://angst.csh.rit.edu/svn/nsspi/trunk/. It's still very much prototype - inconsistent organization, breaks under 64-bit, unsafe handle usage, doesn't support many features, lots of exposed internals, etc. I'm still working on it regularly though. – antiduh Jun 24 '14 at 15:13
  • @jgauffin - I've published my project. It's alpha quality since I don't yet have a production environment to test it in, but I'd welcome your feedback. I've included my notes in my edit so that others may replicate my work. – antiduh Jul 07 '14 at 20:26
  • How do you feel about putting it on nuget.org? – CodeFox Sep 23 '14 at 17:32
  • @antiduh question: once server and clients are completed, who do you authorize the user in a Domain, in case you are writing a custom authentication package (so no NTLM, no Kerberos default packages)? I've seen the examples set a buffer message full of 'x' or 'y' or 'z' but what do they mean? That part is not documented at all – madduci Jan 19 '16 at 08:20
  • @blackibiza - If you have a custom authentication package, you can invoke your package through the SSPI API simply by its string name - in the NSspi project, PackageNames is just a set of string constants that are passed to the Credential concrete classes. If you have some external API to invoke to acquire a handle to the current credential, you can extend the Credential/ClientCredential/ServerCredential classes to do it yourself. Is the buffer question a separate question, and could you clarify? – antiduh Jan 19 '16 at 16:05
  • @antiduh thanks for the reply, the buffer question is related to it. Anyway so far I invoke a custom AP with LsaCallAuthenticationPackage and interact with it. At the end, I understand implementing a SSPI module is useful when some kind of client/server architecture is involved. In my case, everything runs in local on the same machine. Is it still a good approach or should I implement SSPI anyway? – madduci Jan 20 '16 at 08:53
  • @antiduh thanks for such a wonderful post. However, I have a question. Does there need to be a process at the server side which uses SSPI mechanism to sort of complete the whole communication ? when I see the sample documentation of SSPI, I get a feel that there requires a process running at the server too ? I want to achieve this to the local proxy server, and achieve the authentication process. But I doubt if i can have a process running there ! Usually server right now supports basic auth and console client can get authenticated just as normal browser uses basic auth mechanism. – kuldeep Aug 02 '16 at 11:44
  • @k2ibegin - with authentication there always needs to be a client and a server. However, what is the client and what is the server is up to you. In my case, the server is just some program I wrote that listens on a socket (and doesn't use http). Sounds like your program wants to talk to a web server and authenticate like IE does - that happens using GSSAPI Kerberos. The SSPI API supports this, and nsspi can/will; take a look at: https://github.com/antiduh/nsspi/issues/3. That person implemented GSSAPI for nsspi, they've told me they'll contribute it back. I'm just waiting for them to do so. – antiduh Aug 02 '16 at 14:51
  • @antiduh Thanks a lot for your response. Yes, you are right I am trying to talk to a web server, and was looking for the possibility of NTLM mechanism to achieve the authentication. If NTLM is too complicated, i will go for Kerberos. The GSSAPI Kerberos uses kerberos mechanism ? I believe for Kerberos based mechanism Active directory is a requirement? In case SSPi based solution does not workout for me, I am thinking to implement the 3 way handshake by myself (replicating the http scenarios just as happens in basic auth). Can you suggest which implementation would be the best for my scenario? – kuldeep Aug 03 '16 at 07:32
  • @k2ibegin - It looks like NTLM over web is possible - seems they base-64 encode the tokens: https://blogs.msdn.microsoft.com/chiranth/2013/09/20/ntlm-want-to-know-how-it-works/. You could probably use the client side of SSPI to perform the authentication cycle, stuffing the tokens in the http request. Want to open an issue on the github page, and we'll keep talking there? – antiduh Aug 03 '16 at 14:28
  • @antiduh I have opened an issue on the github page. Right now i am following the approach as you mentioned that I am using client side code to generat the tokens and fill the http request headers with these tokens. However, The type 2 message does not gives me back the authorization string, it gives me back again NTLM in WWW-Authenticate! Do I need to encode the token in a specific way before filling in the request header? – kuldeep Aug 09 '16 at 16:04
  • @antiduh: Thanks for your implementation of the NSSPI interface. Anyway could you enlighten us on the derivation process of the session key between the client and the server for your implementaion (starting from the Challenge nonce in the Type 2 message sent from the server to the client) ? Thanks in advance! – meta_warrior May 17 '20 at 16:01
  • @meta_warrior - the contents of the messages that nsspi participates in is decided 100% by Windows. This library simply provides an interface to participate in a conversation. – antiduh May 18 '20 at 00:41
1

You may ask: "How does the server confirm a client are who they say they are?"

Answer: If all the roundtrips of handshake could be finished successfully, i.e. both

  • InitializeSecurityContext returns "OK"
  • AcceptSecurityContext returns "OK"

it means the client's Windows login credentials have been confirmed to be real.

AcceptSecurityContext outputs a CtxtHandle security context (through the 6th parameter).

This context handle includes the client's Windows login username. The server can get the client's windows username by calling QueryContextAttributesEx:

SecPkgContext_NativeNames pinfo;
QueryContextAttributesEx(&m_securitycontext, SECPKG_ATTR_NATIVE_NAMES, &pinfo);

This populates a Native Names structure:

SecPkgContext_NativeNames 
{
   SEC_CHAR *sClientName;
   SEC_CHAR *sServerName;
}

the value pinfo.sClientName is the client's real login user name.

Note: The previous handshakes already guarantee the truth of security context, so the server would believe pinfo.sClientName is just the client's real windows username.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
0

Nikola is correct; there is not a .NET-native way to accomplish what you are doing (at least, not using the low-level .NET Sockets support). You can certainly dive under the covers to do some interop black magic, but if both the client and server are under your control, you may want to consider moving up the stack a little and using a higher-level API such as WCF, which DOES have .NET-native support for Windows Integrated authentication.

Based upon your question and your described environment, you'd be able to use the NetTcpBinding, which offers high performance as well as the plumbing for the authentication/identity flow you're looking for (it also offers a fairly clean way to handle authorization using the ServiceAuthorizationManager class). Without knowing the specifics of your app/service I can't possibly provide a "How To" to implement what you're looking to do, but I can point you at the docs which have a reasonably simple example.

Andy Hopper
  • 3,618
  • 1
  • 20
  • 26
  • I want to use a .NET way (for an own client/server). WCF seems to be able to do it using it's netTcpBinding, but the code is messy and hard to follow. – jgauffin Jun 19 '14 at 12:42
  • I'm not sure what you mean when you say "a .NET way" - WCF is a .NET feature. I do understand that the WCF learning curve can be daunting, but from my own experience it's better to learn how to use the existing frameworks than try to implement your own (in terms of development time and maintenance overhead). – Andy Hopper Jun 19 '14 at 13:09
  • The question is very specific. I do not want to use WCF. I want to be able to use windows authentication in an own client/server. WCF can do it in it's netTcpBinding, so it IS possible. I want to know how using .NET without p/invokes if possible. It's for my own client/server library http://blog.gauffin.org/2014/05/griffin-framework-performant-networking-in-net/. I say again: Any answer suggesting WCF is not OK by me. – jgauffin Jun 19 '14 at 13:13
0

I went totally wrong with WindowsIdentity (which is good for authorization) because I forgot that WCF handles a lot of things with configuration files, endpoint security and message/transport security.

Have you tried with NegotiateStream ? The given example seems to better fit your needs : it uses Kerberos for authenticating before allowing any read/write. Using the CredentialCache.DefaultNetworkCredentials should avoid you to query for a password.

xum59
  • 841
  • 9
  • 16
-1

Ever tried to work with WindowsIdentity ?
Pure C#/.Net, serializable and GetCurrent() returns the executing account.


Security Token
The user delivers a set of claims to your application piggybacked along with her request. In a Web service, these claims are carried in the security header of the SOAP envelope. In a browser-based Web application, the claims arrive via an HTTP POST from the user’s browser, and may later be cached in a cookie if a session is desired. Regardless of how they arrive, they must be serialized somehow, and this is where security tokens come in. A security token is a serialized set of claims that is digitally signed by the issuing authority. The signature is important – it gives you assurance that the user didn’t just make up a bunch of claims and send them to you. In low security situations where cryptography isn’t necessary or desired, you can use unsigned tokens, but that’s not a scenario I’m going to focus on in this paper. One of the core features in WIF is the ability to create and read security tokens. WIF and the underlying plumbing in the .NET Framework handles all the cryptographic heavy lifting, and presents your application with a set of claims that you can read.

quoted from Windows Identity Foudation WhitePaper

Your primary question was :

"Is there a pure C#/.NET way to authenticate users using their logon credentials ?"

The WindowsIdentity "is" the authentication token issued by your domain controller and seems to me like the best approach at this time.


I knew very few about WindowsIdentity when I first posted but I also felt it would help with your problem and constraints. I've done a lot of reading and finally came to this page.
The introduction of WIF is quite self-explanatory, WindowsIdentity is a new set of the .NET framework designed for Windows-based/Role-based security concerns.
SSPI, Kerberos are parts of the whole Windows authentication process, the logon token obtained by the user/machine/process is granted by the domain controller and cannot be obtained by "simply" instanciating a new WindowsIdentity object. If this kind of illegal instanciation existed, the whole Windows security model (domains, UAC, etc.) would be dead.

Here is a (very!) little console program that throws an exception if you aren't part of the "BUILTIN\Administrateurs" (change the group name according to your own needs). Whenever "Run as Admin", the program finishes without errors.
There is a very large set of permissions and every demand is claim-based (is the identity memeber of xxx ?)

using System;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;

namespace WindowsIdentityTest
{
    class Program
    {
        [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
        static string SomeServerAction()
        { return "Authenticated users can access"; }

        [PrincipalPermission(SecurityAction.Demand, Role = "BUILTIN\\Administrateurs")]
        static string SomeCriticalServerAction()
        { return "Only Admins can access"; }

        static void Main(string[] args)
        {
            //This allows to perform security checks against the current Identity.   
            AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

            try
            {
                Console.WriteLine(SomeServerAction());
                Console.WriteLine(SomeCriticalServerAction());

            }
            catch (SecurityException sec)
            {
                Console.WriteLine(string.Format("{0} : {1}\n------------\n{2}"
                    , sec.GetType()
                    , sec.Message
                    , sec.StackTrace));
            }
            catch (Exception ex)
            {
                Console.WriteLine("This shall not appen.");
            }
            Console.WriteLine("Press enter to quit.");
            Console.ReadLine();
        }
    }
}

I hope this will help.

xum59
  • 841
  • 9
  • 16
  • Could you provide a little more detail? How can I use this to convince a service running on a remote machine that I am who I say I am? I see that WindowsIdentity is serializable, but that can't help very much, since good security design (kerberos) and SSPI require that transmitted tokens be designed in such a way that only the receiving service can use them (mutual authentication). I don't see how any of that class or related classes help with that, but then again I'm very new to that half of the API. – antiduh Jun 19 '14 at 21:51
  • The WindowsIdentity (GetCurrent()) object is the identification token obtained when the user logged. Using impersonnation on the server-side will allow to open a SQL Server connection with Integrated_Security set to true (if the domain user is granted), even if the server worker process account can't. For other kind of ressources (I/O Permissions...), it shall work all the same. – xum59 Jun 19 '14 at 22:41
  • You need to read my original question a little more carefully. – antiduh Jun 19 '14 at 23:29
  • You wrote "My users are part of a domain, and so I want to be able to use the logon credentials they created when they logged in." The user's logon credentials are obtained through WindowsIdentity.GetCurrent(), I don't know what kind of other proof you're trying to get with SSPI. As long as you don't want to handle logins and passwords, I believe that relying on Windows Authentification (groups, permissions and so on) is the best aapproach. – xum59 Jun 20 '14 at 05:54
  • Impersonation is not authentication. Feel free to provide a working example where the server can authenticate the client identity using `WindowsIdentity`. – jgauffin Jun 20 '14 at 08:07
  • I don't think you need any server-side authentication as WindowsIdentity IS the authentication token delivered to the user by your domain controller. If you don't want to handle passwords, you can't re-authenticate the user : passwords aren't stored in any way. Impersonation will only allow you to use the user's credentials on the server-side. Depending on your needs, decorate methods with the PrincipalPermissionAttribute and/or use explicit PrincipalPermission.Demand and/or rely on role-based security. – xum59 Jun 20 '14 at 10:38
  • @Xum59 I don't think you understand. We want the server process to authenticate clients so that people can't arbitrarily connect and send commands they don't have permission for. You can't trust the client software in a networking system, since they could forge network commands. Yes, the client could figure out who's logged in using WindowsPrinciple, but the client can't be trusted. – antiduh Jun 20 '14 at 15:13
  • "I don't think you need any server-side authentication" - that is the antithesis of secure server development practices. – antiduh Jun 20 '14 at 15:19
  • Think about how authentication is performed in any client-server software system. Does the Outlook Server trust the Outlook client? No, otherwise someone could write a custom outlook client that pretends to be any user and then an attacker could read any user's email. The client has to *prove* to the server that the user is who he says he is. This is often implemented by asking the user to send their username and password (over an encrypted channel, eg, SSL), to the server. – antiduh Jun 20 '14 at 15:29
  • However, I don't wish to have my users have to do that, and instead I wish to use their logon credentials to produce authentication tokens that I can then send to the sever. You need to read how kerberos and NTLM (network authentication protocols) and SSPI (Window's wrapper around NTLM and Kerberos) work. – antiduh Jun 20 '14 at 15:30
  • 2
    "Kerberos - Designing an Authentication System: a Dialogue in Four Scenes" - http://web.mit.edu/kerberos/dialogue.html – antiduh Jun 20 '14 at 15:31
  • 1
    SSPI - Read the Introduction and Authentication sections from here: http://msdn.microsoft.com/en-us/library/ms973911.aspx – antiduh Jun 20 '14 at 15:33
  • I saw your edits, and you still don't get it, which means you didn't read any of the links I gave you. You're still doing all authentication, identification, and permission management client side. *That's not secure!* Downvote. – antiduh Jun 24 '14 at 15:17
  • @antiduh: I read the links you added, and much more (I wish to do something similar) But have you read some of the document I linked? `My users are part of a domain, and so I want to be able to use the logon credentials they created when they logged in.` Send this logon credential (yep, WindowsIdentity) to the server and run security checks _server-side_ (impersonnation) Either you trust your domain controller to issue the logon tokens and associated credentials (or claims), either you don't (and then will need a full SSPI-complient authentication method which shall require a password) – xum59 Jun 24 '14 at 15:49
  • Ok, so how do you securely transmit a WindowsIdentity to the server? Your code doesn't show that, and the only secure way to do that is using SSPI, to my knowledge. – antiduh Jun 24 '14 at 15:52
  • I haven't had the time to finish my tests but I believe that there is a way to _validate_ the token by submitting it to the issuer, this could be the proof you so much want. Last thing, if you really want to prove everything (Kerberos style), your server shall prove its identity too. – xum59 Jun 24 '14 at 15:54
  • @xum59 - Right, but what you've just described is exactly what SSPI does. Except that SSPI can do NTLM, Kerberos, or any other Security Service Provider that's installed on the machine and meets the requirements imposed by the flags specified in the caller's context attribute flags. – antiduh Jun 24 '14 at 17:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56223/discussion-between-xum59-and-antiduh). – xum59 Jun 24 '14 at 18:35
  • @antiduh: SSPI does nothing, SSPI describes : it's an interface for SSP like Kerberos, NTLM. You say you want to rely on users' credentials, WIF is done for that. I'm no expert (I first came on this post through a Google search) but IMO (and I've done some reading since), implementing SSPI doesn't look like a good thing to me. – xum59 Jun 24 '14 at 18:35