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 SSPI GSSAPI SASL mechanism bind #402

Merged
merged 4 commits into from
Dec 12, 2022

Conversation

FlipB
Copy link
Contributor

@FlipB FlipB commented Nov 4, 2022

This change implements GSSAPI SASL bind using a given GSSAPI (Kerberos) client.

GSSAPI client interface (ldap.GSSAPIClient) is inspired by golang.org/x/crypto/ssh.GSSAPIClient with the following definition:

type GSSAPIClient interface {
	InitSecContext(target string, token []byte) (outputToken []byte, needContinue bool, err error)
	NegotiateSaslAuth(token []byte, authzid string) ([]byte, error)
	DeleteSecContext() error
}

A client using Windows SSPI is included (gssapi.SSPIClient).
This client allows Windows clients to use current process' credentials for bind authentication.

This implementation does not support SASL security layers. Using this implementation (even with TLS) may be a bad idea, see https://wiki.samba.org/index.php/Configuring_LDAP_over_SSL_(LDAPS)_on_a_Samba_AD_DC.

Example:

package main

import (
	"fmt"
	"log"

	"github.com/go-ldap/ldap/v3"
	"github.com/go-ldap/ldap/v3/gssapi"
)

func main() {
	adHost := "addc.domainname.com"
	baseDnToQuery := "dc=domainname,dc=com"
	query := "(samaccountname=*bob*)"

	kerberosClient, err := gssapi.NewSSPIClient()
	if err != nil {
		log.Fatalf("error getting SSPI Kerberos client: %v", err)
	}
	defer kerberosClient.Close()

	// Connect to Active directory at `adHost`
	conn, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", adHost))
	if err != nil {
		log.Fatalf("error connecting to AD: %v", err)
	}

	// Bind using supplied kerberosClient against `adHost`
	err = conn.GSSAPIBind(kerberosClient, fmt.Sprintf("ldap/%s", adHost), "")
	if err != nil {
		log.Fatalf("error performing GSSAPI bind: %w", err)
	}

	// Successfully bound as current process' user.
	q := ldap.NewSearchRequest(baseDnToQuery,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		query, []string{}, nil)
	result, err := conn.Search(q)
	if err != nil {
		log.Fatalf("error querying AD: %v", err)
	}

	log.Printf("Result: %+v\n", *result)
}

This is a partial implementation for #115 (Since only Windows Kerberos client implementation is included).

This change allows Windows clients to use current process' credentials
for bind authentication.
// The returned token will be sent to the server and the handshake considered
// completed successfully and the server authenticated.
// See RFC 4752 section 3.1.
NegotiateSaslAuth(token []byte, authzid string) ([]byte, error)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to expose more details from the GSSAPI implementation (eg. Wrap, Unwrap) then the interface could be used to implement security layers.

Problem is that a lot of details has to be exposed over the interface if the negotiation is to be implemented on the ldap package side (max buffer sizes etc.).

@tooptoop4
Copy link

@FlipB do u have example have how to use this feature? also how to retrieve username if auth was successful?

@FlipB
Copy link
Contributor Author

FlipB commented Nov 16, 2022

@tooptoop4 I have included an example above.

I haven't looked at any way to retreive the authenticated users name from ldap, but on the client it should be the same user as returned from eg. os/user's Current().

@cpuschma
Copy link
Member

Hi,

Apologies for the absence, a lot has happened in the last few days that needed my full attention. I have scrolled through the source code and so far have not found any changes that I would have to reject. When I'm in the office next week I'll check the functionality, I don't have an Active Directory at hand right now.

Admittedly, I am not familiar with kerberos and gssapi. If someone who knows about it could contact me that would be cool to check if the implementation is correct.

Until then :)

Copy link
Member

@cpuschma cpuschma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested your PR with the example you provided, it worked as intended! Great work! Can you please add an test as an example for users who want to use this feature? The functions and the parameters are really self explainatory

@FlipB
Copy link
Contributor Author

FlipB commented Dec 2, 2022

I tested your PR with the example you provided, it worked as intended! Great work! Can you please add an test as an example for users who want to use this feature? The functions and the parameters are really self explainatory

Good to hear it worked with your setup as well, I've only tested against one Active directory instance.

I've added an example.

@cpuschma
Copy link
Member

Might be better to expose more details from the GSSAPI implementation (eg. Wrap, Unwrap) then the interface could be used to implement security layers. Problem is that a lot of details has to be exposed over the interface if the negotiation is to be implemented on the ldap package side (max buffer sizes etc.).

I think we can merge the PR in the current version and expose certain methods in later releases if we want to. Again, thank you for this PR :)

@cpuschma cpuschma merged commit 0e43630 into go-ldap:master Dec 12, 2022
inv2004 pushed a commit to inv2004/ldap that referenced this pull request Jan 17, 2023
* Add support for SSPI GSSAPI SASL mechanism bind

This change allows Windows clients to use current process' credentials
for bind authentication.

Co-authored-by: Filip Björck <filipbj@axis.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants