Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for GSSAPIAuthentication and GSSAPIDelegateCredentials via SSPI #1295

Closed
mgkuhn opened this issue Nov 21, 2018 · 28 comments
Closed
Labels
Issue-Enhancement Feature request

Comments

@mgkuhn
Copy link

mgkuhn commented Nov 21, 2018

Although GSSAPI authentication support was excluded from the initial scope of the project, this remains a highly desireable feature. In domain-managed "enterprise" environments, where both clients and servers usually have already been issued cryptographic keys via Active Directory's Kerberos implementation, GSSAPI would enable secure password-free ssh logins without any need to manage public-key credentials for openssh.

A particular priority would be support of GSSAPI authentication and ticket delegation in the sshd server. This is because client-side GSSAPI support exists already in two other forms:

  • the PuTTY ssh client has supported both GSSAPI authentication and delegation since version 0.61 (2011), via either Microsoft's SSPI or MIT's Kerberos for Windows;
  • the openssh ssh client reportedly can already be compiled with MIT's Kerberos for Windows GSSAPI implementation (although this is not currently done in the binary releases).

However there is currently no freely available sshd server that can receive a delegated ticket and provide it to the logon session such that the user can then use it on the server to access there other Kerberos-authenticated resources, such as shares on a file server. MIT's Kerberos for Windows maintains its own ticket cache separate from those used by the Windows Local Security Authority (LSA). Therefore sshd will have to use Microsoft's SSPI interface in order to ensure that a password-free GSSAPI-authenticated login with ticket delegation will give via the LSA logon session access to all Kerberos-authenticated resources on the server.

@mgkuhn
Copy link
Author

mgkuhn commented Nov 21, 2018

Discussion of this feature request and useful references have previously appeared under various other issues, such as #15, #96, #1157. I hope this dedicated ticket will help to collect all these in one place.

@mgkuhn
Copy link
Author

mgkuhn commented Nov 21, 2018

Adding GSSAPI authentication and delegation to sshd involves

  • reading RFC 4462,
  • looking at how that is currently implemented in sshd in gss-serv.c,
  • then port gss-serv.c from currently using /usr/include/gssapi/gssapi.h on Linux/macOS to using SSPI on Windows.

Microsoft's SSPI is essentially a clone of GSSAPI, even the function names are rather similar, e.g. gss_accept_sec_context in GSSAPI becomes AcceptSecurityContext in SSPI, etc. It looks like this should really be little more than like-for-like API call translation.

Related reading:

@mgkuhn
Copy link
Author

mgkuhn commented Nov 21, 2018

In order to perform GSSAPI authentication (and delegation) on Windows via the SSPI authentication functions, the ssh client will have to call InitializeSecurityContext() and the sshd server will have to call AcceptSecurityContext() and both have to pass the resulting GSSAPI tokens across the SSH2 protocol (as per RFC 4462) to the respective other side, and repeat this exchange of tokens to and fro until these SSPI calls return SEC_E_OK to report that the security context was successfully initialized.

@NoMoreFood
Copy link

Just an update. Yesterday I created a skeleton of what could be a minimalist, OpenSSH-focused SSPI Kerberos GSSAPI implementation. This would be a downsized gssapi.h and corresponding gssapi.c that would be statically compiled. Only a few OpenSSH source code changes would be required other than GSSAPI being defined. Assuming I don't hit any fundamental roadblocks, probably a few weeks out for an alpha version.

@NoMoreFood
Copy link

Another update. I have a working prototype for both the client and server. There is still quite a bit of testing and cleanup to do. Delegation will work provided that the computer account of the server is setup to allow it in Active Directory (using unconstrained, constrained, or resource-based delegation techniques). Unfortunately if the server is running Credential Guard, unconstrained delegation no longer works.

It'll probably be a week before I'll be ready for any one to test.

@mgkuhn
Copy link
Author

mgkuhn commented Nov 29, 2018

I would not be surprised if another prerequisite for delegation to work is to understand and fix #996 first.

@jborean93
Copy link

@mgkuhn I'm not sure supporting kerb delegation will get 996 to start working. The issue with that one is due to the logon session type used with the initial logon and not the delegation of credentials.

@NoMoreFood
Copy link

See PowerShell/openssh-portable#360.

@manojampalam
Copy link
Contributor

manojampalam commented Dec 3, 2018

@NoMoreFood, recommend posting binaries (as a release in your fork ?) so we can get your work tested out and validated. I wont be able to get to reviewing your changes until next year, have my plate full until then.
Otherwise, I'm wondering if your changes are specific to supporting Kerberos or can they also do SPNEGO (that can also do NTLM if needed) ? Sorry if my question is naïve, I'm getting myself familiar with the history and usage of GSSAPI.

@NoMoreFood
Copy link

@manojampalam I'll post a binary release in my fork shortly. As of right now, it only supports Kerberos. That's pretty much industry standard for SSH servers/clients. Use of NTLM deprecated and somewhat taboo these days.

@mgkuhn
Copy link
Author

mgkuhn commented Dec 4, 2018

@NoMoreFood I personally think you do the world a service by not adding NTLM support to a new product in 2018. NTLM support would just create additional worries that using OpenSSH with GSSAPI might pass on an NTLM hash by accident, and therefore expose a user password to dictionary and pass-the-hash attacks. Adding NTLM support to OpenSSH could well reduce security in practice.

@NoMoreFood
Copy link

@manojampalam Test binaries are here: https://github.com/NoMoreFood/openssh-portable/releases/tag/v7.9-sspi

@mgkuhn
Copy link
Author

mgkuhn commented Jan 18, 2019

Lots of thanks to @NoMoreFood for the pull request and to @manojampalam for merging it! This very happy user is delighted to report that the requested Kerberos authentication+delegation functionality works fine in both ssh -K and sshd in the recent release v7.9.0.0p1-Beta (tested on Windows 7 64-bit).

@mgkuhn mgkuhn closed this as completed Jan 18, 2019
@manojampalam
Copy link
Contributor

Thanks @NoMoreFood for yet another phenomenol contribution. I didn't expect this to materialize anytime soon.

@toupeiro
Copy link

toupeiro commented Jan 18, 2019

This is fantastic! I've been working with my support channels to get this worked via Microsoft, I just tested this as well successfully! I hope this can get pushed upstream and officially released soon. Going to point my contacts in MS to this thread to hopefully help it along!

tested on Windows 10.0.17134.471

@mgkuhn
Copy link
Author

mgkuhn commented Mar 8, 2019

Otherwise, I'm wondering if your changes are specific to supporting Kerberos or can they also do SPNEGO (that can also do NTLM if needed)?

Further to my comment above (that NTLM authentication is generally pretty dangerous and therefore best left out of SSH), I just noticed that section 7.3 in RFC 4462 specifically forbids the use of SPNEGO over SSH GSSAPI. This is because SSH has already a method negotiation mechanism, and running a second negotiation inside GSSAPI could subvert the preference order of mechanisms and lead to interoperability problems.

@toupeiro
Copy link

toupeiro commented Mar 8, 2019 via email

@maxadamo
Copy link

maxadamo commented Mar 1, 2021

It's not practical to use Windows AD tickets. It will only work if your Domain controller is the same that you are using with your Unix servers and you always need to login to AD. You may need to add MIT Kerberos.

@mgkuhn
Copy link
Author

mgkuhn commented Mar 2, 2021

@maxadamo Actually, it is perfectly feasible to use Windows AD tickets from Windows 10 computers that are not joined to any Active Directory domain, both for GSSAPI-authenticated SMB and OpenSSH for Windows access. I've just tried it successfully. Before you can authenticate as user [email protected] to Linux server server.example.com (which is in Active-Directory-based Kerberos realm DOMAIN.EXAMPLE.COM), on your non-domain joined Windows 10 computer with OpenSSH for Windows 8.1 installed:

  • Type "Credential Manager" into the search box and open that control panel.
  • Click "Add a Windows Credential"
  • Enter

(This is Windows' kind-of GUI equivalent of the kinit command on Linux. The command cmdkey /add:server.example.com /user:[email protected] /pass is another option.)

This tells the SSPI implementation of your OS which username and password to use when using Kerberos to authenticate to server.example.com (using either SMB or OpenSSH).

Then open a cmd window, confirm with ssh -V that you have OpenSSH for Windows 8.1p1 or newer installed, and then run ssh -Kv [email protected]. I was able to both authenticate and delegate a ticket that way, without my Windows 10 2020H2 PC being in that domain. Afterwards, klist will show that you obtained a ticket for service principal name host/[email protected].

This possibility probably should be be better documented. Microsoft's documentation mostly focuses on PCs that are domain joined when it comes to using Kerberos authentication.

@jborean93
Copy link

Interesting I always assumed that only did NTLM auth for those scenarios, the fact that it can still use Kerberos when it hasn't explicitly trusted that realm is surprising to me. Will have to play with that a bit more. Thanks for the info!.

@maxadamo
Copy link

maxadamo commented Mar 3, 2021

@mgkuhn really appreciated! Thanks.

I had no clue how to set this up until now. However, it would be slightly different from the way I am using MIT Kerberos on my laptop (either Linux or Windows, it won't change much).

I have one file (krb5.conf) where I map a REALM domain to a bunch of Active Directory servers (it could be MIT Kerberos servers but I have Windows AD servers) and I obtain a ticket against this REALM: EXAMPLE.COM and not SERVER.EXAMPLE.COM
With this ticket, I can log in - without inserting a password - on hundreds of servers.

This is what happens if I use MIT Kerberos on Linux and Windows and I guess it might be the same if I log in to AD (but I have never tried).

@mgkuhn
Copy link
Author

mgkuhn commented Mar 3, 2021

@maxadamo Windows appears to be by default far more cautious about engaging with unknown KDCs than MIT Kerberos or Heimdal are.

I don't know for sure the reason behind this, but my suspicion is that this may be due to concerns that SMB UNCs, such as \\evil-server.com\nasty-share\pretty-pixel.gif or file:///evil-server.com/nasty-share/pretty-pixel.gif, could be sent by a bad actor to a Windows user in some document (HTML, word processing, etc.), and could thereby inadvertently trigger the recipient's SMB+SSPI implementation to start to authenticate with the evil KDC of evil-server.com, and thereby hand to the adversary pre-authentication information that might expose the user's Kerberos password to an offline dictionary attack. Typical Unix/Linux/macOS systems don't have equivalent concerns, as they don't resolve filesystem paths like that remotely over the Internet without prior configuration (e.g., via /etc/fstab or automount tables).

For an unconfigured client machine to be able to allow the user to perform Kerberos/GSSAPI authentication when logging in as [email protected] to Unix sshd server server.example.com, at least two things need to happen first:

  • The Kerberos client needs to map the server name server.example.com to the corresponding service principal name (SPN) host/[email protected] (which domain-joined Windows clients normally do via an LDAP query to their Active Directory domain controller)
  • The client then needs to map the server's (and also user's) Kerberos realm DOMAIN.EXAMPLE.COM to the IP address of the KDC that can then issue a service ticket for that SPN (via TCP or UDP on part 88).

On macOS and Linux, this "just works" if you type

because:

  • You've told kinit what the user's realm is, which it remembers in the ticket and which the Kerberos client library will then also use as a possible default realm for server.example.com (in the absence of other information from /etc/krb5.conf or DNS)
  • There should be a DNS TXT record DOMAIN.EXAMPLE.COM at _kerberos.example.com or _kerberos.server.example.com to help Kerberos map fqdn DNS domains or hostnames to Kerberos realms.
  • There should be one or more DNS SRV records at _kerberos._tcp.domain.example.com providing the IP addresses of the KDC to contact for DOMAIN.EXAMPLE.COM

Windows is also able to find KDCs via DNS this way, but (if that KDC is not already your domain controller) you need to explicitly configure the client to permit it to contact them. One way to do that is via “Credential Manager” or cmdkey as I had described above (or possibly also net use, but that may be SMB specific), but as you pointed out that appears to work only for specific server names, and therefore is inconvenient to set up if you have more than one server in your realm that you want to reach.

Another route, to enable Kerberos authentication for an entire remote realm, might be via the ksetup command, which is essentially what Windows 2000 introduced as Microsoft's alternative to /etc/krb5.conf. (However, I haven't tried the ksetup route yet with Windows 10 and OpenSSH, hence no worked example here yet. Stay tuned ...)

@maxadamo
Copy link

maxadamo commented Mar 3, 2021

Yes, it's correct what you're saying about SRV record, SPN, and so on.
Most of these settings are now being handled underneath by the tool and I don't care much about them any longer.
In the past with the MIT Kerberos server, I had to configure the keytab entries manually, but now with Windows AD and PowerBroker (or SSSD-AD) I only need to register the server against the Domain controller (by supplying a password).
And this is the main point: SERVER.EXAMPLE.COM has to be registered in advance against the domain controller (either if you use PowerBroker, Centrify, or SSSD-AD).
And if I got your point correctly @mgkuhn, there can't be evil servers, if they are not registered in the domain.

~> I could try to experiment with ksetup as well, but I know nothing about these tools.

p.s.: Windows 10 + Putty + GSSAPI + MIT Kerberos, will work the same way as Linux/Mac.

@mgkuhn
Copy link
Author

mgkuhn commented May 24, 2021

Further to what I wrote above, I have since learnt that both the Credential Manager configuration menu and the cmdkey tool in Windows 10 support wildcards in the target server name. Therefore, you can obtain Kerberos tickets for server1.example.com, server2.example.com, etc. after just once providing your domain password with

C:\> cmdkey /add:*.example.com /user:[email protected] /pass

and that will enable Kerberos-authenticated communication (both SMB and ssh -K) with all *.example.com servers in the DOMAIN.EXAMPLE.COM domain on client machines that are not joined to the DOMAIN.EXAMPLE.COM domain. (On domain-joined machines that works already without cmdkey.)

That makes cmdkey the closest equivalent to the kinit command from MIT Kerberos that I know.

However, I believe they don't do exactly the same thing:

  • kinit asks you for the password, gets a time-limited TGT with it, and then immediately forgets your password and the key derived from it. That's why you normally have to run kinit once every 10 hours to renew your ticket-granting-ticket.
  • cmdkey appears to actually store permanently the long-term key (keytab) derived from your password, and Windows then can use that from then on any time to get Kerberos tickets on your behalf. That's why you don't have to run it again.

So giving your password to cmdkey may be more dangerous on machines that might get stolen or compromised than giving it to kinit there. With kinit you typically have to trust the machine only for a limited time (typically the next 10 hours by default), with cmdkey and Credential Manager you may have to trust it until you next change your domain/realm password.

@jborean93
Copy link

While cmdkey doesn’t expose the functionality I’m fairly certain it is possible to store a credential that persists just for the current logon session rather than all the time. The underlying Win32 api CredWrite takes in a Credential and one of the Persist flags is CRED_PERSIST_SESSION. It requires some PInvoke work to call in something like PowerShell but it is possible.

@mgkuhn
Copy link
Author

mgkuhn commented May 24, 2021

@jborean93 Thanks for the link to the Persist field in Credential. I learnt there not only about the different forms wildcard syntax for the target (apparently DOMAIN.EXAMPLE.COM\* denotes all servers in that domain/realm). I also learnt something that horrified me quite a bit: the default persistence CRED_PERSIST_ENTERPRISE puts the (long-term key derived from my) password into my roaming profile! I.e. it gets copied and stays forever on every machine I ever log in.

I think CMDKEY should really be extended to add an option to provide access to all three of "session", "local machine" and "enterprise" persistence, and also explain in its man page in plain English what exactly these mean. The CMDKEY help text didn't make much sense to me without reading the API documentation.

I also think Microsoft ought to provide an implementation of kinit, i.e. a tool that asks for your Kerberos password, gets a TGT, and then immediately forgets the password and the key derived (hashed) from it. It seems the way Windows currently uses Kerberos is really as a drop-in replacement for NTLM authentication (keeping the password-derived key in memory, like NTLM has to). It therefore forfeits one of the main security advantages of Kerberos, namely that only time-limited secrets stay on a device. But it doesn't seem to be there is even an API in Windows to do that, or am I missing something?

@jborean93
Copy link

I also think Microsoft ought to provide an implementation of kinit

There sort of is one, klist. It’s not the same klist you get with MIT/Heimdal implementations but it does offer a klist get option to retrieve a service ticket for the SPN specified. I know this limits you to just a specific target rather than the TGT that kinit would give you but it could potentially be (ab)used to get a session key using different credentials when combined with LogonUser and ImpersonateLoggedOnUser.

@mgkuhn
Copy link
Author

mgkuhn commented May 25, 2021

cmdkey /add:DOMAIN.EXAMPLE.COM\* ... results in “invalid parameter”, so it seems that cmdkey does not currently support all the syntactic options allowed in the TargetName field of Credential.

Who is looking after cmdkey and Credential Manager these days? Do they have a GitHub project where one can file issues? (suggestion filed at uservoice.com)

mgkuhn added a commit to mgkuhn/windowsserverdocs that referenced this issue Aug 1, 2022
GSSAPIAuthentication was added with *OpenSSH for Windows 8.1* which came in “2021-05 Cumulative Update for Windows 10” (KB5003173), which is available for Windows 10 Version 20H1 or newer.
Source: PowerShell/Win32-OpenSSH#1693

GSSAPIAuthentication only supports Kerberos (i.e., not SPNEGO or NTLM authentication), so it is useful to mention Kerberos explicitly here.
Source: PowerShell/Win32-OpenSSH#1295
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Enhancement Feature request
Projects
None yet
Development

No branches or pull requests

6 participants