mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2024-11-10 07:04:24 +00:00
implement indeterminate LDAP verification (#1574)
This PR implements tri-state verification for the LDAP detector. This implementation looks for network errors to explicitly flag as indeterminate, rather than authentication errors to explicitly flag as determinate; this is because the error that occurs from authentication failures doesn't appear to have its own type and I didn't want to have to match on the error message text.
This commit is contained in:
parent
e322c4b29d
commit
d763097fdf
2 changed files with 82 additions and 37 deletions
|
@ -3,6 +3,8 @@ package ldap
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
@ -63,7 +65,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||
}
|
||||
|
||||
if verify {
|
||||
s1.Verified = verifyLDAP(ctx, username[1], password[1], ldapURL)
|
||||
verificationErr := verifyLDAP(username[1], password[1], ldapURL)
|
||||
s1.Verified = verificationErr == nil
|
||||
if !isErrDeterminate(verificationErr) {
|
||||
s1.VerificationError = verificationErr
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
|
@ -89,7 +95,12 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||
}
|
||||
|
||||
if verify {
|
||||
s1.Verified = verifyLDAP(ctx, username, password, ldapURL)
|
||||
verificationError := verifyLDAP(username, password, ldapURL)
|
||||
|
||||
s1.Verified = verificationError == nil
|
||||
if !isErrDeterminate(verificationError) {
|
||||
s1.VerificationError = verificationError
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
|
@ -98,7 +109,19 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||
return results, nil
|
||||
}
|
||||
|
||||
func verifyLDAP(ctx context.Context, username, password string, ldapURL *url.URL) bool {
|
||||
func isErrDeterminate(err error) bool {
|
||||
switch e := err.(type) {
|
||||
case *ldap.Error:
|
||||
switch e.Err.(type) {
|
||||
case *net.OpError:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func verifyLDAP(username, password string, ldapURL *url.URL) error {
|
||||
// Tests with non-TLS, TLS, and STARTTLS
|
||||
|
||||
ldap.DefaultTimeout = 5 * time.Second
|
||||
|
@ -109,38 +132,35 @@ func verifyLDAP(ctx context.Context, username, password string, ldapURL *url.URL
|
|||
case "ldap":
|
||||
// Non-TLS dial
|
||||
l, err := ldap.DialURL(uri)
|
||||
if err == nil {
|
||||
defer l.Close()
|
||||
// Non-TLS verify
|
||||
err = l.Bind(username, password)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// STARTTLS
|
||||
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
|
||||
if err == nil {
|
||||
// STARTTLS verify
|
||||
err = l.Bind(username, password)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
// Non-TLS verify
|
||||
err = l.Bind(username, password)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// STARTTLS
|
||||
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// STARTTLS verify
|
||||
return l.Bind(username, password)
|
||||
case "ldaps":
|
||||
// TLS dial
|
||||
l, err := ldap.DialTLS("tcp", uri, &tls.Config{InsecureSkipVerify: true})
|
||||
if err == nil {
|
||||
defer l.Close()
|
||||
// TLS verify
|
||||
err = l.Bind(username, password)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
// TLS verify
|
||||
return l.Bind(username, password)
|
||||
}
|
||||
|
||||
return false
|
||||
return fmt.Errorf("unknown ldap scheme %q", ldapURL.Scheme)
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
|
|
|
@ -7,13 +7,14 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
|
@ -60,11 +61,12 @@ func TestLdap_Integration_FromChunk(t *testing.T) {
|
|||
verify bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
s Scanner
|
||||
args args
|
||||
want []detectors.Result
|
||||
wantErr bool
|
||||
name string
|
||||
s Scanner
|
||||
args args
|
||||
want []detectors.Result
|
||||
wantErr bool
|
||||
wantVerificationErr bool
|
||||
}{
|
||||
{
|
||||
name: "found with URI and separate user+password usage, verified",
|
||||
|
@ -152,6 +154,26 @@ func TestLdap_Integration_FromChunk(t *testing.T) {
|
|||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "inaccessible host",
|
||||
s: Scanner{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
data: []byte(`
|
||||
ldap://badhost:1389
|
||||
binddn="cn=admin,dc=example,dc=org"
|
||||
pass="P@55w0rd"`),
|
||||
verify: true,
|
||||
},
|
||||
want: []detectors.Result{
|
||||
{
|
||||
DetectorType: detectorspb.DetectorType_LDAP,
|
||||
Verified: false,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
wantVerificationErr: true,
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
s: Scanner{},
|
||||
|
@ -176,9 +198,12 @@ func TestLdap_Integration_FromChunk(t *testing.T) {
|
|||
if len(got[i].Raw) == 0 {
|
||||
t.Fatalf("no raw secret present: \n %+v", got[i])
|
||||
}
|
||||
got[i].Raw = nil
|
||||
if (got[i].VerificationError != nil) != tt.wantVerificationErr {
|
||||
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError)
|
||||
}
|
||||
}
|
||||
if diff := pretty.Compare(got, tt.want); diff != "" {
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "VerificationError")
|
||||
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
|
||||
t.Errorf("Ldap.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
})
|
||||
|
@ -205,7 +230,7 @@ func startOpenLDAP() error {
|
|||
return nil
|
||||
case <-time.After(30 * time.Second):
|
||||
stopOpenLDAP()
|
||||
return errors.New("timeout waiting for postgres database to be ready")
|
||||
return errors.New("timeout waiting for ldap service to be ready")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue