diff --git a/credentials.go b/credentials.go index d17d770..6c63092 100644 --- a/credentials.go +++ b/credentials.go @@ -153,7 +153,7 @@ func (c *Credential) DC(ctx context.Context, protocol string) (*Target, error) { } func (c *Credential) mustUseKerberos() bool { - return c.Password == "" && c.NTHash == "" && (c.CCache != "" || c.AESKey != "" || c.ClientCert != nil) + return c.Password == "" && c.NTHash == "" && (c.CCache != "" || c.AESKey != "") } // Keytab returns the Kerberos keytab containing the AES key and/or NT hash if diff --git a/dcerpcauth/dcerpcauth.go b/dcerpcauth/dcerpcauth.go index 124aab9..6ed19a1 100644 --- a/dcerpcauth/dcerpcauth.go +++ b/dcerpcauth/dcerpcauth.go @@ -59,7 +59,7 @@ func AuthenticationOptions( dcerpcOptions = append(dcerpcOptions, dcerpc.WithMechanism(ssp.SPNEGO)) switch { - case target.UseKerberos: + case target.UseKerberos || creds.ClientCert != nil: spn, err := target.SPN(ctx) if err != nil { return nil, fmt.Errorf("build SPN: %w", err) diff --git a/ldapauth/ldap.go b/ldapauth/ldap.go index d5cd9d5..5485e7c 100644 --- a/ldapauth/ldap.go +++ b/ldapauth/ldap.go @@ -13,6 +13,7 @@ import ( "encoding/binary" "encoding/pem" "fmt" + "net" "os" "strings" "time" @@ -99,6 +100,15 @@ func ConnectTo( return nil, fmt.Errorf("configure TLS: %w", err) } + if !ldapOpts.TLSConfig.InsecureSkipVerify && net.ParseIP(target.AddressWithoutPort()) != nil { + hostname, err := target.Hostname(ctx) + if err != nil { + return nil, fmt.Errorf("determine target hostname for TLS verification: %w", err) + } + + opts.TLSConfig.ServerName = hostname + } + conn, err = connect(target, opts) if err != nil { return nil, err @@ -213,18 +223,12 @@ func bind( case creds.ClientCert != nil && strings.EqualFold(opts.Scheme, "ldap"): opts.Debug("authenticating with client certificate via StartTLS") - if !opts.TLSConfig.InsecureSkipVerify { - hostname, err := target.Hostname(ctx) + _, ok := conn.TLSConnectionState() + if !ok { + err = conn.StartTLS(opts.TLSConfig) if err != nil { - return fmt.Errorf("determine target hostname for TLS verification: %w", err) + return fmt.Errorf("StartTLS: %w", err) } - - opts.TLSConfig.ServerName = hostname - } - - err = conn.StartTLS(opts.TLSConfig) - if err != nil { - return fmt.Errorf("StartTLS: %w", err) } err = conn.ExternalBind() diff --git a/options_test.go b/options_test.go index 6f57dd9..763de36 100644 --- a/options_test.go +++ b/options_test.go @@ -266,7 +266,7 @@ func TestKerberos(t *testing.T) { User: upn, PFXFileName: "testdata/someuser@domain.tld.pfx", }, - ShouldUseKerberos: true, + ShouldUseKerberos: false, }, { Opts: adauth.Options{ diff --git a/target.go b/target.go index 0e14f9c..f59f7d0 100644 --- a/target.go +++ b/target.go @@ -20,6 +20,14 @@ type Target struct { ip net.IP // UseKerberos indicated that Kerberos authentication should be used to // authenticate to this target. + // + // Warning: `UseKerberos` is false when the only credential available is a + // client certificate because in this case mTLS may also be used to + // authenticate depending on the protocol (e.g. LDAP/HTTPS). If the protocol + // that is used does not support using client certificates directly, you + // should decide for Kerberos authentication if `target.UserKerberos && + // creds.ClientCert != nil` is `true`. In this case, Kerberos with PKINIT + // will be used. UseKerberos bool // Protocol is a string that represents the protocol that is used when // communicating with this target. It is used to construct the SPN, however,