5

When you logon to Windows, your credentials are cached. This allows you to use single sign-on. If you were to then browse to another computer, e.g.:

\\hydrogen

enter image description here

you would not be prompted for credentials.

Windows will take your:

  • current username
  • (hashed) password

and attempt to authenticate you automatically. The interesting thing is that this even works if your workstation is not on the domain. Windows will automatically use your username and password when connecting to the server. And if your:

  • local username/password matches a
  • domain username/password

you are automatically let in.

Pretty picture:

enter image description here

This is called Single Sign-On. You sign-on to Windows once, and your cached credentials are used to validate you as you use other things on the network.

Browsers also do this

Chrome, Internet Explorer, and Firefox also do a variation of this. If you need to login to a web-site, and the server supports Negotiation authorization, the server will send you back an indication that you should try the user's Windows/Domain/Kerberos credentials:

HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/7.5
WWW-Authenticate: Negotiate
Date: Thu, 09 Jul 2015 14:35:58 GMT
Content-Length: 0

Chrome will then take your cached credentials, and (after some intermediate magic) forward them to the web-server:

GET http://hr.woodglue.com HTTP/1.1
Host: hr.woodglue.com
Authorization: Negotiate YIIFzwYGKwYBBQUCoIIFwzCCBb....

enter image description here

Microsoft talks about this mechanism in Internet Explorer in the old article:

HTTP-Based Cross-Platform Authentication by Using the Negotiate Protocol

enter image description here

  • The client calls AcquireCredentialsHandle() and InitializeSecurityContext() with the SPN to build the Security Context that requests the session ticket from the TGS/KDC.

You validate against a domain, not servers

A final point i want to mention is that servers, web-servers, workstations, file servers, don't validate credentials against a server, they validate against a domain controller. You have a nebulous forest of many domain servers, and one of them handles your request.

In other words, you don't validate credentials against:

  • \\uranium (a domain controller on the woodglue.com domain)

you validate credentials against:

  • the woodglue.com domain

We have the important concept that someone can:

  • validate your cached credentials
  • against a domain
  • without having to enter a username or password

How can I do this?

How can I validate someone's cached credentials? How can I:

  • validate the user's cached credentials
  • against a domain
  • without the user having to enter a username or password (i.e. using the Windows cached credentials)

The important point is that is don't know (or care):

  • if the user's machine is joined to a domain
  • if the user's machine is joined to a workgroup
  • if the user's machine is joined to the woodglue.com or some other domain (e.g. superglue.com)
  • the names of the servers that power the superglue.com domain

enter image description here

I don't know how to do it.
I don't know what API technologies are involved.

I know there is an API called the Security Support Provider Interface (SSPI). This is what powers WWW-Authenticate: Negotiate (although i don't know if it is what powers SMB from a non-domain joined PC).

Chromium's open-source might be able to start us off with a snippet from their http_auth_sspi_win.cc. They use the SSPI function AcquireCredentialsHandle:

int AcquireDefaultCredentials(CredHandle* cred) 
{
   TimeStamp expiry;
   // Pass the username/password to get the credentials handle.
   // Note: Since the 5th argument is NULL, it uses the default
   // cached credentials for the logged in user, which can be used
   // for a single sign-on.
   SECURITY_STATUS status = library->AcquireCredentialsHandle(
         NULL,  // pszPrincipal
         const_cast<SEC_WCHAR*>(package),  // pszPackage
         SECPKG_CRED_OUTBOUND,  // fCredentialUse
         NULL,  // pvLogonID
         NULL,  // pAuthData
         NULL,  // pGetKeyFn (not used)
         NULL,  // pvGetKeyArgument (not used)
         cred,  // phCredential
         &expiry);  // ptsExpiry
}

Pass the username/password to get the credentials handle.

Note: Since the 5th argument is NULL, it uses the default cached credentials for the logged in user, which can be used for a single sign-on.

This "outbound" call to AcquireCredentialsHandle is followed up by a call to InitializeSecurityContext. The idea is that InitializeSecurityContext generates an opaque blob that represents the client.

You can then perform a parallel set of calls:

  • "inbound" call to AcquireCredentialsHandle
  • call AcceptSecurityContext, passing the blob returned earlier from InitializeSecurityContext

To steal rehost Daniel Doubrovkine's excellent image:

enter image description here

Note: In this case "client" and "server" are used to refer to context of producer and consumer. In my case the "client" and "server" are on the same machine.

But this line of "shows research effort" falls apart because i don't see anywhere in InitializeSecurityContext where i can specify woodglue.com as the domain to validate against.

I know that InitializeSecurityContext contacts a kerberos server, and obtains a "ticket" blob. That ticket blob is then passed to the "server" through AcceptSecurityContext. Sometimes the blob can be passed over a network; in my case it is passed around in memory on the same machine.

But i don't see how to specify the domain server that it should be contacting for that ticket.

Not to imply that SSPI is at all useful to solve my problem. It's just "research effort".

Older Research Effort

Of course, during all this, if the cached credentials are not valid on the specified domain, i would have to prompt the user for a username and password. But usernames and passwords are evil, the bane of computing, and i want to avoid them.

i'm using native code; not C#.

Community
  • 1
  • 1
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • Are you sure your first two examples do actually work? AFAIK, SSO only works within a domain, i.e., the client and server must be members of the same domain. (But perhaps Windows 8 has improved on this, or there's a trick to making it work that I don't know.) At any rate, SSPI will only allow the server to authenticate against the domain/forest that it is a member of (that's why InitializeSecurityContext doesn't ask for a domain name) so you can probably rule that out as an option. – Harry Johnston Jul 11 '15 at 02:10
  • It is possible to save the cleartext username and password when the user logs in, if that's any help. – Harry Johnston Jul 11 '15 at 02:26
  • @HarryJohnston I know it works, because i tested it. I put a machine on it's own workgroup, and created a local account named *"Ian"* with a unique password. If i attempt to browse a domain-joined machine: i am prompted for credentials. But if i change the password of my local account, on the non-domain joined machine, to match that of the password of the woodglue\Ian account, it am automatically granted access (e.g. *"Single Sign-on"*). – Ian Boyd Jul 11 '15 at 13:16
  • @HarryJohnston I do save the user's credentials (but not in plaintext!), using [the Data Protection API](http://stackoverflow.com/questions/28862767/how-to-encrypt-bytes-using-the-tpm). It's actually the same mechanism the Credential API uses to store *"generic"* credentials. But user's should not have to enter credentials. – Ian Boyd Jul 11 '15 at 13:18
  • In the past, an attempt to connect to a file server, via a domain account whose username and password matches local cached credentials, only worked if the file server was a domain controller. That's because the client had to specify the domain as well as the username during authentication - only domain controllers would default to trying a domain account, since they don't have local accounts. On an ordinary server, you could only connect silently if there was a local account with the same username and password. But it's been years since I've experimented. Thanks; interesting news! – Harry Johnston Jul 11 '15 at 23:50
  • As for the Data Protection API, do you mean that your application prompts for credentials once and then saves them? I'm talking about capturing the credentials that the user logs into Windows with, the same way the network client does - that's how SSO works when the client and server aren't in the same domain. The network client remembers the username and password from when the user logged on and reuses them as needed, and there are hooks for third-party software to do the same thing. I'm not sure it helps you much, just mentioned it in case it was useful. – Harry Johnston Jul 12 '15 at 00:00
  • Back to the question: the simple approach would be to locate a domain controller for the target domain and try to connect to the `sysvol` share. The problems with this are (1) if you're not inside the corporate network the target domain belongs to, you'll almost certainly be blocked by the firewall, and might not even be able to do a DNS lookup to find a DC; (2) I can't think of any generic way for you to verify that you're really talking to a domain controller for the target domain and not a man-in-the-middle type attacker. Perhaps investigate SMB signing. – Harry Johnston Jul 12 '15 at 00:57

0 Answers0