diff options
Diffstat (limited to 'main.go')
-rw-r--r-- | main.go | 658 |
1 files changed, 0 insertions, 658 deletions
@@ -15,7 +15,6 @@ import ( "math/rand" "os" "os/signal" - "strings" "syscall" ldap "./ldapserver" @@ -374,661 +373,4 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) ( return ldap.LDAPResultInvalidCredentials, nil } -func (server *Server) handleSearch(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) { - state := s.(*State) - r := m.GetSearchRequest() - - code, err := server.handleSearchInternal(state, w, &r) - - res := ldap.NewResponse(code) - if err != nil { - res.SetDiagnosticMessage(err.Error()) - } - w.Write(message.SearchResultDone(res)) -} - -func (server *Server) handleSearchInternal(state *State, w ldap.ResponseWriter, r *message.SearchRequest) (int, error) { - if DEBUG { - server.logger.Printf("-- SEARCH REQUEST: --") - server.logger.Printf("Request BaseDn=%s", r.BaseObject()) - server.logger.Printf("Request Filter=%s", r.Filter()) - server.logger.Printf("Request FilterString=%s", r.FilterString()) - server.logger.Printf("Request Attributes=%s", r.Attributes()) - server.logger.Printf("Request TimeLimit=%d", r.TimeLimit().Int()) - } - - if !server.config.Acl.Check(&state.login, "read", string(r.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 - } - - data, _, err := server.kv.List(basePath+"/", nil) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - entries, err := parseConsulResult(data) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if DEBUG { - server.logger.Printf("in %s: %#v", basePath+"/", data) - server.logger.Printf("%#v", entries) - } - - for dn, entry := range entries { - // Filter out if we don't match requested filter - matched, err := applyFilter(entry, r.Filter()) - if err != nil { - return ldap.LDAPResultUnwillingToPerform, err - } - if !matched { - continue - } - - // Filter out if user is not allowed to read this - if !server.config.Acl.Check(&state.login, "read", dn, []string{}) { - continue - } - - e := ldap.NewSearchResultEntry(dn) - for attr, val := range entry { - // If attribute is not in request, exclude it from returned entry - if len(r.Attributes()) > 0 { - found := false - for _, requested := range r.Attributes() { - if strings.EqualFold(string(requested), attr) { - found = true - break - } - } - if !found { - continue - } - } - // If we are not allowed to read attribute, exclude it from returned entry - if !server.config.Acl.Check(&state.login, "read", dn, []string{attr}) { - continue - } - // Send result - for _, v := range val { - e.AddAttribute(message.AttributeDescription(attr), - message.AttributeValue(v)) - } - } - w.Write(e) - } - - return ldap.LDAPResultSuccess, nil -} - -func applyFilter(entry Entry, filter message.Filter) (bool, error) { - if fAnd, ok := filter.(message.FilterAnd); ok { - for _, cond := range fAnd { - res, err := applyFilter(entry, cond) - if err != nil { - return false, err - } - if !res { - return false, nil - } - } - return true, nil - } else if fOr, ok := filter.(message.FilterOr); ok { - for _, cond := range fOr { - res, err := applyFilter(entry, cond) - if err != nil { - return false, err - } - if res { - return true, nil - } - } - return false, nil - } else if fNot, ok := filter.(message.FilterNot); ok { - res, err := applyFilter(entry, fNot.Filter) - if err != nil { - return false, err - } - return !res, nil - } else if fPresent, ok := filter.(message.FilterPresent); ok { - what := string(fPresent) - // Case insensitive search - for desc, values := range entry { - if strings.EqualFold(what, desc) { - return len(values) > 0, nil - } - } - return false, nil - } else if fEquality, ok := filter.(message.FilterEqualityMatch); ok { - desc := string(fEquality.AttributeDesc()) - target := string(fEquality.AssertionValue()) - // Case insensitive attribute search - for entry_desc, value := range entry { - if strings.EqualFold(entry_desc, desc) { - for _, val := range value { - if val == target { - return true, nil - } - } - return false, nil - } - } - return false, nil - } else { - return false, fmt.Errorf("Unsupported filter: %#v %T", filter, filter) - } -} - -func (server *Server) handleAdd(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) { - state := s.(*State) - r := m.GetAddRequest() - - code, err := server.handleAddInternal(state, &r) - - res := ldap.NewResponse(code) - if err != nil { - res.SetDiagnosticMessage(err.Error()) - } - if code == ldap.LDAPResultSuccess { - server.logger.Printf("Successfully added %s", string(r.Entry())) - } else { - server.logger.Printf("Failed to add %s (%s)", string(r.Entry()), err) - } - w.Write(message.AddResponse(res)) -} - -func (server *Server) handleAddInternal(state *State, r *message.AddRequest) (int, error) { - dn := string(r.Entry()) - - _, err := server.checkSuffix(dn, false) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - dnSplit, err := parseDN(dn) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - // Check permissions - attrListStr := []string{} - for _, attribute := range r.Attributes() { - attrListStr = append(attrListStr, string(attribute.Type_())) - } - if !server.config.Acl.Check(&state.login, "add", dn, attrListStr) { - return ldap.LDAPResultInsufficientAccessRights, nil - } - - // Check that object does not already exist - exists, err := server.objectExists(dn) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if exists { - return ldap.LDAPResultEntryAlreadyExists, nil - } - - // Add object - - // If adding a group, track of who the members will be so that their memberOf field can be updated later - var members []string = nil - - entry := Entry{} - for _, attribute := range r.Attributes() { - key := string(attribute.Type_()) - vals_str := []string{} - for _, val := range attribute.Vals() { - vals_str = append(vals_str, string(val)) - } - - // Fail if they are trying to write memberOf, we manage this ourselves - err = checkRestrictedAttr(key) - if err != nil { - return ldap.LDAPResultObjectClassViolation, err - } - // If they are writing a member key, we have to check they are adding valid members - if strings.EqualFold(key, "member") { - members = vals_str - for _, member := range members { - _, err := server.checkSuffix(member, false) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - exists, err = server.objectExists(member) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if !exists { - return ldap.LDAPResultNoSuchObject, fmt.Errorf( - "Cannot add %s to members, it does not exist!", - member) - } - } - } - entry[key] = vals_str - } - - entry[ATTR_CREATORSNAME] = []string{state.login.user} - entry[ATTR_CREATETIMESTAMP] = []string{genTimestamp()} - entry[ATTR_ENTRYUUID] = []string{genUuid()} - entry[dnSplit[0].Type] = []string{dnSplit[0].Value} - - err = server.addElements(dn, entry) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - if members != nil { - for _, member := range members { - memberGroups, err := server.getAttribute(member, ATTR_MEMBEROF) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if memberGroups == nil { - memberGroups = []string{} - } - - alreadyMember := false - 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!", - member, dn) - break - } - } - - if !alreadyMember { - memberGroups = append(memberGroups, dn) - err = server.addElements(member, Entry{ - ATTR_MEMBEROF: memberGroups, - }) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - } - } - } - - return ldap.LDAPResultSuccess, nil -} - -func (server *Server) handleCompare(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) { - state := s.(*State) - r := m.GetCompareRequest() - - code, err := server.handleCompareInternal(state, &r) - - res := ldap.NewResponse(code) - if err != nil { - res.SetDiagnosticMessage(err.Error()) - } - w.Write(message.CompareResponse(res)) -} - -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) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - // Check permissions - if !server.config.Acl.Check(&state.login, dn, "read", []string{attr}) { - return ldap.LDAPResultInsufficientAccessRights, nil - } - - // Do query - exists, err := server.objectExists(dn) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if !exists { - return ldap.LDAPResultNoSuchObject, fmt.Errorf("Not found: %s", dn) - } - - values, err := server.getAttribute(dn, attr) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - for _, v := range values { - if v == expected { - return ldap.LDAPResultCompareTrue, nil - } - } - - return ldap.LDAPResultCompareFalse, nil -} - -func (server *Server) handleDelete(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) { - state := s.(*State) - r := m.GetDeleteRequest() - - code, err := server.handleDeleteInternal(state, &r) - - res := ldap.NewResponse(code) - if err != nil { - res.SetDiagnosticMessage(err.Error()) - } - if code == ldap.LDAPResultSuccess { - server.logger.Printf("Successfully deleted %s", string(r)) - } else { - server.logger.Printf("Failed to delete %s (%s)", string(r), err) - } - w.Write(message.DelResponse(res)) -} - -func (server *Server) handleDeleteInternal(state *State, r *message.DelRequest) (int, error) { - dn := string(*r) - - _, err := server.checkSuffix(dn, false) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - // Check for delete permission - if !server.config.Acl.Check(&state.login, "delete", dn, []string{}) { - return ldap.LDAPResultInsufficientAccessRights, nil - } - - // Check that this LDAP entry exists and has no children - path, err := dnToConsul(dn) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - items, _, err := server.kv.List(path+"/", nil) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - if len(items) == 0 { - return ldap.LDAPResultNoSuchObject, fmt.Errorf("Not found: %s", dn) - } - for _, item := range items { - itemDN, _, err := consulToDN(item.Key) - if err != nil { - continue - } - if itemDN != dn { - return ldap.LDAPResultNotAllowedOnNonLeaf, fmt.Errorf( - "Cannot delete %d as it has children", dn) - } - } - - // Retrieve group membership before we delete everything - memberOf, err := server.getAttribute(dn, ATTR_MEMBEROF) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - // Delete the LDAP entry - _, err = server.kv.DeleteTree(path+"/", nil) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - // Delete it from the member list of all the groups it was a member of - if memberOf != nil { - for _, group := range memberOf { - groupMembers, err := server.getAttribute(dn, "member") - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - newMembers := []string{} - for _, memb := range groupMembers { - if memb != dn { - newMembers = append(newMembers, memb) - } - } - - err = server.addElements(group, Entry{ - "member": newMembers, - }) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - } - } - - return ldap.LDAPResultSuccess, nil -} - -func (server *Server) handleModify(s ldap.UserState, w ldap.ResponseWriter, m *ldap.Message) { - state := s.(*State) - r := m.GetModifyRequest() - - code, err := server.handleModifyInternal(state, &r) - - res := ldap.NewResponse(code) - if err != nil { - res.SetDiagnosticMessage(err.Error()) - } - if code == ldap.LDAPResultSuccess { - server.logger.Printf("Successfully modified %s", string(r.Object())) - } else { - server.logger.Printf("Failed to modifiy %s (%s)", string(r.Object()), err) - } - w.Write(message.ModifyResponse(res)) -} - -func (server *Server) handleModifyInternal(state *State, r *message.ModifyRequest) (int, error) { - dn := string(r.Object()) - - _, err := server.checkSuffix(dn, false) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - dnSplit, err := parseDN(dn) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - // First permission check with no particular attributes - if !server.config.Acl.Check(&state.login, "modify", dn, []string{}) { - return ldap.LDAPResultInsufficientAccessRights, nil - } - - // Retrieve previous values (by the way, check object exists) - path, err := dnToConsul(dn) - if err != nil { - return ldap.LDAPResultInvalidDNSyntax, err - } - - items, _, err := server.kv.List(path+"/attribute=", nil) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - - if len(items) == 0 { - return ldap.LDAPResultNoSuchObject, fmt.Errorf("Not found: %s", dn) - } - - prevEntry := Entry{} - for _, item := range items { - itemDN, attr, err := consulToDN(item.Key) - if err != nil { - continue - } - if itemDN != dn { - panic("itemDN != dn in handleModifyInternal") - } - vals, err := parseValue(item.Value) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - prevEntry[attr] = vals - } - - // Keep track of group members added/deleted - addMembers, delMembers := []string{}, []string{} - - // Produce new entry values to be saved - newEntry := Entry{} - for _, change := range r.Changes() { - attr := string(change.Modification().Type_()) - values := change.Modification().Vals() - - err = checkRestrictedAttr(attr) - if err != nil { - return ldap.LDAPResultObjectClassViolation, err - } - if strings.EqualFold(attr, dnSplit[0].Type) { - return ldap.LDAPResultObjectClassViolation, fmt.Errorf("%s may not be changed as it is part of object path", attr) - } - // Check for permission to modify this attribute - if !server.config.Acl.Check(&state.login, "modify", dn, []string{attr}) { - return ldap.LDAPResultInsufficientAccessRights, nil - } - - if change.Operation() == ldap.ModifyRequestChangeOperationAdd { - newEntry[attr] = prevEntry[attr] - for _, val := range values { - present := false - for _, prevVal := range newEntry[attr] { - if prevVal == string(val) { - present = true - break - } - } - if !present { - newEntry[attr] = append(newEntry[attr], string(val)) - if strings.EqualFold(attr, "member") { - addMembers = append(addMembers, string(val)) - } - } - } - } else if change.Operation() == ldap.ModifyRequestChangeOperationDelete { - if len(values) == 0 { - // Delete everything - newEntry[attr] = []string{} - if strings.EqualFold(attr, "member") { - delMembers = append(delMembers, prevEntry[attr]...) - } - } else { - // Delete only those specified - newEntry[attr] = []string{} - for _, prevVal := range prevEntry[attr] { - keep := true - for _, delVal := range values { - if string(delVal) == prevVal { - keep = false - break - } - } - if keep { - newEntry[attr] = append(newEntry[attr], prevVal) - } else { - if strings.EqualFold(attr, "member") { - delMembers = append(delMembers, prevVal) - } - } - } - } - } else if change.Operation() == ldap.ModifyRequestChangeOperationReplace { - newEntry[attr] = []string{} - for _, newVal := range values { - newEntry[attr] = append(newEntry[attr], string(newVal)) - } - if strings.EqualFold(attr, "member") { - for _, newMem := range newEntry[attr] { - mustAdd := true - for _, prevMem := range prevEntry[attr] { - if prevMem == newMem { - mustAdd = false - break - } - } - if mustAdd { - addMembers = append(addMembers, newMem) - } - } - for _, prevMem := range prevEntry[attr] { - mustDel := true - for _, newMem := range newEntry[attr] { - if newMem == prevMem { - mustDel = false - break - } - } - if mustDel { - delMembers = append(delMembers, prevMem) - } - } - } - } - } - - // Check that added members actually exist - for _, addMem := range addMembers { - exists, err := server.objectExists(addMem) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if !exists { - return ldap.LDAPResultNoSuchObject, fmt.Errorf( - "Cannot add member %s, it does not exist", addMem) - } - } - - newEntry[ATTR_MODIFIERSNAME] = []string{state.login.user} - newEntry[ATTR_MODIFYTIMESTAMP] = []string{genTimestamp()} - - // Save the edited values - server.addElements(dn, newEntry) - - // Update memberOf for added members and deleted members - for _, addMem := range addMembers { - memberOf, err := server.getAttribute(addMem, ATTR_MEMBEROF) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if memberOf == nil { - memberOf = []string{} - } - memberOf = append(memberOf, dn) - err = server.addElements(addMem, Entry{ATTR_MEMBEROF: memberOf}) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - } - - for _, delMem := range delMembers { - memberOf, err := server.getAttribute(delMem, ATTR_MEMBEROF) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - if memberOf == nil { - memberOf = []string{} - } - newMemberOf := []string{} - for _, g := range memberOf { - if g != dn { - newMemberOf = append(newMemberOf, g) - } - } - - err = server.addElements(delMem, Entry{ATTR_MEMBEROF: newMemberOf}) - if err != nil { - return ldap.LDAPResultOperationsError, err - } - } - - return ldap.LDAPResultSuccess, nil -} |