mirror of
https://github.com/netauth/ldap.git
synced 2026-11-03 11:43:44 -06:00
148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
package ldap
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"math/big"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// writeSelfSignedTLSMaterial creates a throwaway certificate and key
|
|
// on disk for the TLS serve tests.
|
|
func writeSelfSignedTLSMaterial(t *testing.T) (certPath, keyPath string) {
|
|
t.Helper()
|
|
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatalf("generating key: %s", err)
|
|
}
|
|
tmpl := &x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{CommonName: "ldap-bridge-test"},
|
|
NotBefore: time.Now().Add(-time.Hour),
|
|
NotAfter: time.Now().Add(time.Hour),
|
|
}
|
|
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
|
|
if err != nil {
|
|
t.Fatalf("creating certificate: %s", err)
|
|
}
|
|
keyDER, err := x509.MarshalECPrivateKey(key)
|
|
if err != nil {
|
|
t.Fatalf("marshaling key: %s", err)
|
|
}
|
|
|
|
certPath = filepath.Join(t.TempDir(), "cert.pem")
|
|
keyPath = filepath.Join(t.TempDir(), "key.pem")
|
|
if err := os.WriteFile(certPath, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), 0600); err != nil {
|
|
t.Fatalf("writing cert: %s", err)
|
|
}
|
|
if err := os.WriteFile(keyPath, pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER}), 0600); err != nil {
|
|
t.Fatalf("writing key: %s", err)
|
|
}
|
|
return certPath, keyPath
|
|
}
|
|
|
|
// freeAddr reserves an ephemeral port on loopback and returns its
|
|
// address. The listener is closed immediately; the caller is expected
|
|
// to hand the address to the server under test.
|
|
func freeAddr(t *testing.T) string {
|
|
t.Helper()
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("reserving a port: %s", err)
|
|
}
|
|
addr := ln.Addr().String()
|
|
if err := ln.Close(); err != nil {
|
|
t.Fatalf("releasing port: %s", err)
|
|
}
|
|
return addr
|
|
}
|
|
|
|
func TestServeListensAndServeStops(t *testing.T) {
|
|
s := New(WithLogger(testLogger()), WithNetAuth(&fakeClient{}))
|
|
if err := s.SetDomain("example.com"); err != nil {
|
|
t.Fatalf("SetDomain: %s", err)
|
|
}
|
|
|
|
addr := freeAddr(t)
|
|
if err := s.Serve(addr); err != nil {
|
|
t.Fatalf("Serve: %s", err)
|
|
}
|
|
// Closing the listener stops the serve loop. The embedded
|
|
// server's own Stop cannot be used under -race because it
|
|
// closes a done channel that the accept loop reads without a
|
|
// synchronising edge.
|
|
defer s.Listener.Close()
|
|
|
|
conn, err := net.Dial("tcp", addr)
|
|
if err != nil {
|
|
t.Fatalf("dialing server: %s", err)
|
|
}
|
|
conn.Close()
|
|
}
|
|
|
|
func TestServeReportsBadAddress(t *testing.T) {
|
|
s := New(WithLogger(testLogger()), WithNetAuth(&fakeClient{}))
|
|
if err := s.Serve("256.256.256.256:0"); err == nil {
|
|
t.Fatal("expected an error for an invalid bind address")
|
|
}
|
|
}
|
|
|
|
func TestServeTLSRequiresUsableKeyPair(t *testing.T) {
|
|
s := New(WithLogger(testLogger()), WithNetAuth(&fakeClient{}))
|
|
if err := s.ServeTLS("127.0.0.1:0", "/nope/not-here.key", "/nope/not-here.crt"); err == nil {
|
|
t.Fatal("expected an error for a missing key pair")
|
|
}
|
|
}
|
|
|
|
// TestServeTLSCapableNegotiation checks that the bridge negotiates a
|
|
// modern TLS version and refuses legacy ones, which the vendored
|
|
// server made impossible by hard coding an SSL 3.0 floor.
|
|
func TestServeTLSNegotiatesModernVersions(t *testing.T) {
|
|
certPath, keyPath := writeSelfSignedTLSMaterial(t)
|
|
|
|
s := New(WithLogger(testLogger()), WithNetAuth(&fakeClient{}))
|
|
if err := s.SetDomain("example.com"); err != nil {
|
|
t.Fatalf("SetDomain: %s", err)
|
|
}
|
|
addr := freeAddr(t)
|
|
if err := s.ServeTLS(addr, keyPath, certPath); err != nil {
|
|
t.Fatalf("ServeTLS: %s", err)
|
|
}
|
|
defer s.Listener.Close()
|
|
|
|
conn, err := tls.Dial("tcp", addr, &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
MinVersion: tls.VersionTLS12,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("TLS 1.2 handshake: %s", err)
|
|
}
|
|
if v := conn.ConnectionState().Version; v < tls.VersionTLS12 {
|
|
t.Errorf("negotiated TLS version %x, want >= TLS 1.2", v)
|
|
}
|
|
conn.Close()
|
|
|
|
for _, ver := range []uint16{tls.VersionSSL30, tls.VersionTLS10, tls.VersionTLS11} {
|
|
conn, err := tls.Dial("tcp", addr, &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
MinVersion: ver,
|
|
MaxVersion: ver,
|
|
})
|
|
if err == nil {
|
|
conn.Close()
|
|
t.Errorf("legacy version %x was accepted, want rejection", ver)
|
|
}
|
|
}
|
|
}
|