diff options
Diffstat (limited to 'server_test.go')
| -rw-r--r-- | server_test.go | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/server_test.go b/server_test.go index 29df06b..991d927 100644 --- a/server_test.go +++ b/server_test.go @@ -2,18 +2,235 @@ package ldapserver import ( "bytes" + "crypto/rsa" "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "io/ioutil" "log" + "math/big" "net" + "os" "os/exec" + "runtime" "strings" "testing" "time" + + "github.com/golang/go/src/crypto/rand" ) var timeout = 400 * time.Millisecond var serverBaseDN = "o=testers,c=test" +type selfSignedCert struct { + // Path to the SSL certificates. + CACertPath, CertPath string + + // Path to the private keys for the SSL certificates. + CAKeyPath, KeyPath string +} + +func newSelfSignedCert() *selfSignedCert { + capk, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + panic(err) + } + + caSerial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + panic(err) + } + + caTemplate := x509.Certificate{ + SerialNumber: caSerial, + NotBefore: time.Now(), + NotAfter: time.Now().Add(7 * 24 * time.Hour), + + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + + BasicConstraintsValid: true, + + Subject: pkix.Name{ + Organization: []string{"my_test_ca"}, + CommonName: "My Test CA", + }, + + IsCA: true, + } + + caCert, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, capk.Public(), capk) + if err != nil { + panic(err) + } + // fmt.Printf("CA CERT\n%#v\n", caCert) + caCertPEM := &pem.Block{Type: "CERTIFICATE", Bytes: caCert} + caCertFile, err := ioutil.TempFile("", "cacert-*.pem") + if err != nil { + panic(err) + } + if err := pem.Encode(caCertFile, caCertPEM); err != nil { + panic(err) + } + caCertFile.Close() + + caKeyFile, err := ioutil.TempFile("", "cakey-*.pem") + if err != nil { + panic(err) + } + if err := pem.Encode(caKeyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(capk)}); err != nil { + panic(err) + } + caKeyFile.Close() + + serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + panic(err) + } + // Basically the same as the CA template, but its own serial, and with ip addresses and dns names. + template := x509.Certificate{ + SerialNumber: serial, + NotBefore: time.Now(), + NotAfter: time.Now().Add(7 * 24 * time.Hour), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + + BasicConstraintsValid: true, + + Subject: pkix.Name{ + CommonName: "localhost", + }, + + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}, + DNSNames: []string{"localhost"}, + } + + pk, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + panic(err) + } + cert, err := x509.CreateCertificate(rand.Reader, &template, &caTemplate, pk.Public(), capk) + if err != nil { + panic(err) + } + certPEM := &pem.Block{Type: "CERTIFICATE", Bytes: cert} + certFile, err := ioutil.TempFile("", "sslcert-*.pem") + if err != nil { + panic(err) + } + if err := pem.Encode(certFile, certPEM); err != nil { + panic(err) + } + certFile.Close() + + keyFile, err := ioutil.TempFile("", "key-*.pem") + if err != nil { + panic(err) + } + if err := pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}); err != nil { + panic(err) + } + keyFile.Close() + + return &selfSignedCert{ + CACertPath: caCertFile.Name(), + CAKeyPath: caKeyFile.Name(), + CertPath: certFile.Name(), + KeyPath: keyFile.Name(), + } +} + +func (c *selfSignedCert) cleanup() { + os.RemoveAll(c.CertPath) + os.RemoveAll(c.CACertPath) + os.RemoveAll(c.KeyPath) + os.RemoveAll(c.CAKeyPath) +} + +func (c *selfSignedCert) ClientTLSConfig() *tls.Config { + cert, err := ioutil.ReadFile(c.CACertPath) + if err != nil { + panic(err) + } + + // Return a TLS config that trusts our self-generated CA. + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM(cert) { + panic("failed to append certificate") + } + return &tls.Config{ + RootCAs: pool, + } +} + +func (c *selfSignedCert) ServerTLSConfig() *tls.Config { + cert, err := tls.LoadX509KeyPair(c.CertPath, c.KeyPath) + if err != nil { + panic(err) + } + return &tls.Config{ + ServerName: "localhost", + Certificates: []tls.Certificate{cert}, + } +} + +func TestStartTLS(t *testing.T) { + if runtime.GOOS == "darwin" { + defer func() { + if t.Failed() { + t.Logf(`NOTE: this test won't pass with the built-in Mac ldap utilities. +Work around this by using brew install openldap, and running the test as PATH=/usr/local/opt/openldap/bin:$PATH go test. + +This test uses environment variables that are respected by OpenLDAP, but the Mac utilities don't let you override +security settings through environment variables; they expect certificates to be added to the system keychain, +which is very heavy-handed for a test like this. +`) + } + }() + } + cert := newSelfSignedCert() + defer cert.cleanup() + + s := NewServer() + defer s.Close() + s.Bind = BindAnonOK + s.Search = SearchSimple + s.TLSConfig = cert.ServerTLSConfig() + + ln, addr := mustListen() + go func() { + if err := s.Serve(ln); err != nil { + t.Errorf("s.Serve failed: %s", err.Error()) + } + }() + + done := make(chan struct{}) + go func() { + cmd := exec.Command("env", + "LDAPTLS_CACERT="+cert.CACertPath, + "ldapsearch", "-H", "ldap://"+addr, "-ZZ", "-d", "-1", "-x", "-b", "o=testers,c=test") + out, err := cmd.CombinedOutput() + if err != nil { + t.Error(err) + } + + if !strings.Contains(string(out), "# numEntries: 3") || !strings.Contains(string(out), "result: 0 Success") { + t.Errorf("search did not succeed:\n%s", out) + } + + close(done) + }() + + select { + case <-done: + case <-time.After(timeout): + t.Error("ldapsearch command timed out") + } +} + ///////////////////////// func TestBindAnonOK(t *testing.T) { done := make(chan bool) |
