diff --git a/go.mod b/go.mod index ff45457..2bf8f4c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( github.com/hashicorp/go-hclog v0.9.2 + github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect github.com/netauth/netauth v0.3.4 github.com/ps78674/goldap v0.0.0-20200721080011-cd2e7ee23841 github.com/ps78674/ldapserver v0.0.0-20200521101606-2395f680392c diff --git a/go.sum b/go.sum index 86a8691..c1863fd 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A= +github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= diff --git a/internal/buildinfo/info.go b/internal/buildinfo/info.go new file mode 100644 index 0000000..f756b1b --- /dev/null +++ b/internal/buildinfo/info.go @@ -0,0 +1,6 @@ +package buildinfo + +var ( + // Version is the version this server. + Version = "dev" +) diff --git a/internal/ldap/bind.go b/internal/ldap/bind.go index a76e15a..8d6bbd1 100644 --- a/internal/ldap/bind.go +++ b/internal/ldap/bind.go @@ -22,7 +22,16 @@ func (s *server) handleBind(w ldap.ResponseWriter, m *ldap.Message) { s.l.Debug("Bind from dn", "dn", r.Name()) - if err := s.c.AuthEntity(ctx, entityIDFromDN(r.Name()), string(r.AuthenticationSimple())); err != nil { + entityID, err := s.entityIDFromDN(r.Name()) + if err != nil { + res := ldap.NewBindResponse(ldap.LDAPResultInvalidDNSyntax) + res.SetDiagnosticMessage(err.Error()) + s.l.Warn("Request with invalid DN", "dn", r.Name()) + w.Write(res) + return + } + + if err := s.c.AuthEntity(ctx, entityID, string(r.AuthenticationSimple())); err != nil { res := ldap.NewBindResponse(ldap.LDAPResultInvalidCredentials) res.SetDiagnosticMessage("invalid credentials") w.Write(res) diff --git a/internal/ldap/ldap.go b/internal/ldap/ldap.go index 0bafc29..3fca128 100644 --- a/internal/ldap/ldap.go +++ b/internal/ldap/ldap.go @@ -3,6 +3,7 @@ package ldap import ( "os" "os/signal" + "strings" "syscall" "github.com/hashicorp/go-hclog" @@ -79,3 +80,17 @@ func (s *server) handleAbandon(w ldap.ResponseWriter, m *ldap.Message) { requestToAbandon.Abandon() } } + +func (s *server) SetDomain(domain string) { + nc := "dc=netauth," + parts := strings.Split(domain, ".") + for i := range parts { + nc += "dc=" + parts[i] + "," + } + nc = strings.TrimSuffix(nc, ",") + + s.nc = strings.Split(nc, ",") + for i := range s.nc { + s.nc[i] = strings.TrimSpace(s.nc[i]) + } +} diff --git a/internal/ldap/search.go b/internal/ldap/search.go index a3f5c50..fbddf5e 100644 --- a/internal/ldap/search.go +++ b/internal/ldap/search.go @@ -2,17 +2,23 @@ package ldap import ( "log" + "strings" + "github.com/ps78674/goldap/message" ldap "github.com/ps78674/ldapserver" + + "github.com/netauth/ldap/internal/buildinfo" ) func (s *server) handleSearchDSE(w ldap.ResponseWriter, m *ldap.Message) { + nc := strings.Join(s.nc, ", ") + e := ldap.NewSearchResultEntry("") e.AddAttribute("vendorName", "NetAuth") - e.AddAttribute("vendorVersion", "1.0") + e.AddAttribute("vendorVersion", message.AttributeValue(buildinfo.Version)) e.AddAttribute("objectClass", "top", "extensibleObject") e.AddAttribute("supportedLDAPVersion", "3") - e.AddAttribute("namingContexts", "o=My Company, c=US") + e.AddAttribute("namingContexts", message.AttributeValue(nc)) w.Write(e) res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess) diff --git a/internal/ldap/type.go b/internal/ldap/type.go index 9760744..78f71d6 100644 --- a/internal/ldap/type.go +++ b/internal/ldap/type.go @@ -16,4 +16,6 @@ type server struct { c naClient l hclog.Logger + + nc []string } diff --git a/internal/ldap/util.go b/internal/ldap/util.go index 475d263..e8e5501 100644 --- a/internal/ldap/util.go +++ b/internal/ldap/util.go @@ -1,9 +1,34 @@ package ldap import ( + "errors" + "strings" + "github.com/ps78674/goldap/message" ) -func entityIDFromDN(message.LDAPDN) string { - return "" +// entityIDFromDN parses out an ID from a given DN. This only works +// for ID's that don't contain an escaped comma, but these aren't +// valid in NetAuth anyway, so this is taken as a known defect. +func (s *server) entityIDFromDN(dn message.LDAPDN) (string, error) { + parts := strings.Split(string(dn), ",") + for i := range parts { + parts[i] = strings.TrimSpace(parts[i]) + } + + if !strings.HasPrefix(parts[0], "uid=") { + return "", errors.New("entity DN must start with uid=") + } + + if parts[1] != "cn=entities" { + return "", errors.New("entity DN is underneath cn=entities") + } + + for i, p := range parts[2:] { + if p != s.nc[i] { + return "", errors.New("queries must be rooted at " + strings.Join(s.nc, ",")) + } + } + + return strings.TrimPrefix(parts[0], "uid="), nil } diff --git a/main.go b/main.go index 645e602..093bb09 100644 --- a/main.go +++ b/main.go @@ -42,6 +42,9 @@ func main() { } ls := ldap.New(appLogger, nacl) + + ls.SetDomain("netauth.org") + if err := ls.Serve("localhost:10389"); err != nil { appLogger.Error("Error serving", "error", err) return