aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--TODO.md6
-rw-r--r--bottin.hcl.example2
-rw-r--r--main.go12
-rw-r--r--read.go18
-rw-r--r--util.go28
-rw-r--r--write.go49
7 files changed, 82 insertions, 42 deletions
diff --git a/README.md b/README.md
index a50f167..48c988c 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,6 @@ Features:
- Access control through an ACL (hardcoded in the configuration file)
-Building Bottin can be done simply by running `go build` in this folder.
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/lxpz/bottin_amd64).
An example for running Bottin on a Nomad cluster can be found in `bottin.hcl.example`.
@@ -26,6 +25,14 @@ The configuration file is a JSON file whose contents is described in the followi
Bottin is licensed under the terms of the GPLv3.
+## Building Bottin
+
+Bottin requires go 1.13 or later.
+
+To build Bottin, clone this repository outside of your `$GOPATH`.
+Then, run `make` in the root of the repo.
+
+
## Server initialization
When Bottin is launched on an empty database,
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..d9fcafa
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,6 @@
+- Switch to `go mod` for building Bottin
+
+- Implement missing search filters (in applyFilter)
+- Add an initial prefix to the consul key value
+
+- Potential bugs with different combinations of lower/uppercase names
diff --git a/bottin.hcl.example b/bottin.hcl.example
index 8305a01..eb29095 100644
--- a/bottin.hcl.example
+++ b/bottin.hcl.example
@@ -12,7 +12,7 @@ job "directory" {
task "server" {
driver = "docker"
config {
- image = "lxpz/bottin_amd64:4"
+ image = "lxpz/bottin_amd64:6"
readonly_rootfs = true
port_map {
ldap_port = 389
diff --git a/main.go b/main.go
index 7939733..a9b8031 100644
--- a/main.go
+++ b/main.go
@@ -1,8 +1,5 @@
package main
-// @FIXME: Proper handling of various upper/lower case combinations
-// @FIXME: Implement missing search filters (in applyFilter)
-// @FIXME: Add an initial prefix to the consul key value
import (
"crypto/rand"
@@ -381,7 +378,14 @@ func (server *Server) objectExists(dn string) (bool, error) {
return len(data) > 0, nil
}
-func (server *Server) checkSuffix(dn string, allow_extend bool) (string, error) {
+func (server *Server) checkDN(dn string, allow_extend bool) (string, error) {
+ // 1. Canonicalize: remove spaces between things
+ dn, err := canonicalDN(dn)
+ if err != nil {
+ return "", err
+ }
+
+ // 2. Check suffix (add it if allow_extend is set)
suffix := server.config.Suffix
if len(dn) < len(suffix) {
if dn != suffix[len(suffix)-len(dn):] || !allow_extend {
diff --git a/read.go b/read.go
index d3bb359..ef06109 100644
--- a/read.go
+++ b/read.go
@@ -25,11 +25,10 @@ func (server *Server) handleCompare(s ldap.UserState, w ldap.ResponseWriter, m *
}
func (server *Server) handleCompareInternal(state *State, r *message.CompareRequest) (int, error) {
- dn := string(r.Entry())
attr := string(r.Ava().AttributeDesc())
expected := string(r.Ava().AssertionValue())
- _, err := server.checkSuffix(dn, false)
+ dn, err := server.checkDN(string(r.Entry()), false)
if err != nil {
return ldap.LDAPResultInvalidDNSyntax, err
}
@@ -82,21 +81,22 @@ func (server *Server) handleSearch(s ldap.UserState, w ldap.ResponseWriter, m *l
func (server *Server) handleSearchInternal(state *State, w ldap.ResponseWriter, r *message.SearchRequest) (int, error) {
+ baseObject, err := server.checkDN(string(r.BaseObject()), true)
+ if err != nil {
+ return ldap.LDAPResultInvalidDNSyntax, err
+ }
+
server.logger.Tracef("-- SEARCH REQUEST: --")
- server.logger.Tracef("Request BaseDn=%s", r.BaseObject())
+ server.logger.Tracef("Request BaseDn=%s", baseObject)
server.logger.Tracef("Request Filter=%s", r.Filter())
server.logger.Tracef("Request FilterString=%s", r.FilterString())
server.logger.Tracef("Request Attributes=%s", r.Attributes())
server.logger.Tracef("Request TimeLimit=%d", r.TimeLimit().Int())
- if !server.config.Acl.Check(&state.login, "read", string(r.BaseObject()), []string{}) {
+ if !server.config.Acl.Check(&state.login, "read", baseObject, []string{}) {
return ldap.LDAPResultInsufficientAccessRights, fmt.Errorf("Please specify a base object on which you have read rights")
}
- baseObject, err := server.checkSuffix(string(r.BaseObject()), true)
- if err != nil {
- return ldap.LDAPResultInvalidDNSyntax, err
- }
basePath, err := dnToConsul(baseObject)
if err != nil {
return ldap.LDAPResultInvalidDNSyntax, err
@@ -206,7 +206,7 @@ func applyFilter(entry Entry, filter message.Filter) (bool, error) {
for entry_desc, value := range entry {
if strings.EqualFold(entry_desc, desc) {
for _, val := range value {
- if val == target {
+ if valueMatch(entry_desc, val, target) {
return true, nil
}
}
diff --git a/util.go b/util.go
index d778bc2..96bb00b 100644
--- a/util.go
+++ b/util.go
@@ -98,13 +98,29 @@ func parseDN(dn string) ([]DNComponent, error) {
return nil, fmt.Errorf("Wrong DN component: %s (expected type=value)", rdn)
}
ret = append(ret, DNComponent{
- Type: splits[0],
- Value: splits[1],
+ Type: strings.TrimSpace(splits[0]),
+ Value: strings.TrimSpace(splits[1]),
})
}
return ret, nil
}
+func canonicalDN(dn string) (string, error) {
+ path, err := parseDN(dn)
+ if err != nil {
+ return "", err
+ }
+
+ ret := ""
+ for _, c := range path {
+ if ret != "" {
+ ret = ret + ","
+ }
+ ret = ret + c.Type + "=" + c.Value
+ }
+ return ret, nil
+}
+
func checkRestrictedAttr(attr string) error {
RESTRICTED_ATTRS := []string{
ATTR_MEMBEROF,
@@ -138,3 +154,11 @@ func genUuid() string {
}
return uuid.String()
}
+
+func valueMatch(attr, val1, val2 string) bool {
+ if strings.EqualFold(attr, ATTR_USERPASSWORD) {
+ return val1 == val2
+ } else {
+ return strings.EqualFold(val1, val2)
+ }
+}
diff --git a/write.go b/write.go
index 0af7ae5..2c35309 100644
--- a/write.go
+++ b/write.go
@@ -30,9 +30,7 @@ func (server *Server) handleAdd(s ldap.UserState, w ldap.ResponseWriter, m *ldap
}
func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (int, error) {
- dn := string(r.Entry())
-
- _, err := server.checkSuffix(dn, false)
+ dn, err := server.checkDN(string(r.Entry()), false)
if err != nil {
return ldap.LDAPResultInvalidDNSyntax, err
}
@@ -81,18 +79,18 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
if strings.EqualFold(key, ATTR_MEMBER) {
members = vals_str
for _, member := range members {
- _, err := server.checkSuffix(member, false)
+ member_canonical, err := server.checkDN(member, false)
if err != nil {
return ldap.LDAPResultInvalidDNSyntax, err
}
- exists, err = server.objectExists(member)
+ exists, err = server.objectExists(member_canonical)
if err != nil {
return ldap.LDAPResultOperationsError, err
}
if !exists {
return ldap.LDAPResultNoSuchObject, fmt.Errorf(
"Cannot add %s to members, it does not exist!",
- member)
+ member_canonical)
}
}
}
@@ -104,7 +102,7 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
entry[ATTR_ENTRYUUID] = []string{genUuid()}
entry[dnSplit[0].Type] = []string{dnSplit[0].Value}
- // Add our intem in the DB
+ // Add our item in the DB
err = server.addElements(dn, entry)
if err != nil {
return ldap.LDAPResultOperationsError, err
@@ -117,7 +115,7 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
for _, member := range members {
memberGroups, err := server.getAttribute(member, ATTR_MEMBEROF)
if err != nil {
- server.logger.Printf("Could not add %s to memberOf of %s: %s", dn, member, err)
+ server.logger.Warnf("Could not add %s to memberOf of %s: %s", dn, member, err)
continue
}
if memberGroups == nil {
@@ -128,7 +126,7 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
for _, mb := range memberGroups {
if mb == dn {
alreadyMember = true
- server.logger.Printf("Warning: inconsistency detected, %s was memberOf %s at a time when it didn't exist!",
+ server.logger.Warnf("Warning: inconsistency detected, %s was memberOf %s at a time when it didn't exist!",
member, dn)
break
}
@@ -140,7 +138,7 @@ func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (in
ATTR_MEMBEROF: memberGroups,
})
if err != nil {
- server.logger.Printf("Could not add %s to memberOf of %s: %s", dn, member, err)
+ server.logger.Warnf("Could not add %s to memberOf of %s: %s", dn, member, err)
}
}
}
@@ -170,9 +168,7 @@ func (server *Server) handleDelete(s ldap.UserState, w ldap.ResponseWriter, m *l
}
func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest) (int, error) {
- dn := string(*r)
-
- _, err := server.checkSuffix(dn, false)
+ dn, err := server.checkDN(string(*r), false)
if err != nil {
return ldap.LDAPResultInvalidDNSyntax, err
}
@@ -230,7 +226,7 @@ func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest)
for _, group := range memberOf {
groupMembers, err := server.getAttribute(group, ATTR_MEMBER)
if err != nil {
- server.logger.Printf("Could not remove %s from members of %s: %s", dn, group, err)
+ server.logger.Warnf("Could not remove %s from members of %s: %s", dn, group, err)
continue
}
@@ -245,7 +241,7 @@ func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest)
ATTR_MEMBER: newMembers,
})
if err != nil {
- server.logger.Printf("Could not remove %s from members of %s: %s", dn, group, err)
+ server.logger.Warnf("Could not remove %s from members of %s: %s", dn, group, err)
}
}
}
@@ -255,7 +251,7 @@ func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest)
for _, member := range memberList {
memberOf, err := server.getAttribute(member, ATTR_MEMBEROF)
if err != nil || memberOf == nil {
- server.logger.Printf("Could not remove %s from memberOf of %s: %s", dn, member, err)
+ server.logger.Warnf("Could not remove %s from memberOf of %s: %s", dn, member, err)
continue
}
@@ -270,7 +266,7 @@ func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest)
ATTR_MEMBEROF: newMemberOf,
})
if err != nil {
- server.logger.Printf("Could not remove %s from memberOf of %s: %s", dn, member, err)
+ server.logger.Warnf("Could not remove %s from memberOf of %s: %s", dn, member, err)
}
}
}
@@ -299,9 +295,7 @@ func (server *Server) handleModify(s ldap.UserState, w ldap.ResponseWriter, m *l
}
func (server *Server) handleModifyInternal(state *State, r *message.ModifyRequest) (int, error) {
- dn := string(r.Object())
-
- _, err := server.checkSuffix(dn, false)
+ dn, err := server.checkDN(string(r.Object()), false)
if err != nil {
return ldap.LDAPResultInvalidDNSyntax, err
}
@@ -448,7 +442,11 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
}
// Check that added members actually exist
- for _, addMem := range addMembers {
+ for i := range addMembers {
+ addMem, err := server.checkDN(addMembers[i], false)
+ if err != nil {
+ return ldap.LDAPResultInvalidDNSyntax, err
+ }
exists, err := server.objectExists(addMem)
if err != nil {
return ldap.LDAPResultOperationsError, err
@@ -457,6 +455,7 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
return ldap.LDAPResultNoSuchObject, fmt.Errorf(
"Cannot add member %s, it does not exist", addMem)
}
+ addMembers[i] = addMem
}
newEntry[ATTR_MODIFIERSNAME] = []string{state.login.user}
@@ -471,7 +470,7 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
for _, addMem := range addMembers {
memberOf, err := server.getAttribute(addMem, ATTR_MEMBEROF)
if err != nil {
- server.logger.Printf("Could not add %s to memberOf of %s: %s", dn, addMem, err)
+ server.logger.Warnf("Could not add %s to memberOf of %s: %s", dn, addMem, err)
continue
}
if memberOf == nil {
@@ -491,7 +490,7 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
ATTR_MEMBEROF: memberOf,
})
if err != nil {
- server.logger.Printf("Could not add %s to memberOf of %s: %s", dn, addMem, err)
+ server.logger.Warnf("Could not add %s to memberOf of %s: %s", dn, addMem, err)
}
}
}
@@ -499,7 +498,7 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
for _, delMem := range delMembers {
memberOf, err := server.getAttribute(delMem, ATTR_MEMBEROF)
if err != nil {
- server.logger.Printf("Could not remove %s from memberOf of %s: %s", dn, delMem, err)
+ server.logger.Warnf("Could not remove %s from memberOf of %s: %s", dn, delMem, err)
continue
}
if memberOf == nil {
@@ -514,7 +513,7 @@ func (server *Server) handleModifyInternal(state *State, r *message.ModifyReques
err = server.addElements(delMem, Entry{ATTR_MEMBEROF: newMemberOf})
if err != nil {
- server.logger.Printf("Could not remove %s from memberOf of %s: %s", dn, delMem, err)
+ server.logger.Warnf("Could not remove %s from memberOf of %s: %s", dn, delMem, err)
}
}