Initial QSfera import
This commit is contained in:
+225
@@ -0,0 +1,225 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Copyright 2021 The LibreGraph Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ldapserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
func HandleSearchRequest(req *ber.Packet, controls *[]ldap.Control, messageID int64, boundDN string, server *Server, conn net.Conn) (doneControls *[]ldap.Control, resultErr error) {
|
||||
searchReq, err := parseSearchRequest(boundDN, req, controls)
|
||||
if err != nil {
|
||||
return nil, ldap.NewError(ldap.LDAPResultOperationsError, err)
|
||||
}
|
||||
|
||||
var filterPacket *ber.Packet
|
||||
if server.EnforceLDAP {
|
||||
filterPacket, err = ldap.CompileFilter(searchReq.Filter)
|
||||
if err != nil {
|
||||
return nil, ldap.NewError(ldap.LDAPResultOperationsError, err)
|
||||
}
|
||||
}
|
||||
|
||||
fnNames := []string{}
|
||||
for k := range server.SearchFns {
|
||||
fnNames = append(fnNames, k)
|
||||
}
|
||||
fn := routeFunc(searchReq.BaseDN, fnNames)
|
||||
searchResp, err := server.SearchFns[fn].Search(boundDN, searchReq, conn)
|
||||
if err != nil {
|
||||
return &searchResp.Controls, ldap.NewError(uint16(searchResp.ResultCode), err)
|
||||
}
|
||||
|
||||
if server.EnforceLDAP {
|
||||
if searchReq.DerefAliases != ldap.NeverDerefAliases { // [-a {never|always|search|find}
|
||||
// TODO: Server DerefAliases not supported: RFC4511 4.5.1.3
|
||||
}
|
||||
if searchReq.TimeLimit > 0 {
|
||||
// TODO: Server TimeLimit not implemented
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for _, entry := range searchResp.Entries {
|
||||
if server.EnforceLDAP {
|
||||
// filter
|
||||
keep, resultCode := ServerApplyFilter(filterPacket, entry)
|
||||
if resultCode != ldap.LDAPResultSuccess {
|
||||
return &searchResp.Controls, ldap.NewError(uint16(resultCode), errors.New("ServerApplyFilter error"))
|
||||
}
|
||||
if !keep {
|
||||
continue
|
||||
}
|
||||
|
||||
keep, resultCode = ServerFilterScope(searchReq.BaseDN, searchReq.Scope, entry)
|
||||
if resultCode != ldap.LDAPResultSuccess {
|
||||
return &searchResp.Controls, ldap.NewError(uint16(resultCode), errors.New("ServerApplyScope error"))
|
||||
}
|
||||
if !keep {
|
||||
continue
|
||||
}
|
||||
|
||||
resultCode, err = ServerFilterAttributes(searchReq.Attributes, entry)
|
||||
if err != nil {
|
||||
return &searchResp.Controls, ldap.NewError(uint16(resultCode), err)
|
||||
}
|
||||
|
||||
// size limit
|
||||
if searchReq.SizeLimit > 0 && i >= searchReq.SizeLimit {
|
||||
resultErr = ldap.NewError(
|
||||
ldap.LDAPResultSizeLimitExceeded,
|
||||
errors.New(ldap.LDAPResultCodeMap[ldap.LDAPResultSizeLimitExceeded]),
|
||||
)
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// respond
|
||||
responsePacket := encodeSearchResponse(messageID, searchReq, entry)
|
||||
if err = sendPacket(conn, responsePacket); err != nil {
|
||||
return &searchResp.Controls, ldap.NewError(ldap.LDAPResultOperationsError, err)
|
||||
}
|
||||
}
|
||||
return &searchResp.Controls, resultErr
|
||||
}
|
||||
|
||||
func parseSearchRequest(boundDN string, req *ber.Packet, controls *[]ldap.Control) (*ldap.SearchRequest, error) {
|
||||
if len(req.Children) != 8 {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultOperationsError, errors.New("Bad search request"))
|
||||
}
|
||||
|
||||
// Parse the request.
|
||||
baseObject, ok := req.Children[0].Value.(string)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
s, ok := req.Children[1].Value.(int64)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
scope := int(s)
|
||||
d, ok := req.Children[2].Value.(int64)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
derefAliases := int(d)
|
||||
s, ok = req.Children[3].Value.(int64)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
sizeLimit := int(s)
|
||||
t, ok := req.Children[4].Value.(int64)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
timeLimit := int(t)
|
||||
typesOnly := false
|
||||
if req.Children[5].Value != nil {
|
||||
typesOnly, ok = req.Children[5].Value.(bool)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
}
|
||||
filter, err := DecompileFilter(req.Children[6])
|
||||
if err != nil {
|
||||
return &ldap.SearchRequest{}, err
|
||||
}
|
||||
attributes := []string{}
|
||||
for _, attr := range req.Children[7].Children {
|
||||
a, ok := attr.Value.(string)
|
||||
if !ok {
|
||||
return &ldap.SearchRequest{}, ldap.NewError(ldap.LDAPResultProtocolError, errors.New("Bad search request"))
|
||||
}
|
||||
attributes = append(attributes, a)
|
||||
}
|
||||
searchReq := &ldap.SearchRequest{
|
||||
BaseDN: baseObject,
|
||||
Scope: scope,
|
||||
DerefAliases: derefAliases,
|
||||
SizeLimit: sizeLimit,
|
||||
TimeLimit: timeLimit,
|
||||
TypesOnly: typesOnly,
|
||||
Filter: filter,
|
||||
Attributes: attributes,
|
||||
Controls: *controls,
|
||||
}
|
||||
|
||||
return searchReq, nil
|
||||
}
|
||||
|
||||
func filterAttributes(entry *ldap.Entry, attributes []string) (*ldap.Entry, error) {
|
||||
// Only return requested attributes.
|
||||
newAttributes := []*ldap.EntryAttribute{}
|
||||
|
||||
for _, attr := range entry.Attributes {
|
||||
for _, requested := range attributes {
|
||||
if requested == "*" || strings.EqualFold(attr.Name, requested) {
|
||||
newAttributes = append(newAttributes, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
entry.Attributes = newAttributes
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
func encodeSearchResponse(messageID int64, req *ldap.SearchRequest, res *ldap.Entry) *ber.Packet {
|
||||
responsePacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Response")
|
||||
responsePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "Message ID"))
|
||||
|
||||
searchEntry := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ldap.ApplicationSearchResultEntry, nil, "Search Result Entry")
|
||||
searchEntry.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, res.DN, "Object Name"))
|
||||
|
||||
attrs := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes: ")
|
||||
for _, attribute := range res.Attributes {
|
||||
attrs.AppendChild(encodeSearchAttribute(attribute.Name, attribute.Values))
|
||||
}
|
||||
|
||||
searchEntry.AppendChild(attrs)
|
||||
responsePacket.AppendChild(searchEntry)
|
||||
|
||||
return responsePacket
|
||||
}
|
||||
|
||||
func encodeSearchAttribute(name string, values []string) *ber.Packet {
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
|
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, name, "Attribute Name"))
|
||||
|
||||
valuesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "Attribute Values: ")
|
||||
for _, value := range values {
|
||||
valuesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Attribute Value"))
|
||||
}
|
||||
|
||||
packet.AppendChild(valuesPacket)
|
||||
|
||||
return packet
|
||||
}
|
||||
|
||||
func encodeSearchDone(messageID int64, ldapResultCode LDAPResultCode, doneControls *[]ldap.Control) *ber.Packet {
|
||||
responsePacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Response")
|
||||
responsePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "Message ID"))
|
||||
donePacket := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ldap.ApplicationSearchResultDone, nil, "Search result done")
|
||||
donePacket.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ldapResultCode), "resultCode: "))
|
||||
donePacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "matchedDN: "))
|
||||
donePacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "errorMessage: "))
|
||||
responsePacket.AppendChild(donePacket)
|
||||
|
||||
if doneControls != nil {
|
||||
contextPacket := ber.Encode(ber.ClassContext, ber.TypeConstructed, ber.TagEOC, nil, "Controls: ")
|
||||
for _, control := range *doneControls {
|
||||
contextPacket.AppendChild(control.Encode())
|
||||
}
|
||||
responsePacket.AppendChild(contextPacket)
|
||||
}
|
||||
// ber.PrintPacket(responsePacket)
|
||||
return responsePacket
|
||||
}
|
||||
Reference in New Issue
Block a user