diff --git a/cmd/whawty-auth/config.go b/cmd/whawty-auth/config.go index e94db10..95bc2c6 100644 --- a/cmd/whawty-auth/config.go +++ b/cmd/whawty-auth/config.go @@ -51,23 +51,22 @@ type httpsConfig struct { TLS *tlsconfig.TLSConfig `yaml:"tls"` } -// type ldapConfig struct { -// Listen []string `yaml:"listen"` -// StartTLS bool `yaml:"start-tls"` -// TLS *tlsconfig.TLSConfig `yaml:"tls"` -// } +type ldapConfig struct { + Listen []string `yaml:"listen"` + TLS *tlsconfig.TLSConfig `yaml:"tls"` +} -// type ldapsConfig struct { -// Listen []string `yaml:"listen"` -// TLS *tlsconfig.TLSConfig `yaml:"tls"` -// } +type ldapsConfig struct { + Listen []string `yaml:"listen"` + TLS *tlsconfig.TLSConfig `yaml:"tls"` +} type listenerConfig struct { SASLAuthd *saslauthdConfig `yaml:"saslauthd"` HTTP *httpConfig `yaml:"http"` HTTPs *httpsConfig `yaml:"https"` - // LDAP *ldapConfig `yaml:"ldap"` - // LDAPs *ldapsConfig `yaml:"ldaps"` + LDAP *ldapConfig `yaml:"ldap"` + LDAPs *ldapsConfig `yaml:"ldaps"` } func readListenerConfig(configfile string) (*listenerConfig, error) { diff --git a/cmd/whawty-auth/ldap.go b/cmd/whawty-auth/ldap.go new file mode 100644 index 0000000..4f3d5cf --- /dev/null +++ b/cmd/whawty-auth/ldap.go @@ -0,0 +1,97 @@ +// +// Copyright (c) 2016 whawty contributors (see AUTHORS file) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of whawty.auth nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +package main + +import ( + "crypto/tls" + "net" + + "github.com/glauth/ldap" +) + +type ldapHandler struct { + store *Store +} + +func (h ldapHandler) Bind(bindDN, bindSimplePw string, conn net.Conn) (ldap.LDAPResultCode, error) { + if ok, _, _, _ := h.store.Authenticate(bindDN, bindSimplePw); !ok { + return ldap.LDAPResultInvalidCredentials, nil + } + return ldap.LDAPResultSuccess, nil +} + +func runLDAPsListener(listener *net.TCPListener, config *ldapsConfig, store *Store) error { + server := ldap.NewServer() + server.BindFunc("", ldapHandler{store: store}) + + tlsConfig, err := config.TLS.ToGoTLSConfig() + if err != nil { + return err + } + wl.Printf("ldap: listening on '%s' using TLS", listener.Addr()) + return server.Serve(tls.NewListener(listener, tlsConfig)) +} + +func runLDAPsAddr(addr string, config *ldapsConfig, store *Store) error { + if addr == "" { + addr = ":ldaps" + } + listener, err := net.Listen("tcp", addr) + if err != nil { + return err + } + return runLDAPsListener(listener.(*net.TCPListener), config, store) +} + +func runLDAPListener(listener *net.TCPListener, config *ldapConfig, store *Store) (err error) { + server := ldap.NewServer() + server.BindFunc("", ldapHandler{store: store}) + if config.TLS != nil { + if server.TLSConfig, err = config.TLS.ToGoTLSConfig(); err != nil { + return err + } + wl.Printf("ldap: listening on '%s' with StartTLS", listener.Addr()) + } else { + wl.Printf("ldap: listening on '%s'", listener.Addr()) + } + return server.Serve(listener) +} + +func runLDAPAddr(addr string, config *ldapConfig, store *Store) error { + if addr == "" { + addr = ":ldap" + } + listener, err := net.Listen("tcp", addr) + if err != nil { + return err + } + return runLDAPListener(listener.(*net.TCPListener), config, store) +} diff --git a/cmd/whawty-auth/main.go b/cmd/whawty-auth/main.go index 68c6fae..a9a1119 100644 --- a/cmd/whawty-auth/main.go +++ b/cmd/whawty-auth/main.go @@ -410,6 +410,30 @@ func cmdRun(c *cli.Context) error { }() } } + if lc.LDAP != nil { + for _, addr := range lc.LDAP.Listen { + a := addr + wg.Add(1) + go func() { + defer wg.Done() + if err := runLDAPAddr(a, lc.LDAP, s.GetInterface()); err != nil { + fmt.Printf("warning running web-api failed: %s\n", err) + } + }() + } + } + if lc.LDAPs != nil { + for _, addr := range lc.LDAPs.Listen { + a := addr + wg.Add(1) + go func() { + defer wg.Done() + if err := runLDAPsAddr(a, lc.LDAPs, s.GetInterface()); err != nil { + fmt.Printf("warning running web-api failed: %s\n", err) + } + }() + } + } wg.Wait() return cli.NewExitError(fmt.Sprintf("shutting down since all auth sockets have closed."), 0) @@ -496,6 +520,42 @@ func cmdRunSa(c *cli.Context) error { } }() } + case "ldap": + if lc.LDAP == nil { + fmt.Printf("ingoring unexpected socket for LDAP listener (no config found in listener-config)\n") + continue + } + for _, listener := range listeners { + ln, ok := listener.(*net.TCPListener) + if !ok { + fmt.Printf("ingoring invalid socket type %T for LDAP listener\n", listener) + } + wg.Add(1) + go func() { + defer wg.Done() + if err := runLDAPListener(ln, lc.LDAP, s.GetInterface()); err != nil { + fmt.Printf("warning running web-api failed: %s\n", err) + } + }() + } + case "ldaps": + if lc.LDAPs == nil { + fmt.Printf("ingoring unexpected socket for LDAPs listener (no config found in listener-config)\n") + continue + } + for _, listener := range listeners { + ln, ok := listener.(*net.TCPListener) + if !ok { + fmt.Printf("ingoring invalid socket type %T for LDAPs listener\n", listener) + } + wg.Add(1) + go func() { + defer wg.Done() + if err := runLDAPsListener(ln, lc.LDAPs, s.GetInterface()); err != nil { + fmt.Printf("warning running web-api failed: %s\n", err) + } + }() + } } } diff --git a/cmd/whawty-auth/web_api.go b/cmd/whawty-auth/web_api.go index ead8a0c..b47448b 100644 --- a/cmd/whawty-auth/web_api.go +++ b/cmd/whawty-auth/web_api.go @@ -543,6 +543,5 @@ func runHTTPAddr(addr string, config *httpConfig, store *Store) error { if err != nil { return err } - return runHTTPListener(listener.(*net.TCPListener), config, store) } diff --git a/contrib/listener-cfg.yml b/contrib/listener-cfg.yml index 75d8889..03015e7 100644 --- a/contrib/listener-cfg.yml +++ b/contrib/listener-cfg.yml @@ -44,14 +44,17 @@ https: # - x25519 # session-tickets: true # session-ticket-key: "b947e39f50e20351bdd81046e20fff7948d359a3aec391719d60645c5972cc77" -# ldap: -# listen: -# - 127.0.0.1:389 -# start-tls: true -# ldaps: -# listen: -# - 127.0.0.1:636 -# tls: -# certificate: "/path/to/server-crt.pem" -# certificate-key: "/path/to/server-key.pem" -# min-protocol-version: "TLSv1.2" +ldap: + listen: + - 127.0.0.1:389 + tls: ## if set start-tls is enabled + certificate: "/path/to/server-crt.pem" + certificate-key: "/path/to/server-key.pem" + min-protocol-version: "TLSv1.2" +ldaps: + listen: + - 127.0.0.1:636 + tls: + certificate: "/path/to/server-crt.pem" + certificate-key: "/path/to/server-key.pem" + min-protocol-version: "TLSv1.2" diff --git a/go.mod b/go.mod index 92cb399..0edfe9d 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,8 @@ require ( github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/fatih/color v1.15.0 // indirect + github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/kr/pretty v0.2.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.18 // indirect diff --git a/go.sum b/go.sum index 4e2935b..52edc0a 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e h1:ohe1O2DUo198delHsQvA8O+CgAPP8wv1SmSxLPlO9ao= +github.com/glauth/ldap v0.0.0-20231210225823-b9bf4d1baf6e/go.mod h1:EHMcFuVSIs0huelHOhaGtj9IKMB/Dmxo+jDheipjtYA= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=