1// File contains Search functionality 2// 3// https://tools.ietf.org/html/rfc4511 4// 5// SearchRequest ::= [APPLICATION 3] SEQUENCE { 6// baseObject LDAPDN, 7// scope ENUMERATED { 8// baseObject (0), 9// singleLevel (1), 10// wholeSubtree (2), 11// ... }, 12// derefAliases ENUMERATED { 13// neverDerefAliases (0), 14// derefInSearching (1), 15// derefFindingBaseObj (2), 16// derefAlways (3) }, 17// sizeLimit INTEGER (0 .. maxInt), 18// timeLimit INTEGER (0 .. maxInt), 19// typesOnly BOOLEAN, 20// filter Filter, 21// attributes AttributeSelection } 22// 23// AttributeSelection ::= SEQUENCE OF selector LDAPString 24// -- The LDAPString is constrained to 25// -- <attributeSelector> in Section 4.5.1.8 26// 27// Filter ::= CHOICE { 28// and [0] SET SIZE (1..MAX) OF filter Filter, 29// or [1] SET SIZE (1..MAX) OF filter Filter, 30// not [2] Filter, 31// equalityMatch [3] AttributeValueAssertion, 32// substrings [4] SubstringFilter, 33// greaterOrEqual [5] AttributeValueAssertion, 34// lessOrEqual [6] AttributeValueAssertion, 35// present [7] AttributeDescription, 36// approxMatch [8] AttributeValueAssertion, 37// extensibleMatch [9] MatchingRuleAssertion, 38// ... } 39// 40// SubstringFilter ::= SEQUENCE { 41// type AttributeDescription, 42// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { 43// initial [0] AssertionValue, -- can occur at most once 44// any [1] AssertionValue, 45// final [2] AssertionValue } -- can occur at most once 46// } 47// 48// MatchingRuleAssertion ::= SEQUENCE { 49// matchingRule [1] MatchingRuleId OPTIONAL, 50// type [2] AttributeDescription OPTIONAL, 51// matchValue [3] AssertionValue, 52// dnAttributes [4] BOOLEAN DEFAULT FALSE } 53// 54// 55 56package ldap 57 58import ( 59 "errors" 60 "fmt" 61 "sort" 62 "strings" 63 64 "gopkg.in/asn1-ber.v1" 65) 66 67// scope choices 68const ( 69 ScopeBaseObject = 0 70 ScopeSingleLevel = 1 71 ScopeWholeSubtree = 2 72) 73 74// ScopeMap contains human readable descriptions of scope choices 75var ScopeMap = map[int]string{ 76 ScopeBaseObject: "Base Object", 77 ScopeSingleLevel: "Single Level", 78 ScopeWholeSubtree: "Whole Subtree", 79} 80 81// derefAliases 82const ( 83 NeverDerefAliases = 0 84 DerefInSearching = 1 85 DerefFindingBaseObj = 2 86 DerefAlways = 3 87) 88 89// DerefMap contains human readable descriptions of derefAliases choices 90var DerefMap = map[int]string{ 91 NeverDerefAliases: "NeverDerefAliases", 92 DerefInSearching: "DerefInSearching", 93 DerefFindingBaseObj: "DerefFindingBaseObj", 94 DerefAlways: "DerefAlways", 95} 96 97// NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs. 98// The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the 99// same input map of attributes, the output entry will contain the same order of attributes 100func NewEntry(dn string, attributes map[string][]string) *Entry { 101 var attributeNames []string 102 for attributeName := range attributes { 103 attributeNames = append(attributeNames, attributeName) 104 } 105 sort.Strings(attributeNames) 106 107 var encodedAttributes []*EntryAttribute 108 for _, attributeName := range attributeNames { 109 encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName])) 110 } 111 return &Entry{ 112 DN: dn, 113 Attributes: encodedAttributes, 114 } 115} 116 117// Entry represents a single search result entry 118type Entry struct { 119 // DN is the distinguished name of the entry 120 DN string 121 // Attributes are the returned attributes for the entry 122 Attributes []*EntryAttribute 123} 124 125// GetAttributeValues returns the values for the named attribute, or an empty list 126func (e *Entry) GetAttributeValues(attribute string) []string { 127 for _, attr := range e.Attributes { 128 if attr.Name == attribute { 129 return attr.Values 130 } 131 } 132 return []string{} 133} 134 135// GetRawAttributeValues returns the byte values for the named attribute, or an empty list 136func (e *Entry) GetRawAttributeValues(attribute string) [][]byte { 137 for _, attr := range e.Attributes { 138 if attr.Name == attribute { 139 return attr.ByteValues 140 } 141 } 142 return [][]byte{} 143} 144 145// GetAttributeValue returns the first value for the named attribute, or "" 146func (e *Entry) GetAttributeValue(attribute string) string { 147 values := e.GetAttributeValues(attribute) 148 if len(values) == 0 { 149 return "" 150 } 151 return values[0] 152} 153 154// GetRawAttributeValue returns the first value for the named attribute, or an empty slice 155func (e *Entry) GetRawAttributeValue(attribute string) []byte { 156 values := e.GetRawAttributeValues(attribute) 157 if len(values) == 0 { 158 return []byte{} 159 } 160 return values[0] 161} 162 163// Print outputs a human-readable description 164func (e *Entry) Print() { 165 fmt.Printf("DN: %s\n", e.DN) 166 for _, attr := range e.Attributes { 167 attr.Print() 168 } 169} 170 171// PrettyPrint outputs a human-readable description indenting 172func (e *Entry) PrettyPrint(indent int) { 173 fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN) 174 for _, attr := range e.Attributes { 175 attr.PrettyPrint(indent + 2) 176 } 177} 178 179// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair 180func NewEntryAttribute(name string, values []string) *EntryAttribute { 181 var bytes [][]byte 182 for _, value := range values { 183 bytes = append(bytes, []byte(value)) 184 } 185 return &EntryAttribute{ 186 Name: name, 187 Values: values, 188 ByteValues: bytes, 189 } 190} 191 192// EntryAttribute holds a single attribute 193type EntryAttribute struct { 194 // Name is the name of the attribute 195 Name string 196 // Values contain the string values of the attribute 197 Values []string 198 // ByteValues contain the raw values of the attribute 199 ByteValues [][]byte 200} 201 202// Print outputs a human-readable description 203func (e *EntryAttribute) Print() { 204 fmt.Printf("%s: %s\n", e.Name, e.Values) 205} 206 207// PrettyPrint outputs a human-readable description with indenting 208func (e *EntryAttribute) PrettyPrint(indent int) { 209 fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values) 210} 211 212// SearchResult holds the server's response to a search request 213type SearchResult struct { 214 // Entries are the returned entries 215 Entries []*Entry 216 // Referrals are the returned referrals 217 Referrals []string 218 // Controls are the returned controls 219 Controls []Control 220} 221 222// Print outputs a human-readable description 223func (s *SearchResult) Print() { 224 for _, entry := range s.Entries { 225 entry.Print() 226 } 227} 228 229// PrettyPrint outputs a human-readable description with indenting 230func (s *SearchResult) PrettyPrint(indent int) { 231 for _, entry := range s.Entries { 232 entry.PrettyPrint(indent) 233 } 234} 235 236// SearchRequest represents a search request to send to the server 237type SearchRequest struct { 238 BaseDN string 239 Scope int 240 DerefAliases int 241 SizeLimit int 242 TimeLimit int 243 TypesOnly bool 244 Filter string 245 Attributes []string 246 Controls []Control 247} 248 249func (s *SearchRequest) encode() (*ber.Packet, error) { 250 request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request") 251 request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN")) 252 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope")) 253 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases")) 254 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit")) 255 request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit")) 256 request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only")) 257 // compile and encode filter 258 filterPacket, err := CompileFilter(s.Filter) 259 if err != nil { 260 return nil, err 261 } 262 request.AppendChild(filterPacket) 263 // encode attributes 264 attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") 265 for _, attribute := range s.Attributes { 266 attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) 267 } 268 request.AppendChild(attributesPacket) 269 return request, nil 270} 271 272// NewSearchRequest creates a new search request 273func NewSearchRequest( 274 BaseDN string, 275 Scope, DerefAliases, SizeLimit, TimeLimit int, 276 TypesOnly bool, 277 Filter string, 278 Attributes []string, 279 Controls []Control, 280) *SearchRequest { 281 return &SearchRequest{ 282 BaseDN: BaseDN, 283 Scope: Scope, 284 DerefAliases: DerefAliases, 285 SizeLimit: SizeLimit, 286 TimeLimit: TimeLimit, 287 TypesOnly: TypesOnly, 288 Filter: Filter, 289 Attributes: Attributes, 290 Controls: Controls, 291 } 292} 293 294// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the 295// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically. 296// The following four cases are possible given the arguments: 297// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size 298// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries 299// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request 300// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries 301// A requested pagingSize of 0 is interpreted as no limit by LDAP servers. 302func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) { 303 var pagingControl *ControlPaging 304 305 control := FindControl(searchRequest.Controls, ControlTypePaging) 306 if control == nil { 307 pagingControl = NewControlPaging(pagingSize) 308 searchRequest.Controls = append(searchRequest.Controls, pagingControl) 309 } else { 310 castControl, ok := control.(*ControlPaging) 311 if !ok { 312 return nil, fmt.Errorf("expected paging control to be of type *ControlPaging, got %v", control) 313 } 314 if castControl.PagingSize != pagingSize { 315 return nil, fmt.Errorf("paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize) 316 } 317 pagingControl = castControl 318 } 319 320 searchResult := new(SearchResult) 321 for { 322 result, err := l.Search(searchRequest) 323 l.Debug.Printf("Looking for Paging Control...") 324 if err != nil { 325 return searchResult, err 326 } 327 if result == nil { 328 return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) 329 } 330 331 for _, entry := range result.Entries { 332 searchResult.Entries = append(searchResult.Entries, entry) 333 } 334 for _, referral := range result.Referrals { 335 searchResult.Referrals = append(searchResult.Referrals, referral) 336 } 337 for _, control := range result.Controls { 338 searchResult.Controls = append(searchResult.Controls, control) 339 } 340 341 l.Debug.Printf("Looking for Paging Control...") 342 pagingResult := FindControl(result.Controls, ControlTypePaging) 343 if pagingResult == nil { 344 pagingControl = nil 345 l.Debug.Printf("Could not find paging control. Breaking...") 346 break 347 } 348 349 cookie := pagingResult.(*ControlPaging).Cookie 350 if len(cookie) == 0 { 351 pagingControl = nil 352 l.Debug.Printf("Could not find cookie. Breaking...") 353 break 354 } 355 pagingControl.SetCookie(cookie) 356 } 357 358 if pagingControl != nil { 359 l.Debug.Printf("Abandoning Paging...") 360 pagingControl.PagingSize = 0 361 l.Search(searchRequest) 362 } 363 364 return searchResult, nil 365} 366 367// Search performs the given search request 368func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { 369 packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 370 packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 371 // encode search request 372 encodedSearchRequest, err := searchRequest.encode() 373 if err != nil { 374 return nil, err 375 } 376 packet.AppendChild(encodedSearchRequest) 377 // encode search controls 378 if len(searchRequest.Controls) > 0 { 379 packet.AppendChild(encodeControls(searchRequest.Controls)) 380 } 381 382 l.Debug.PrintPacket(packet) 383 384 msgCtx, err := l.sendMessage(packet) 385 if err != nil { 386 return nil, err 387 } 388 defer l.finishMessage(msgCtx) 389 390 result := &SearchResult{ 391 Entries: make([]*Entry, 0), 392 Referrals: make([]string, 0), 393 Controls: make([]Control, 0)} 394 395 foundSearchResultDone := false 396 for !foundSearchResultDone { 397 l.Debug.Printf("%d: waiting for response", msgCtx.id) 398 packetResponse, ok := <-msgCtx.responses 399 if !ok { 400 return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 401 } 402 packet, err = packetResponse.ReadPacket() 403 l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 404 if err != nil { 405 return nil, err 406 } 407 408 if l.Debug { 409 if err := addLDAPDescriptions(packet); err != nil { 410 return nil, err 411 } 412 ber.PrintPacket(packet) 413 } 414 415 switch packet.Children[1].Tag { 416 case 4: 417 entry := new(Entry) 418 entry.DN = packet.Children[1].Children[0].Value.(string) 419 for _, child := range packet.Children[1].Children[1].Children { 420 attr := new(EntryAttribute) 421 attr.Name = child.Children[0].Value.(string) 422 for _, value := range child.Children[1].Children { 423 attr.Values = append(attr.Values, value.Value.(string)) 424 attr.ByteValues = append(attr.ByteValues, value.ByteValue) 425 } 426 entry.Attributes = append(entry.Attributes, attr) 427 } 428 result.Entries = append(result.Entries, entry) 429 case 5: 430 err := GetLDAPError(packet) 431 if err != nil { 432 return nil, err 433 } 434 if len(packet.Children) == 3 { 435 for _, child := range packet.Children[2].Children { 436 decodedChild, err := DecodeControl(child) 437 if err != nil { 438 return nil, fmt.Errorf("failed to decode child control: %s", err) 439 } 440 result.Controls = append(result.Controls, decodedChild) 441 } 442 } 443 foundSearchResultDone = true 444 case 19: 445 result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string)) 446 } 447 } 448 l.Debug.Printf("%d: returning", msgCtx.id) 449 return result, nil 450} 451