parent
25d6ae69d1
commit
59a7c7c5a5
@ -0,0 +1,27 @@ |
||||
Copyright (c) 2012 The Go Authors. All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are |
||||
met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above |
||||
copyright notice, this list of conditions and the following disclaimer |
||||
in the documentation and/or other materials provided with the |
||||
distribution. |
||||
* Neither the name of Google Inc. nor the names of its |
||||
contributors may be used to endorse or promote products derived from |
||||
this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,11 @@ |
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include $(GOROOT)/src/Make.inc |
||||
|
||||
TARG=github.com/mmitton/asn1-ber
|
||||
GOFILES=\
|
||||
ber.go\
|
||||
|
||||
include $(GOROOT)/src/Make.pkg |
@ -0,0 +1,14 @@ |
||||
ASN1 BER Encoding / Decoding Library for the GO programming language. |
||||
|
||||
Required Librarys: |
||||
None |
||||
|
||||
Working: |
||||
Very basic encoding / decoding needed for LDAP protocol |
||||
|
||||
Tests Implemented: |
||||
None |
||||
|
||||
TODO: |
||||
Fix all encoding / decoding to conform to ASN1 BER spec |
||||
Implement Tests / Benchmarks |
@ -0,0 +1,492 @@ |
||||
package ber |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"io" |
||||
"reflect" |
||||
) |
||||
|
||||
type Packet struct { |
||||
ClassType uint8 |
||||
TagType uint8 |
||||
Tag uint8 |
||||
Value interface{} |
||||
ByteValue []byte |
||||
Data *bytes.Buffer |
||||
Children []*Packet |
||||
Description string |
||||
} |
||||
|
||||
const ( |
||||
TagEOC = 0x00 |
||||
TagBoolean = 0x01 |
||||
TagInteger = 0x02 |
||||
TagBitString = 0x03 |
||||
TagOctetString = 0x04 |
||||
TagNULL = 0x05 |
||||
TagObjectIdentifier = 0x06 |
||||
TagObjectDescriptor = 0x07 |
||||
TagExternal = 0x08 |
||||
TagRealFloat = 0x09 |
||||
TagEnumerated = 0x0a |
||||
TagEmbeddedPDV = 0x0b |
||||
TagUTF8String = 0x0c |
||||
TagRelativeOID = 0x0d |
||||
TagSequence = 0x10 |
||||
TagSet = 0x11 |
||||
TagNumericString = 0x12 |
||||
TagPrintableString = 0x13 |
||||
TagT61String = 0x14 |
||||
TagVideotexString = 0x15 |
||||
TagIA5String = 0x16 |
||||
TagUTCTime = 0x17 |
||||
TagGeneralizedTime = 0x18 |
||||
TagGraphicString = 0x19 |
||||
TagVisibleString = 0x1a |
||||
TagGeneralString = 0x1b |
||||
TagUniversalString = 0x1c |
||||
TagCharacterString = 0x1d |
||||
TagBMPString = 0x1e |
||||
TagBitmask = 0x1f // xxx11111b
|
||||
) |
||||
|
||||
var TagMap = map[uint8]string{ |
||||
TagEOC: "EOC (End-of-Content)", |
||||
TagBoolean: "Boolean", |
||||
TagInteger: "Integer", |
||||
TagBitString: "Bit String", |
||||
TagOctetString: "Octet String", |
||||
TagNULL: "NULL", |
||||
TagObjectIdentifier: "Object Identifier", |
||||
TagObjectDescriptor: "Object Descriptor", |
||||
TagExternal: "External", |
||||
TagRealFloat: "Real (float)", |
||||
TagEnumerated: "Enumerated", |
||||
TagEmbeddedPDV: "Embedded PDV", |
||||
TagUTF8String: "UTF8 String", |
||||
TagRelativeOID: "Relative-OID", |
||||
TagSequence: "Sequence and Sequence of", |
||||
TagSet: "Set and Set OF", |
||||
TagNumericString: "Numeric String", |
||||
TagPrintableString: "Printable String", |
||||
TagT61String: "T61 String", |
||||
TagVideotexString: "Videotex String", |
||||
TagIA5String: "IA5 String", |
||||
TagUTCTime: "UTC Time", |
||||
TagGeneralizedTime: "Generalized Time", |
||||
TagGraphicString: "Graphic String", |
||||
TagVisibleString: "Visible String", |
||||
TagGeneralString: "General String", |
||||
TagUniversalString: "Universal String", |
||||
TagCharacterString: "Character String", |
||||
TagBMPString: "BMP String", |
||||
} |
||||
|
||||
const ( |
||||
ClassUniversal = 0 // 00xxxxxxb
|
||||
ClassApplication = 64 // 01xxxxxxb
|
||||
ClassContext = 128 // 10xxxxxxb
|
||||
ClassPrivate = 192 // 11xxxxxxb
|
||||
ClassBitmask = 192 // 11xxxxxxb
|
||||
) |
||||
|
||||
var ClassMap = map[uint8]string{ |
||||
ClassUniversal: "Universal", |
||||
ClassApplication: "Application", |
||||
ClassContext: "Context", |
||||
ClassPrivate: "Private", |
||||
} |
||||
|
||||
const ( |
||||
TypePrimitive = 0 // xx0xxxxxb
|
||||
TypeConstructed = 32 // xx1xxxxxb
|
||||
TypeBitmask = 32 // xx1xxxxxb
|
||||
) |
||||
|
||||
var TypeMap = map[uint8]string{ |
||||
TypePrimitive: "Primative", |
||||
TypeConstructed: "Constructed", |
||||
} |
||||
|
||||
var Debug bool = false |
||||
|
||||
func PrintBytes(buf []byte, indent string) { |
||||
data_lines := make([]string, (len(buf)/30)+1) |
||||
num_lines := make([]string, (len(buf)/30)+1) |
||||
|
||||
for i, b := range buf { |
||||
data_lines[i/30] += fmt.Sprintf("%02x ", b) |
||||
num_lines[i/30] += fmt.Sprintf("%02d ", (i+1)%100) |
||||
} |
||||
|
||||
for i := 0; i < len(data_lines); i++ { |
||||
fmt.Print(indent + data_lines[i] + "\n") |
||||
fmt.Print(indent + num_lines[i] + "\n\n") |
||||
} |
||||
} |
||||
|
||||
func PrintPacket(p *Packet) { |
||||
printPacket(p, 0, false) |
||||
} |
||||
|
||||
func printPacket(p *Packet, indent int, printBytes bool) { |
||||
indent_str := "" |
||||
|
||||
for len(indent_str) != indent { |
||||
indent_str += " " |
||||
} |
||||
|
||||
class_str := ClassMap[p.ClassType] |
||||
|
||||
tagtype_str := TypeMap[p.TagType] |
||||
|
||||
tag_str := fmt.Sprintf("0x%02X", p.Tag) |
||||
|
||||
if p.ClassType == ClassUniversal { |
||||
tag_str = TagMap[p.Tag] |
||||
} |
||||
|
||||
value := fmt.Sprint(p.Value) |
||||
description := "" |
||||
|
||||
if p.Description != "" { |
||||
description = p.Description + ": " |
||||
} |
||||
|
||||
fmt.Printf("%s%s(%s, %s, %s) Len=%d %q\n", indent_str, description, class_str, tagtype_str, tag_str, p.Data.Len(), value) |
||||
|
||||
if printBytes { |
||||
PrintBytes(p.Bytes(), indent_str) |
||||
} |
||||
|
||||
for _, child := range p.Children { |
||||
printPacket(child, indent+1, printBytes) |
||||
} |
||||
} |
||||
|
||||
func resizeBuffer(in []byte, new_size uint64) (out []byte) { |
||||
out = make([]byte, new_size) |
||||
|
||||
copy(out, in) |
||||
|
||||
return |
||||
} |
||||
|
||||
func readBytes(reader io.Reader, buf []byte) error { |
||||
idx := 0 |
||||
buflen := len(buf) |
||||
|
||||
for idx < buflen { |
||||
n, err := reader.Read(buf[idx:]) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
idx += n |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func ReadPacket(reader io.Reader) (*Packet, error) { |
||||
buf := make([]byte, 2) |
||||
|
||||
err := readBytes(reader, buf) |
||||
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
idx := uint64(2) |
||||
datalen := uint64(buf[1]) |
||||
|
||||
if Debug { |
||||
fmt.Printf("Read: datalen = %d len(buf) = %d ", datalen, len(buf)) |
||||
|
||||
for _, b := range buf { |
||||
fmt.Printf("%02X ", b) |
||||
} |
||||
|
||||
fmt.Printf("\n") |
||||
} |
||||
|
||||
if datalen&128 != 0 { |
||||
a := datalen - 128 |
||||
|
||||
idx += a |
||||
buf = resizeBuffer(buf, 2+a) |
||||
|
||||
err := readBytes(reader, buf[2:]) |
||||
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
datalen = DecodeInteger(buf[2 : 2+a]) |
||||
|
||||
if Debug { |
||||
fmt.Printf("Read: a = %d idx = %d datalen = %d len(buf) = %d", a, idx, datalen, len(buf)) |
||||
|
||||
for _, b := range buf { |
||||
fmt.Printf("%02X ", b) |
||||
} |
||||
|
||||
fmt.Printf("\n") |
||||
} |
||||
} |
||||
|
||||
buf = resizeBuffer(buf, idx+datalen) |
||||
err = readBytes(reader, buf[idx:]) |
||||
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if Debug { |
||||
fmt.Printf("Read: len( buf ) = %d idx=%d datalen=%d idx+datalen=%d\n", len(buf), idx, datalen, idx+datalen) |
||||
|
||||
for _, b := range buf { |
||||
fmt.Printf("%02X ", b) |
||||
} |
||||
} |
||||
|
||||
p := DecodePacket(buf) |
||||
|
||||
return p, nil |
||||
} |
||||
|
||||
func DecodeString(data []byte) (ret string) { |
||||
for _, c := range data { |
||||
ret += fmt.Sprintf("%c", c) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func DecodeInteger(data []byte) (ret uint64) { |
||||
for _, i := range data { |
||||
ret = ret * 256 |
||||
ret = ret + uint64(i) |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func EncodeInteger(val uint64) []byte { |
||||
var out bytes.Buffer |
||||
|
||||
found := false |
||||
|
||||
shift := uint(56) |
||||
|
||||
mask := uint64(0xFF00000000000000) |
||||
|
||||
for mask > 0 { |
||||
if !found && (val&mask != 0) { |
||||
found = true |
||||
} |
||||
|
||||
if found || (shift == 0) { |
||||
out.Write([]byte{byte((val & mask) >> shift)}) |
||||
} |
||||
|
||||
shift -= 8 |
||||
mask = mask >> 8 |
||||
} |
||||
|
||||
return out.Bytes() |
||||
} |
||||
|
||||
func DecodePacket(data []byte) *Packet { |
||||
p, _ := decodePacket(data) |
||||
|
||||
return p |
||||
} |
||||
|
||||
func decodePacket(data []byte) (*Packet, []byte) { |
||||
if Debug { |
||||
fmt.Printf("decodePacket: enter %d\n", len(data)) |
||||
} |
||||
|
||||
p := new(Packet) |
||||
|
||||
p.ClassType = data[0] & ClassBitmask |
||||
p.TagType = data[0] & TypeBitmask |
||||
p.Tag = data[0] & TagBitmask |
||||
|
||||
datalen := DecodeInteger(data[1:2]) |
||||
datapos := uint64(2) |
||||
|
||||
if datalen&128 != 0 { |
||||
datalen -= 128 |
||||
datapos += datalen |
||||
datalen = DecodeInteger(data[2 : 2+datalen]) |
||||
} |
||||
|
||||
p.Data = new(bytes.Buffer) |
||||
|
||||
p.Children = make([]*Packet, 0, 2) |
||||
|
||||
p.Value = nil |
||||
|
||||
value_data := data[datapos : datapos+datalen] |
||||
|
||||
if p.TagType == TypeConstructed { |
||||
for len(value_data) != 0 { |
||||
var child *Packet |
||||
|
||||
child, value_data = decodePacket(value_data) |
||||
p.AppendChild(child) |
||||
} |
||||
} else if p.ClassType == ClassUniversal { |
||||
p.Data.Write(data[datapos : datapos+datalen]) |
||||
p.ByteValue = value_data |
||||
|
||||
switch p.Tag { |
||||
case TagEOC: |
||||
case TagBoolean: |
||||
val := DecodeInteger(value_data) |
||||
|
||||
p.Value = val != 0 |
||||
case TagInteger: |
||||
p.Value = DecodeInteger(value_data) |
||||
case TagBitString: |
||||
case TagOctetString: |
||||
p.Value = DecodeString(value_data) |
||||
case TagNULL: |
||||
case TagObjectIdentifier: |
||||
case TagObjectDescriptor: |
||||
case TagExternal: |
||||
case TagRealFloat: |
||||
case TagEnumerated: |
||||
p.Value = DecodeInteger(value_data) |
||||
case TagEmbeddedPDV: |
||||
case TagUTF8String: |
||||
case TagRelativeOID: |
||||
case TagSequence: |
||||
case TagSet: |
||||
case TagNumericString: |
||||
case TagPrintableString: |
||||
p.Value = DecodeString(value_data) |
||||
case TagT61String: |
||||
case TagVideotexString: |
||||
case TagIA5String: |
||||
case TagUTCTime: |
||||
case TagGeneralizedTime: |
||||
case TagGraphicString: |
||||
case TagVisibleString: |
||||
case TagGeneralString: |
||||
case TagUniversalString: |
||||
case TagCharacterString: |
||||
case TagBMPString: |
||||
} |
||||
} else { |
||||
p.Data.Write(data[datapos : datapos+datalen]) |
||||
} |
||||
|
||||
return p, data[datapos+datalen:] |
||||
} |
||||
|
||||
func (p *Packet) DataLength() uint64 { |
||||
return uint64(p.Data.Len()) |
||||
} |
||||
|
||||
func (p *Packet) Bytes() []byte { |
||||
var out bytes.Buffer |
||||
|
||||
out.Write([]byte{p.ClassType | p.TagType | p.Tag}) |
||||
packet_length := EncodeInteger(p.DataLength()) |
||||
|
||||
if p.DataLength() > 127 || len(packet_length) > 1 { |
||||
out.Write([]byte{byte(len(packet_length) | 128)}) |
||||
out.Write(packet_length) |
||||
} else { |
||||
out.Write(packet_length) |
||||
} |
||||
|
||||
out.Write(p.Data.Bytes()) |
||||
|
||||
return out.Bytes() |
||||
} |
||||
|
||||
func (p *Packet) AppendChild(child *Packet) { |
||||
p.Data.Write(child.Bytes()) |
||||
|
||||
if len(p.Children) == cap(p.Children) { |
||||
newChildren := make([]*Packet, cap(p.Children)*2) |
||||
|
||||
copy(newChildren, p.Children) |
||||
p.Children = newChildren[0:len(p.Children)] |
||||
} |
||||
|
||||
p.Children = p.Children[0 : len(p.Children)+1] |
||||
p.Children[len(p.Children)-1] = child |
||||
} |
||||
|
||||
func Encode(ClassType, TagType, Tag uint8, Value interface{}, Description string) *Packet { |
||||
p := new(Packet) |
||||
|
||||
p.ClassType = ClassType |
||||
p.TagType = TagType |
||||
p.Tag = Tag |
||||
p.Data = new(bytes.Buffer) |
||||
|
||||
p.Children = make([]*Packet, 0, 2) |
||||
|
||||
p.Value = Value |
||||
p.Description = Description |
||||
|
||||
if Value != nil { |
||||
v := reflect.ValueOf(Value) |
||||
|
||||
if ClassType == ClassUniversal { |
||||
switch Tag { |
||||
case TagOctetString: |
||||
sv, ok := v.Interface().(string) |
||||
|
||||
if ok { |
||||
p.Data.Write([]byte(sv)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return p |
||||
} |
||||
|
||||
func NewSequence(Description string) *Packet { |
||||
return Encode(ClassUniversal, TypePrimitive, TagSequence, nil, Description) |
||||
} |
||||
|
||||
func NewBoolean(ClassType, TagType, Tag uint8, Value bool, Description string) *Packet { |
||||
intValue := 0 |
||||
|
||||
if Value { |
||||
intValue = 1 |
||||
} |
||||
|
||||
p := Encode(ClassType, TagType, Tag, nil, Description) |
||||
|
||||
p.Value = Value |
||||
p.Data.Write(EncodeInteger(uint64(intValue))) |
||||
|
||||
return p |
||||
} |
||||
|
||||
func NewInteger(ClassType, TagType, Tag uint8, Value uint64, Description string) *Packet { |
||||
p := Encode(ClassType, TagType, Tag, nil, Description) |
||||
|
||||
p.Value = Value |
||||
p.Data.Write(EncodeInteger(Value)) |
||||
|
||||
return p |
||||
} |
||||
|
||||
func NewString(ClassType, TagType, Tag uint8, Value, Description string) *Packet { |
||||
p := Encode(ClassType, TagType, Tag, nil, Description) |
||||
|
||||
p.Value = Value |
||||
p.Data.Write([]byte(Value)) |
||||
|
||||
return p |
||||
} |
@ -0,0 +1,27 @@ |
||||
Copyright (c) 2012 The Go Authors. All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are |
||||
met: |
||||
|
||||
* Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
* Redistributions in binary form must reproduce the above |
||||
copyright notice, this list of conditions and the following disclaimer |
||||
in the documentation and/or other materials provided with the |
||||
distribution. |
||||
* Neither the name of Google Inc. nor the names of its |
||||
contributors may be used to endorse or promote products derived from |
||||
this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,33 @@ |
||||
Basic LDAP v3 functionality for the GO programming language. |
||||
|
||||
Required Librarys: |
||||
github.com/johnweldon/asn1-ber |
||||
|
||||
Working: |
||||
Connecting to LDAP server |
||||
Binding to LDAP server |
||||
Searching for entries |
||||
Compiling string filters to LDAP filters |
||||
Paging Search Results |
||||
Modify Requests / Responses |
||||
|
||||
Examples: |
||||
search |
||||
modify |
||||
|
||||
Tests Implemented: |
||||
Filter Compile / Decompile |
||||
|
||||
TODO: |
||||
Add Requests / Responses |
||||
Delete Requests / Responses |
||||
Modify DN Requests / Responses |
||||
Compare Requests / Responses |
||||
Implement Tests / Benchmarks |
||||
|
||||
This feature is disabled at the moment, because in some cases the "Search Request Done" packet will be handled before the last "Search Request Entry": |
||||
Mulitple internal goroutines to handle network traffic |
||||
Makes library goroutine safe |
||||
Can perform multiple search requests at the same time and return |
||||
the results to the proper goroutine. All requests are blocking |
||||
requests, so the goroutine does not need special handling |
@ -0,0 +1,55 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"errors" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
func (l *Conn) Bind(username, password string) error { |
||||
messageID := l.nextMessageID() |
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") |
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) |
||||
bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") |
||||
bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) |
||||
bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name")) |
||||
bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password")) |
||||
packet.AppendChild(bindRequest) |
||||
|
||||
if l.Debug { |
||||
ber.PrintPacket(packet) |
||||
} |
||||
|
||||
channel, err := l.sendMessage(packet) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if channel == nil { |
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message")) |
||||
} |
||||
defer l.finishMessage(messageID) |
||||
|
||||
packet = <-channel |
||||
if packet == nil { |
||||
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve response")) |
||||
} |
||||
|
||||
if l.Debug { |
||||
if err := addLDAPDescriptions(packet); err != nil { |
||||
return err |
||||
} |
||||
ber.PrintPacket(packet) |
||||
} |
||||
|
||||
resultCode, resultDescription := getLDAPResultCode(packet) |
||||
if resultCode != 0 { |
||||
return NewError(resultCode, errors.New(resultDescription)) |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,275 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"crypto/tls" |
||||
"errors" |
||||
"log" |
||||
"net" |
||||
"sync" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
const ( |
||||
MessageQuit = 0 |
||||
MessageRequest = 1 |
||||
MessageResponse = 2 |
||||
MessageFinish = 3 |
||||
) |
||||
|
||||
type messagePacket struct { |
||||
Op int |
||||
MessageID uint64 |
||||
Packet *ber.Packet |
||||
Channel chan *ber.Packet |
||||
} |
||||
|
||||
// Conn represents an LDAP Connection
|
||||
type Conn struct { |
||||
conn net.Conn |
||||
isTLS bool |
||||
isClosing bool |
||||
Debug debugging |
||||
chanConfirm chan bool |
||||
chanResults map[uint64]chan *ber.Packet |
||||
chanMessage chan *messagePacket |
||||
chanMessageID chan uint64 |
||||
wgSender sync.WaitGroup |
||||
wgClose sync.WaitGroup |
||||
once sync.Once |
||||
} |
||||
|
||||
// Dial connects to the given address on the given network using net.Dial
|
||||
// and then returns a new Conn for the connection.
|
||||
func Dial(network, addr string) (*Conn, error) { |
||||
c, err := net.Dial(network, addr) |
||||
if err != nil { |
||||
return nil, NewError(ErrorNetwork, err) |
||||
} |
||||
conn := NewConn(c) |
||||
conn.start() |
||||
return conn, nil |
||||
} |
||||
|
||||
// DialTLS connects to the given address on the given network using tls.Dial
|
||||
// and then returns a new Conn for the connection.
|
||||
func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { |
||||
c, err := tls.Dial(network, addr, config) |
||||
if err != nil { |
||||
return nil, NewError(ErrorNetwork, err) |
||||
} |
||||
conn := NewConn(c) |
||||
conn.isTLS = true |
||||
conn.start() |
||||
return conn, nil |
||||
} |
||||
|
||||
// NewConn returns a new Conn using conn for network I/O.
|
||||
func NewConn(conn net.Conn) *Conn { |
||||
return &Conn{ |
||||
conn: conn, |
||||
chanConfirm: make(chan bool), |
||||
chanMessageID: make(chan uint64), |
||||
chanMessage: make(chan *messagePacket, 10), |
||||
chanResults: map[uint64]chan *ber.Packet{}, |
||||
} |
||||
} |
||||
|
||||
func (l *Conn) start() { |
||||
go l.reader() |
||||
go l.processMessages() |
||||
l.wgClose.Add(1) |
||||
} |
||||
|
||||
// Close closes the connection.
|
||||
func (l *Conn) Close() { |
||||
l.once.Do(func() { |
||||
l.isClosing = true |
||||
l.wgSender.Wait() |
||||
|
||||
l.Debug.Printf("Sending quit message and waiting for confirmation") |
||||
l.chanMessage <- &messagePacket{Op: MessageQuit} |
||||
<-l.chanConfirm |
||||
close(l.chanMessage) |
||||
|
||||
l.Debug.Printf("Closing network connection") |
||||
if err := l.conn.Close(); err != nil { |
||||
log.Print(err) |
||||
} |
||||
|
||||
l.conn = nil |
||||
l.wgClose.Done() |
||||
}) |
||||
l.wgClose.Wait() |
||||
} |
||||
|
||||
// Returns the next available messageID
|
||||
func (l *Conn) nextMessageID() uint64 { |
||||
if l.chanMessageID != nil { |
||||
if messageID, ok := <-l.chanMessageID; ok { |
||||
return messageID |
||||
} |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
// StartTLS sends the command to start a TLS session and then creates a new TLS Client
|
||||
func (l *Conn) StartTLS(config *tls.Config) error { |
||||
messageID := l.nextMessageID() |
||||
|
||||
if l.isTLS { |
||||
return NewError(ErrorNetwork, errors.New("ldap: already encrypted")) |
||||
} |
||||
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") |
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) |
||||
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS") |
||||
request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command")) |
||||
packet.AppendChild(request) |
||||
l.Debug.PrintPacket(packet) |
||||
|
||||
_, err := l.conn.Write(packet.Bytes()) |
||||
if err != nil { |
||||
return NewError(ErrorNetwork, err) |
||||
} |
||||
|
||||
packet, err = ber.ReadPacket(l.conn) |
||||
if err != nil { |
||||
return NewError(ErrorNetwork, err) |
||||
} |
||||
|
||||
if l.Debug { |
||||
if err := addLDAPDescriptions(packet); err != nil { |
||||
return err |
||||
} |
||||
ber.PrintPacket(packet) |
||||
} |
||||
|
||||
if packet.Children[1].Children[0].Value.(uint64) == 0 { |
||||
conn := tls.Client(l.conn, config) |
||||
l.isTLS = true |
||||
l.conn = conn |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (l *Conn) sendMessage(packet *ber.Packet) (chan *ber.Packet, error) { |
||||
if l.isClosing { |
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed")) |
||||
} |
||||
out := make(chan *ber.Packet) |
||||
message := &messagePacket{ |
||||
Op: MessageRequest, |
||||
MessageID: packet.Children[0].Value.(uint64), |
||||
Packet: packet, |
||||
Channel: out, |
||||
} |
||||
l.sendProcessMessage(message) |
||||
return out, nil |
||||
} |
||||
|
||||
func (l *Conn) finishMessage(messageID uint64) { |
||||
if l.isClosing { |
||||
return |
||||
} |
||||
message := &messagePacket{ |
||||
Op: MessageFinish, |
||||
MessageID: messageID, |
||||
} |
||||
l.sendProcessMessage(message) |
||||
} |
||||
|
||||
func (l *Conn) sendProcessMessage(message *messagePacket) bool { |
||||
if l.isClosing { |
||||
return false |
||||
} |
||||
l.wgSender.Add(1) |
||||
l.chanMessage <- message |
||||
l.wgSender.Done() |
||||
return true |
||||
} |
||||
|
||||
func (l *Conn) processMessages() { |
||||
defer func() { |
||||
for messageID, channel := range l.chanResults { |
||||
l.Debug.Printf("Closing channel for MessageID %d", messageID) |
||||
close(channel) |
||||
delete(l.chanResults, messageID) |
||||
} |
||||
close(l.chanMessageID) |
||||
l.chanConfirm <- true |
||||
close(l.chanConfirm) |
||||
}() |
||||
|
||||
var messageID uint64 = 1 |
||||
for { |
||||
select { |
||||
case l.chanMessageID <- messageID: |
||||
messageID++ |
||||
case messagePacket, ok := <-l.chanMessage: |
||||
if !ok { |
||||
l.Debug.Printf("Shutting down - message channel is closed") |
||||
return |
||||
} |
||||
switch messagePacket.Op { |
||||
case MessageQuit: |
||||
l.Debug.Printf("Shutting down - quit message received") |
||||
return |
||||
case MessageRequest: |
||||
// Add to message list and write to network
|
||||
l.Debug.Printf("Sending message %d", messagePacket.MessageID) |
||||
l.chanResults[messagePacket.MessageID] = messagePacket.Channel |
||||
// go routine
|
||||
buf := messagePacket.Packet.Bytes() |
||||
|
||||
_, err := l.conn.Write(buf) |
||||
if err != nil { |
||||
l.Debug.Printf("Error Sending Message: %s", err.Error()) |
||||
break |
||||
} |
||||
case MessageResponse: |
||||
l.Debug.Printf("Receiving message %d", messagePacket.MessageID) |
||||
if chanResult, ok := l.chanResults[messagePacket.MessageID]; ok { |
||||
chanResult <- messagePacket.Packet |
||||
} else { |
||||
log.Printf("Received unexpected message %d", messagePacket.MessageID) |
||||
ber.PrintPacket(messagePacket.Packet) |
||||
} |
||||
case MessageFinish: |
||||
// Remove from message list
|
||||
l.Debug.Printf("Finished message %d", messagePacket.MessageID) |
||||
close(l.chanResults[messagePacket.MessageID]) |
||||
delete(l.chanResults, messagePacket.MessageID) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (l *Conn) reader() { |
||||
defer func() { |
||||
l.Close() |
||||
}() |
||||
|
||||
for { |
||||
packet, err := ber.ReadPacket(l.conn) |
||||
if err != nil { |
||||
l.Debug.Printf("reader: %s", err.Error()) |
||||
return |
||||
} |
||||
addLDAPDescriptions(packet) |
||||
message := &messagePacket{ |
||||
Op: MessageResponse, |
||||
MessageID: packet.Children[0].Value.(uint64), |
||||
Packet: packet, |
||||
} |
||||
if !l.sendProcessMessage(message) { |
||||
return |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,157 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
const ( |
||||
ControlTypePaging = "1.2.840.113556.1.4.319" |
||||
) |
||||
|
||||
var ControlTypeMap = map[string]string{ |
||||
ControlTypePaging: "Paging", |
||||
} |
||||
|
||||
type Control interface { |
||||
GetControlType() string |
||||
Encode() *ber.Packet |
||||
String() string |
||||
} |
||||
|
||||
type ControlString struct { |
||||
ControlType string |
||||
Criticality bool |
||||
ControlValue string |
||||
} |
||||
|
||||
func (c *ControlString) GetControlType() string { |
||||
return c.ControlType |
||||
} |
||||
|
||||
func (c *ControlString) Encode() *ber.Packet { |
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") |
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")")) |
||||
if c.Criticality { |
||||
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) |
||||
} |
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlValue, "Control Value")) |
||||
return packet |
||||
} |
||||
|
||||
func (c *ControlString) String() string { |
||||
return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue) |
||||
} |
||||
|
||||
type ControlPaging struct { |
||||
PagingSize uint32 |
||||
Cookie []byte |
||||
} |
||||
|
||||
func (c *ControlPaging) GetControlType() string { |
||||
return ControlTypePaging |
||||
} |
||||
|
||||
func (c *ControlPaging) Encode() *ber.Packet { |
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") |
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")")) |
||||
|
||||
p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)") |
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value") |
||||
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(c.PagingSize), "Paging Size")) |
||||
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie") |
||||
cookie.Value = c.Cookie |
||||
cookie.Data.Write(c.Cookie) |
||||
seq.AppendChild(cookie) |
||||
p2.AppendChild(seq) |
||||
|
||||
packet.AppendChild(p2) |
||||
return packet |
||||
} |
||||
|
||||
func (c *ControlPaging) String() string { |
||||
return fmt.Sprintf( |
||||
"Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q", |
||||
ControlTypeMap[ControlTypePaging], |
||||
ControlTypePaging, |
||||
false, |
||||
c.PagingSize, |
||||
c.Cookie) |
||||
} |
||||
|
||||
func (c *ControlPaging) SetCookie(cookie []byte) { |
||||
c.Cookie = cookie |
||||
} |
||||
|
||||
func FindControl(controls []Control, controlType string) Control { |
||||
for _, c := range controls { |
||||
if c.GetControlType() == controlType { |
||||
return c |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func DecodeControl(packet *ber.Packet) Control { |
||||
ControlType := packet.Children[0].Value.(string) |
||||
Criticality := false |
||||
|
||||
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" |
||||
value := packet.Children[1] |
||||
if len(packet.Children) == 3 { |
||||
value = packet.Children[2] |
||||
packet.Children[1].Description = "Criticality" |
||||
Criticality = packet.Children[1].Value.(bool) |
||||
} |
||||
|
||||
value.Description = "Control Value" |
||||
switch ControlType { |
||||
case ControlTypePaging: |
||||
value.Description += " (Paging)" |
||||
c := new(ControlPaging) |
||||
if value.Value != nil { |
||||
valueChildren := ber.DecodePacket(value.Data.Bytes()) |
||||
value.Data.Truncate(0) |
||||
value.Value = nil |
||||
value.AppendChild(valueChildren) |
||||
} |
||||
value = value.Children[0] |
||||
value.Description = "Search Control Value" |
||||
value.Children[0].Description = "Paging Size" |
||||
value.Children[1].Description = "Cookie" |
||||
c.PagingSize = uint32(value.Children[0].Value.(uint64)) |
||||
c.Cookie = value.Children[1].Data.Bytes() |
||||
value.Children[1].Value = c.Cookie |
||||
return c |
||||
} |
||||
c := new(ControlString) |
||||
c.ControlType = ControlType |
||||
c.Criticality = Criticality |
||||
c.ControlValue = value.Value.(string) |
||||
return c |
||||
} |
||||
|
||||
func NewControlString(controlType string, criticality bool, controlValue string) *ControlString { |
||||
return &ControlString{ |
||||
ControlType: controlType, |
||||
Criticality: criticality, |
||||
ControlValue: controlValue, |
||||
} |
||||
} |
||||
|
||||
func NewControlPaging(pagingSize uint32) *ControlPaging { |
||||
return &ControlPaging{PagingSize: pagingSize} |
||||
} |
||||
|
||||
func encodeControls(controls []Control) *ber.Packet { |
||||
packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls") |
||||
for _, control := range controls { |
||||
packet.AppendChild(control.Encode()) |
||||
} |
||||
return packet |
||||
} |
@ -0,0 +1,24 @@ |
||||
package ldap |
||||
|
||||
import ( |
||||
"log" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
// debbuging type
|
||||
// - has a Printf method to write the debug output
|
||||
type debugging bool |
||||
|
||||
// write debug output
|
||||
func (debug debugging) Printf(format string, args ...interface{}) { |
||||
if debug { |
||||
log.Printf(format, args...) |
||||
} |
||||
} |
||||
|
||||
func (debug debugging) PrintPacket(packet *ber.Packet) { |
||||
if debug { |
||||
ber.PrintPacket(packet) |
||||
} |
||||
} |
@ -0,0 +1,63 @@ |
||||
dn: dc=enterprise,dc=org |
||||
objectClass: dcObject |
||||
objectClass: organization |
||||
o: acme |
||||
|
||||
dn: cn=admin,dc=enterprise,dc=org |
||||
objectClass: person |
||||
cn: admin |
||||
sn: admin |
||||
description: "LDAP Admin" |
||||
|
||||
dn: ou=crew,dc=enterprise,dc=org |
||||
ou: crew |
||||
objectClass: organizationalUnit |
||||
|
||||
|
||||
dn: cn=kirkj,ou=crew,dc=enterprise,dc=org |
||||
cn: kirkj |
||||
sn: Kirk |
||||
gn: James Tiberius |
||||
mail: james.kirk@enterprise.org |
||||
objectClass: inetOrgPerson |
||||
|
||||
dn: cn=spock,ou=crew,dc=enterprise,dc=org |
||||
cn: spock |
||||
sn: Spock |
||||
mail: spock@enterprise.org |
||||
objectClass: inetOrgPerson |
||||
|
||||
dn: cn=mccoyl,ou=crew,dc=enterprise,dc=org |
||||
cn: mccoyl |
||||
sn: McCoy |
||||
gn: Leonard |
||||
mail: leonard.mccoy@enterprise.org |
||||
objectClass: inetOrgPerson |
||||
|
||||
dn: cn=scottm,ou=crew,dc=enterprise,dc=org |
||||
cn: scottm |
||||
sn: Scott |
||||
gn: Montgomery |
||||
mail: Montgomery.scott@enterprise.org |
||||
objectClass: inetOrgPerson |
||||
|
||||
dn: cn=uhuran,ou=crew,dc=enterprise,dc=org |
||||
cn: uhuran |
||||
sn: Uhura |
||||
gn: Nyota |
||||
mail: nyota.uhura@enterprise.org |
||||
objectClass: inetOrgPerson |
||||
|
||||
dn: cn=suluh,ou=crew,dc=enterprise,dc=org |
||||
cn: suluh |
||||
sn: Sulu |
||||
gn: Hikaru |
||||
mail: hikaru.sulu@enterprise.org |
||||
objectClass: inetOrgPerson |
||||
|
||||
dn: cn=chekovp,ou=crew,dc=enterprise,dc=org |
||||
cn: chekovp |
||||
sn: Chekov |
||||
gn: pavel |
||||
mail: pavel.chekov@enterprise.org |
||||
objectClass: inetOrgPerson |
@ -0,0 +1,89 @@ |
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"log" |
||||
|
||||
"github.com/juju2013/goldap" |
||||
) |
||||
|
||||
var ( |
||||
LdapServer string = "localhost" |
||||
LdapPort uint16 = 389 |
||||
BaseDN string = "dc=enterprise,dc=org" |
||||
BindDN string = "cn=admin,dc=enterprise,dc=org" |
||||
BindPW string = "enterprise" |
||||
Filter string = "(cn=kirkj)" |
||||
) |
||||
|
||||
func search(l *ldap.Conn, filter string, attributes []string) (*ldap.Entry, *ldap.Error) { |
||||
search := ldap.NewSearchRequest( |
||||
BaseDN, |
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
||||
filter, |
||||
attributes, |
||||
nil) |
||||
|
||||
sr, err := l.Search(search) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err) |
||||
return nil, err |
||||
} |
||||
|
||||
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries)) |
||||
if len(sr.Entries) == 0 { |
||||
return nil, ldap.NewError(ldap.ErrorDebugging, errors.New(fmt.Sprintf("no entries found for: %s", filter))) |
||||
} |
||||
return sr.Entries[0], nil |
||||
} |
||||
|
||||
func main() { |
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", LdapServer, LdapPort)) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
} |
||||
defer l.Close() |
||||
// l.Debug = true
|
||||
|
||||
l.Bind(BindDN, BindPW) |
||||
|
||||
log.Printf("The Search for Kirk ... %s\n", Filter) |
||||
entry, err := search(l, Filter, []string{}) |
||||
if err != nil { |
||||
log.Fatal("could not get entry") |
||||
} |
||||
entry.PrettyPrint(0) |
||||
|
||||
log.Printf("modify the mail address and add a description ... \n") |
||||
modify := ldap.NewModifyRequest(entry.DN) |
||||
modify.Add("description", []string{"Captain of the USS Enterprise"}) |
||||
modify.Replace("mail", []string{"captain@enterprise.org"}) |
||||
if err := l.Modify(modify); err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
} |
||||
|
||||
entry, err = search(l, Filter, []string{}) |
||||
if err != nil { |
||||
log.Fatal("could not get entry") |
||||
} |
||||
entry.PrettyPrint(0) |
||||
|
||||
log.Printf("reset the entry ... \n") |
||||
modify = ldap.NewModifyRequest(entry.DN) |
||||
modify.Delete("description", []string{}) |
||||
modify.Replace("mail", []string{"james.kirk@enterprise.org"}) |
||||
if err := l.Modify(modify); err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
} |
||||
|
||||
entry, err = search(l, Filter, []string{}) |
||||
if err != nil { |
||||
log.Fatal("could not get entry") |
||||
} |
||||
entry.PrettyPrint(0) |
||||
} |
@ -0,0 +1,52 @@ |
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"log" |
||||
|
||||
"github.com/juju2013/goldap" |
||||
) |
||||
|
||||
var ( |
||||
ldapServer string = "adserver" |
||||
ldapPort uint16 = 3268 |
||||
baseDN string = "dc=*,dc=*" |
||||
filter string = "(&(objectClass=user)(sAMAccountName=*)(memberOf=CN=*,OU=*,DC=*,DC=*))" |
||||
Attributes []string = []string{"memberof"} |
||||
user string = "*" |
||||
passwd string = "*" |
||||
) |
||||
|
||||
func main() { |
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
} |
||||
defer l.Close() |
||||
// l.Debug = true
|
||||
|
||||
err = l.Bind(user, passwd) |
||||
if err != nil { |
||||
log.Printf("ERROR: Cannot bind: %s\n", err.Error()) |
||||
return |
||||
} |
||||
search := ldap.NewSearchRequest( |
||||
baseDN, |
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
||||
filter, |
||||
Attributes, |
||||
nil) |
||||
|
||||
sr, err := l.Search(search) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
return |
||||
} |
||||
|
||||
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries)) |
||||
sr.PrettyPrint(0) |
||||
} |
@ -0,0 +1,45 @@ |
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"log" |
||||
|
||||
"github.com/juju2013/goldap" |
||||
) |
||||
|
||||
var ( |
||||
LdapServer string = "localhost" |
||||
LdapPort uint16 = 636 |
||||
BaseDN string = "dc=enterprise,dc=org" |
||||
Filter string = "(cn=kirkj)" |
||||
Attributes []string = []string{"mail"} |
||||
) |
||||
|
||||
func main() { |
||||
l, err := ldap.DialSSL("tcp", fmt.Sprintf("%s:%d", LdapServer, LdapPort), nil) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.String()) |
||||
} |
||||
defer l.Close() |
||||
// l.Debug = true
|
||||
|
||||
search := ldap.NewSearchRequest( |
||||
BaseDN, |
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
||||
Filter, |
||||
Attributes, |
||||
nil) |
||||
|
||||
sr, err := l.Search(search) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.String()) |
||||
return |
||||
} |
||||
|
||||
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries)) |
||||
sr.PrettyPrint(0) |
||||
} |
@ -0,0 +1,45 @@ |
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"log" |
||||
|
||||
"github.com/juju2013/goldap" |
||||
) |
||||
|
||||
var ( |
||||
LdapServer string = "localhost" |
||||
LdapPort uint16 = 389 |
||||
BaseDN string = "dc=enterprise,dc=org" |
||||
Filter string = "(cn=kirkj)" |
||||
Attributes []string = []string{"mail"} |
||||
) |
||||
|
||||
func main() { |
||||
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", LdapServer, LdapPort), nil) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
} |
||||
defer l.Close() |
||||
// l.Debug = true
|
||||
|
||||
search := ldap.NewSearchRequest( |
||||
BaseDN, |
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, |
||||
Filter, |
||||
Attributes, |
||||
nil) |
||||
|
||||
sr, err := l.Search(search) |
||||
if err != nil { |
||||
log.Fatalf("ERROR: %s\n", err.Error()) |
||||
return |
||||
} |
||||
|
||||
log.Printf("Search: %s -> num of entries = %d\n", search.Filter, len(sr.Entries)) |
||||
sr.PrettyPrint(0) |
||||
} |
@ -0,0 +1,67 @@ |
||||
# |
||||
# See slapd.conf(5) for details on configuration options. |
||||
# This file should NOT be world readable. |
||||
# |
||||
include /private/etc/openldap/schema/core.schema |
||||
include /private/etc/openldap/schema/cosine.schema |
||||
include /private/etc/openldap/schema/inetorgperson.schema |
||||
|
||||
# Define global ACLs to disable default read access. |
||||
|
||||
# Do not enable referrals until AFTER you have a working directory |
||||
# service AND an understanding of referrals. |
||||
#referral ldap://root.openldap.org |
||||
|
||||
pidfile /private/var/db/openldap/run/slapd.pid |
||||
argsfile /private/var/db/openldap/run/slapd.args |
||||
|
||||
# Load dynamic backend modules: |
||||
# modulepath /usr/libexec/openldap |
||||
# moduleload back_bdb.la |
||||
# moduleload back_hdb.la |
||||
# moduleload back_ldap.la |
||||
|
||||
# Sample security restrictions |
||||
# Require integrity protection (prevent hijacking) |
||||
# Require 112-bit (3DES or better) encryption for updates |
||||
# Require 63-bit encryption for simple bind |
||||
# security ssf=1 update_ssf=112 simple_bind=64 |
||||
|
||||
# Sample access control policy: |
||||
# Root DSE: allow anyone to read it |
||||
# Subschema (sub)entry DSE: allow anyone to read it |
||||
# Other DSEs: |
||||
# Allow self write access |
||||
# Allow authenticated users read access |
||||
# Allow anonymous users to authenticate |
||||
# Directives needed to implement policy: |
||||
# access to dn.base="" by * read |
||||
# access to dn.base="cn=Subschema" by * read |
||||
# access to * |
||||
# by self write |
||||
# by users read |
||||
# by anonymous auth |
||||
# |
||||
# if no access controls are present, the default policy |
||||
# allows anyone and everyone to read anything but restricts |
||||
# updates to rootdn. (e.g., "access to * by * read") |
||||
# |
||||
# rootdn can always read and write EVERYTHING! |
||||
|
||||
####################################################################### |
||||
# BDB database definitions |
||||
####################################################################### |
||||
|
||||
database bdb |
||||
suffix "dc=enterprise,dc=org" |
||||
rootdn "cn=admin,dc=enterprise,dc=org" |
||||
# Cleartext passwords, especially for the rootdn, should |
||||
# be avoid. See slappasswd(8) and slapd.conf(5) for details. |
||||
# Use of strong authentication encouraged. |
||||
rootpw {SSHA}laO00HsgszhK1O0Z5qR0/i/US69Osfeu |
||||
# The database directory MUST exist prior to running slapd AND |
||||
# should only be accessible by the slapd and slap tools. |
||||
# Mode 700 recommended. |
||||
directory /private/var/db/openldap/openldap-data |
||||
# Indices to maintain |
||||
index objectClass eq |
@ -0,0 +1,248 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
const ( |
||||
FilterAnd = 0 |
||||
FilterOr = 1 |
||||
FilterNot = 2 |
||||
FilterEqualityMatch = 3 |
||||
FilterSubstrings = 4 |
||||
FilterGreaterOrEqual = 5 |
||||
FilterLessOrEqual = 6 |
||||
FilterPresent = 7 |
||||
FilterApproxMatch = 8 |
||||
FilterExtensibleMatch = 9 |
||||
) |
||||
|
||||
var FilterMap = map[uint64]string{ |
||||
FilterAnd: "And", |
||||
FilterOr: "Or", |
||||
FilterNot: "Not", |
||||
FilterEqualityMatch: "Equality Match", |
||||
FilterSubstrings: "Substrings", |
||||
FilterGreaterOrEqual: "Greater Or Equal", |
||||
FilterLessOrEqual: "Less Or Equal", |
||||
FilterPresent: "Present", |
||||
FilterApproxMatch: "Approx Match", |
||||
FilterExtensibleMatch: "Extensible Match", |
||||
} |
||||
|
||||
const ( |
||||
FilterSubstringsInitial = 0 |
||||
FilterSubstringsAny = 1 |
||||
FilterSubstringsFinal = 2 |
||||
) |
||||
|
||||
var FilterSubstringsMap = map[uint64]string{ |
||||
FilterSubstringsInitial: "Substrings Initial", |
||||
FilterSubstringsAny: "Substrings Any", |
||||
FilterSubstringsFinal: "Substrings Final", |
||||
} |
||||
|
||||
func CompileFilter(filter string) (*ber.Packet, error) { |
||||
if len(filter) == 0 || filter[0] != '(' { |
||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('")) |
||||
} |
||||
packet, pos, err := compileFilter(filter, 1) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if pos != len(filter) { |
||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:]))) |
||||
} |
||||
return packet, nil |
||||
} |
||||
|
||||
func DecompileFilter(packet *ber.Packet) (ret string, err error) { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
err = NewError(ErrorFilterDecompile, errors.New("ldap: error decompiling filter")) |
||||
} |
||||
}() |
||||
ret = "(" |
||||
err = nil |
||||
childStr := "" |
||||
|
||||
switch packet.Tag { |
||||
case FilterAnd: |
||||
ret += "&" |
||||
for _, child := range packet.Children { |
||||
childStr, err = DecompileFilter(child) |
||||
if err != nil { |
||||
return |
||||
} |
||||
ret += childStr |
||||
} |
||||
case FilterOr: |
||||
ret += "|" |
||||
for _, child := range packet.Children { |
||||
childStr, err = DecompileFilter(child) |
||||
if err != nil { |
||||
return |
||||
} |
||||
ret += childStr |
||||
} |
||||
case FilterNot: |
||||
ret += "!" |
||||
childStr, err = DecompileFilter(packet.Children[0]) |
||||
if err != nil { |
||||
return |
||||
} |
||||
ret += childStr |
||||
|
||||
case FilterSubstrings: |
||||
ret += ber.DecodeString(packet.Children[0].Data.Bytes()) |
||||
ret += "=" |
||||
switch packet.Children[1].Children[0].Tag { |
||||
case FilterSubstringsInitial: |
||||
ret += ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) + "*" |
||||
case FilterSubstringsAny: |
||||
ret += "*" + ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) + "*" |
||||
case FilterSubstringsFinal: |
||||
ret += "*" + ber.DecodeString(packet.Children[1].Children[0].Data.Bytes()) |
||||
} |
||||
case FilterEqualityMatch: |
||||
ret += ber.DecodeString(packet.Children[0].Data.Bytes()) |
||||
ret += "=" |
||||
ret += ber.DecodeString(packet.Children[1].Data.Bytes()) |
||||
case FilterGreaterOrEqual: |
||||
ret += ber.DecodeString(packet.Children[0].Data.Bytes()) |
||||
ret += ">=" |
||||
ret += ber.DecodeString(packet.Children[1].Data.Bytes()) |
||||
case FilterLessOrEqual: |
||||
ret += ber.DecodeString(packet.Children[0].Data.Bytes()) |
||||
ret += "<=" |
||||
ret += ber.DecodeString(packet.Children[1].Data.Bytes()) |
||||
case FilterPresent: |
||||
ret += ber.DecodeString(packet.Children[0].Data.Bytes()) |
||||
ret += "=*" |
||||
case FilterApproxMatch: |
||||
ret += ber.DecodeString(packet.Children[0].Data.Bytes()) |
||||
ret += "~=" |
||||
ret += ber.DecodeString(packet.Children[1].Data.Bytes()) |
||||
} |
||||
|
||||
ret += ")" |
||||
return |
||||
} |
||||
|
||||
func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) { |
||||
for pos < len(filter) && filter[pos] == '(' { |
||||
child, newPos, err := compileFilter(filter, pos+1) |
||||
if err != nil { |
||||
return pos, err |
||||
} |
||||
pos = newPos |
||||
parent.AppendChild(child) |
||||
} |
||||
if pos == len(filter) { |
||||
return pos, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter")) |
||||
} |
||||
|
||||
return pos + 1, nil |
||||
} |
||||
|
||||
func compileFilter(filter string, pos int) (*ber.Packet, int, error) { |
||||
var packet *ber.Packet |
||||
var err error |
||||
|
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter")) |
||||
} |
||||
}() |
||||
|
||||
newPos := pos |
||||
switch filter[pos] { |
||||
case '(': |
||||
packet, newPos, err = compileFilter(filter, pos+1) |
||||
newPos++ |
||||
return packet, newPos, err |
||||
case '&': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd]) |
||||
newPos, err = compileFilterSet(filter, pos+1, packet) |
||||
return packet, newPos, err |
||||
case '|': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr]) |
||||
newPos, err = compileFilterSet(filter, pos+1, packet) |
||||
return packet, newPos, err |
||||
case '!': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot]) |
||||
var child *ber.Packet |
||||
child, newPos, err = compileFilter(filter, pos+1) |
||||
packet.AppendChild(child) |
||||
return packet, newPos, err |
||||
default: |
||||
attribute := "" |
||||
condition := "" |
||||
for newPos < len(filter) && filter[newPos] != ')' { |
||||
switch { |
||||
case packet != nil: |
||||
condition += fmt.Sprintf("%c", filter[newPos]) |
||||
case filter[newPos] == '=': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch]) |
||||
case filter[newPos] == '>' && filter[newPos+1] == '=': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual]) |
||||
newPos++ |
||||
case filter[newPos] == '<' && filter[newPos+1] == '=': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual]) |
||||
newPos++ |
||||
case filter[newPos] == '~' && filter[newPos+1] == '=': |
||||
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterLessOrEqual]) |
||||
newPos++ |
||||
case packet == nil: |
||||
attribute += fmt.Sprintf("%c", filter[newPos]) |
||||
} |
||||
newPos++ |
||||
} |
||||
if newPos == len(filter) { |
||||
err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter")) |
||||
return packet, newPos, err |
||||
} |
||||
if packet == nil { |
||||
err = NewError(ErrorFilterCompile, errors.New("ldap: error parsing filter")) |
||||
return packet, newPos, err |
||||
} |
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) |
||||
switch { |
||||
case packet.Tag == FilterEqualityMatch && condition == "*": |
||||
packet.Tag = FilterPresent |
||||
packet.Description = FilterMap[uint64(packet.Tag)] |
||||
case packet.Tag == FilterEqualityMatch && condition[0] == '*' && condition[len(condition)-1] == '*': |
||||
// Any
|
||||
packet.Tag = FilterSubstrings |
||||
packet.Description = FilterMap[uint64(packet.Tag)] |
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings") |
||||
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsAny, condition[1:len(condition)-1], "Any Substring")) |
||||
packet.AppendChild(seq) |
||||
case packet.Tag == FilterEqualityMatch && condition[0] == '*': |
||||
// Final
|
||||
packet.Tag = FilterSubstrings |
||||
packet.Description = FilterMap[uint64(packet.Tag)] |
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings") |
||||
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsFinal, condition[1:], "Final Substring")) |
||||
packet.AppendChild(seq) |
||||
case packet.Tag == FilterEqualityMatch && condition[len(condition)-1] == '*': |
||||
// Initial
|
||||
packet.Tag = FilterSubstrings |
||||
packet.Description = FilterMap[uint64(packet.Tag)] |
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Substrings") |
||||
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterSubstringsInitial, condition[:len(condition)-1], "Initial Substring")) |
||||
packet.AppendChild(seq) |
||||
default: |
||||
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, condition, "Condition")) |
||||
} |
||||
newPos++ |
||||
return packet, newPos, err |
||||
} |
||||
} |
@ -0,0 +1,78 @@ |
||||
package ldap |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/johnweldon/asn1-ber" |
||||
) |
||||
|
||||
type compileTest struct { |
||||
filterStr string |
||||
filterType int |
||||
} |
||||
|
||||
var testFilters = []compileTest{ |
||||
compileTest{filterStr: "(&(sn=Miller)(givenName=Bob))", filterType: FilterAnd}, |
||||
compileTest{filterStr: "(|(sn=Miller)(givenName=Bob))", filterType: FilterOr}, |
||||
compileTest{filterStr: "(!(sn=Miller))", filterType: FilterNot}, |
||||
compileTest{filterStr: "(sn=Miller)", filterType: FilterEqualityMatch}, |
||||
compileTest{filterStr: "(sn=Mill*)", filterType: FilterSubstrings}, |
||||
compileTest{filterStr: "(sn=*Mill)", filterType: FilterSubstrings}, |
||||
compileTest{filterStr: "(sn=*Mill*)", filterType: FilterSubstrings}, |
||||
compileTest{filterStr: "(sn>=Miller)", filterType: FilterGreaterOrEqual}, |
||||
compileTest{filterStr: "(sn<=Miller)", filterType: FilterLessOrEqual}, |
||||
compileTest{filterStr: "(sn=*)", filterType: FilterPresent}, |
||||
compileTest{filterStr: "(sn~=Miller)", filterType: FilterApproxMatch}, |
||||
// compileTest{ filterStr: "()", filterType: FilterExtensibleMatch },
|
||||
} |
||||
|
||||
func TestFilter(t *testing.T) { |
||||
// Test Compiler and Decompiler
|
||||
for _, i := range testFilters { |
||||
filter, err := CompileFilter(i.filterStr) |
||||
if err != nil { |
||||
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error()) |
||||
} else if filter.Tag != uint8(i.filterType) { |
||||
t.Errorf("%q Expected %q got %q", i.filterStr, FilterMap[uint64(i.filterType)], FilterMap[uint64(filter.Tag)]) |
||||
} else { |
||||
o, err := DecompileFilter(filter) |
||||
if err != nil { |
||||
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error()) |
||||
} else if i.filterStr != o { |
||||
t.Errorf("%q expected, got %q", i.filterStr, o) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func BenchmarkFilterCompile(b *testing.B) { |
||||
b.StopTimer() |
||||
filters := make([]string, len(testFilters)) |
||||
|
||||
// Test Compiler and Decompiler
|
||||
for idx, i := range testFilters { |
||||
filters[idx] = i.filterStr |
||||
} |
||||
|
||||
maxIdx := len(filters) |
||||
b.StartTimer() |
||||
for i := 0; i < b.N; i++ { |
||||
CompileFilter(filters[i%maxIdx]) |
||||
} |
||||
} |
||||
|
||||
func BenchmarkFilterDecompile(b *testing.B) { |
||||
b.StopTimer() |
||||
filters := make([]*ber.Packet, len(testFilters)) |
||||
|
||||
// Test Compiler and Decompiler
|
||||
for idx, i := range testFilters { |
||||
filters[idx], _ = CompileFilter(i.filterStr) |
||||
} |
||||
|
||||
maxIdx := len(filters) |
||||
b.StartTimer() |
||||
for i := 0; i < b.N; i++ { |
||||
DecompileFilter(filters[i%maxIdx]) |
||||
} |
||||
} |
@ -0,0 +1,302 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"io/ioutil" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
// LDAP Application Codes
|
||||
const ( |
||||
ApplicationBindRequest = 0 |
||||
ApplicationBindResponse = 1 |
||||
ApplicationUnbindRequest = 2 |
||||
ApplicationSearchRequest = 3 |
||||
ApplicationSearchResultEntry = 4 |
||||
ApplicationSearchResultDone = 5 |
||||
ApplicationModifyRequest = 6 |
||||
ApplicationModifyResponse = 7 |
||||
ApplicationAddRequest = 8 |
||||
ApplicationAddResponse = 9 |
||||
ApplicationDelRequest = 10 |
||||
ApplicationDelResponse = 11 |
||||
ApplicationModifyDNRequest = 12 |
||||
ApplicationModifyDNResponse = 13 |
||||
ApplicationCompareRequest = 14 |
||||
ApplicationCompareResponse = 15 |
||||
ApplicationAbandonRequest = 16 |
||||
ApplicationSearchResultReference = 19 |
||||
ApplicationExtendedRequest = 23 |
||||
ApplicationExtendedResponse = 24 |
||||
) |
||||
|
||||
var ApplicationMap = map[uint8]string{ |
||||
ApplicationBindRequest: "Bind Request", |
||||
ApplicationBindResponse: "Bind Response", |
||||
ApplicationUnbindRequest: "Unbind Request", |
||||
ApplicationSearchRequest: "Search Request", |
||||
ApplicationSearchResultEntry: "Search Result Entry", |
||||
ApplicationSearchResultDone: "Search Result Done", |
||||
ApplicationModifyRequest: "Modify Request", |
||||
ApplicationModifyResponse: "Modify Response", |
||||
ApplicationAddRequest: "Add Request", |
||||
ApplicationAddResponse: "Add Response", |
||||
ApplicationDelRequest: "Del Request", |
||||
ApplicationDelResponse: "Del Response", |
||||
ApplicationModifyDNRequest: "Modify DN Request", |
||||
ApplicationModifyDNResponse: "Modify DN Response", |
||||
ApplicationCompareRequest: "Compare Request", |
||||
ApplicationCompareResponse: "Compare Response", |
||||
ApplicationAbandonRequest: "Abandon Request", |
||||
ApplicationSearchResultReference: "Search Result Reference", |
||||
ApplicationExtendedRequest: "Extended Request", |
||||
ApplicationExtendedResponse: "Extended Response", |
||||
} |
||||
|
||||
// LDAP Result Codes
|
||||
const ( |
||||
LDAPResultSuccess = 0 |
||||
LDAPResultOperationsError = 1 |
||||
LDAPResultProtocolError = 2 |
||||
LDAPResultTimeLimitExceeded = 3 |
||||
LDAPResultSizeLimitExceeded = 4 |
||||
LDAPResultCompareFalse = 5 |
||||
LDAPResultCompareTrue = 6 |
||||
LDAPResultAuthMethodNotSupported = 7 |
||||
LDAPResultStrongAuthRequired = 8 |
||||
LDAPResultReferral = 10 |
||||
LDAPResultAdminLimitExceeded = 11 |
||||
LDAPResultUnavailableCriticalExtension = 12 |
||||
LDAPResultConfidentialityRequired = 13 |
||||
LDAPResultSaslBindInProgress = 14 |
||||
LDAPResultNoSuchAttribute = 16 |
||||
LDAPResultUndefinedAttributeType = 17 |
||||
LDAPResultInappropriateMatching = 18 |
||||
LDAPResultConstraintViolation = 19 |
||||
LDAPResultAttributeOrValueExists = 20 |
||||
LDAPResultInvalidAttributeSyntax = 21 |
||||
LDAPResultNoSuchObject = 32 |
||||
LDAPResultAliasProblem = 33 |
||||
LDAPResultInvalidDNSyntax = 34 |
||||
LDAPResultAliasDereferencingProblem = 36 |
||||
LDAPResultInappropriateAuthentication = 48 |
||||
LDAPResultInvalidCredentials = 49 |
||||
LDAPResultInsufficientAccessRights = 50 |
||||
LDAPResultBusy = 51 |
||||
LDAPResultUnavailable = 52 |
||||
LDAPResultUnwillingToPerform = 53 |
||||
LDAPResultLoopDetect = 54 |
||||
LDAPResultNamingViolation = 64 |
||||
LDAPResultObjectClassViolation = 65 |
||||
LDAPResultNotAllowedOnNonLeaf = 66 |
||||
LDAPResultNotAllowedOnRDN = 67 |
||||
LDAPResultEntryAlreadyExists = 68 |
||||
LDAPResultObjectClassModsProhibited = 69 |
||||
LDAPResultAffectsMultipleDSAs = 71 |
||||
LDAPResultOther = 80 |
||||
|
||||
ErrorNetwork = 200 |
||||
ErrorFilterCompile = 201 |
||||
ErrorFilterDecompile = 202 |
||||
ErrorDebugging = 203 |
||||
) |
||||
|
||||
var LDAPResultCodeMap = map[uint8]string{ |
||||
LDAPResultSuccess: "Success", |
||||
LDAPResultOperationsError: "Operations Error", |
||||
LDAPResultProtocolError: "Protocol Error", |
||||
LDAPResultTimeLimitExceeded: "Time Limit Exceeded", |
||||
LDAPResultSizeLimitExceeded: "Size Limit Exceeded", |
||||
LDAPResultCompareFalse: "Compare False", |
||||
LDAPResultCompareTrue: "Compare True", |
||||
LDAPResultAuthMethodNotSupported: "Auth Method Not Supported", |
||||
LDAPResultStrongAuthRequired: "Strong Auth Required", |
||||
LDAPResultReferral: "Referral", |
||||
LDAPResultAdminLimitExceeded: "Admin Limit Exceeded", |
||||
LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension", |
||||
LDAPResultConfidentialityRequired: "Confidentiality Required", |
||||
LDAPResultSaslBindInProgress: "Sasl Bind In Progress", |
||||
LDAPResultNoSuchAttribute: "No Such Attribute", |
||||
LDAPResultUndefinedAttributeType: "Undefined Attribute Type", |
||||
LDAPResultInappropriateMatching: "Inappropriate Matching", |
||||
LDAPResultConstraintViolation: "Constraint Violation", |
||||
LDAPResultAttributeOrValueExists: "Attribute Or Value Exists", |
||||
LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax", |
||||
LDAPResultNoSuchObject: "No Such Object", |
||||
LDAPResultAliasProblem: "Alias Problem", |
||||
LDAPResultInvalidDNSyntax: "Invalid DN Syntax", |
||||
LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem", |
||||
LDAPResultInappropriateAuthentication: "Inappropriate Authentication", |
||||
LDAPResultInvalidCredentials: "Invalid Credentials", |
||||
LDAPResultInsufficientAccessRights: "Insufficient Access Rights", |
||||
LDAPResultBusy: "Busy", |
||||
LDAPResultUnavailable: "Unavailable", |
||||
LDAPResultUnwillingToPerform: "Unwilling To Perform", |
||||
LDAPResultLoopDetect: "Loop Detect", |
||||
LDAPResultNamingViolation: "Naming Violation", |
||||
LDAPResultObjectClassViolation: "Object Class Violation", |
||||
LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf", |
||||
LDAPResultNotAllowedOnRDN: "Not Allowed On RDN", |
||||
LDAPResultEntryAlreadyExists: "Entry Already Exists", |
||||
LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited", |
||||
LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs", |
||||
LDAPResultOther: "Other", |
||||
} |
||||
|
||||
// Adds descriptions to an LDAP Response packet for debugging
|
||||
func addLDAPDescriptions(packet *ber.Packet) (err error) { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
err = NewError(ErrorDebugging, errors.New("ldap: cannot process packet to add descriptions")) |
||||
} |
||||
}() |
||||
packet.Description = "LDAP Response" |
||||
packet.Children[0].Description = "Message ID" |
||||
|
||||
application := packet.Children[1].Tag |
||||
packet.Children[1].Description = ApplicationMap[application] |
||||
|
||||
switch application { |
||||
case ApplicationBindRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationBindResponse: |
||||
addDefaultLDAPResponseDescriptions(packet) |
||||
case ApplicationUnbindRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationSearchRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationSearchResultEntry: |
||||
packet.Children[1].Children[0].Description = "Object Name" |
||||
packet.Children[1].Children[1].Description = "Attributes" |
||||
for _, child := range packet.Children[1].Children[1].Children { |
||||
child.Description = "Attribute" |
||||
child.Children[0].Description = "Attribute Name" |
||||
child.Children[1].Description = "Attribute Values" |
||||
for _, grandchild := range child.Children[1].Children { |
||||
grandchild.Description = "Attribute Value" |
||||
} |
||||
} |
||||
if len(packet.Children) == 3 { |
||||
addControlDescriptions(packet.Children[2]) |
||||
} |
||||
case ApplicationSearchResultDone: |
||||
addDefaultLDAPResponseDescriptions(packet) |
||||
case ApplicationModifyRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationModifyResponse: |
||||
case ApplicationAddRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationAddResponse: |
||||
case ApplicationDelRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationDelResponse: |
||||
case ApplicationModifyDNRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationModifyDNResponse: |
||||
case ApplicationCompareRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationCompareResponse: |
||||
case ApplicationAbandonRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationSearchResultReference: |
||||
case ApplicationExtendedRequest: |
||||
addRequestDescriptions(packet) |
||||
case ApplicationExtendedResponse: |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func addControlDescriptions(packet *ber.Packet) { |
||||
packet.Description = "Controls" |
||||
for _, child := range packet.Children { |
||||
child.Description = "Control" |
||||
child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")" |
||||
value := child.Children[1] |
||||
if len(child.Children) == 3 { |
||||
child.Children[1].Description = "Criticality" |
||||
value = child.Children[2] |
||||
} |
||||
value.Description = "Control Value" |
||||
|
||||
switch child.Children[0].Value.(string) { |
||||
case ControlTypePaging: |
||||
value.Description += " (Paging)" |
||||
if value.Value != nil { |
||||
valueChildren := ber.DecodePacket(value.Data.Bytes()) |
||||
value.Data.Truncate(0) |
||||
value.Value = nil |
||||
valueChildren.Children[1].Value = valueChildren.Children[1].Data.Bytes() |
||||
value.AppendChild(valueChildren) |
||||
} |
||||
value.Children[0].Description = "Real Search Control Value" |
||||
value.Children[0].Children[0].Description = "Paging Size" |
||||
value.Children[0].Children[1].Description = "Cookie" |
||||
} |
||||
} |
||||
} |
||||
|
||||
func addRequestDescriptions(packet *ber.Packet) { |
||||
packet.Description = "LDAP Request" |
||||
packet.Children[0].Description = "Message ID" |
||||
packet.Children[1].Description = ApplicationMap[packet.Children[1].Tag] |
||||
if len(packet.Children) == 3 { |
||||
addControlDescriptions(packet.Children[2]) |
||||
} |
||||
} |
||||
|
||||
func addDefaultLDAPResponseDescriptions(packet *ber.Packet) { |
||||
resultCode := packet.Children[1].Children[0].Value.(uint64) |
||||
packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[uint8(resultCode)] + ")" |
||||
packet.Children[1].Children[1].Description = "Matched DN" |
||||
packet.Children[1].Children[2].Description = "Error Message" |
||||
if len(packet.Children[1].Children) > 3 { |
||||
packet.Children[1].Children[3].Description = "Referral" |
||||
} |
||||
if len(packet.Children) == 3 { |
||||
addControlDescriptions(packet.Children[2]) |
||||
} |
||||
} |
||||
|
||||
func DebugBinaryFile(fileName string) error { |
||||
file, err := ioutil.ReadFile(fileName) |
||||
if err != nil { |
||||
return NewError(ErrorDebugging, err) |
||||
} |
||||
ber.PrintBytes(file, "") |
||||
packet := ber.DecodePacket(file) |
||||
addLDAPDescriptions(packet) |
||||
ber.PrintPacket(packet) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
type Error struct { |
||||
Err error |
||||
ResultCode uint8 |
||||
} |
||||
|
||||
func (e *Error) Error() string { |
||||
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error()) |
||||
} |
||||
|
||||
func NewError(resultCode uint8, err error) error { |
||||
return &Error{ResultCode: resultCode, Err: err} |
||||
} |
||||
|
||||
func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) { |
||||
if len(packet.Children) >= 2 { |
||||
response := packet.Children[1] |
||||
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) == 3 { |
||||
return uint8(response.Children[0].Value.(uint64)), response.Children[2].Value.(string) |
||||
} |
||||
} |
||||
|
||||
return ErrorNetwork, "Invalid packet format" |
||||
} |
@ -0,0 +1,123 @@ |
||||
package ldap |
||||
|
||||
import ( |
||||
"fmt" |
||||
"testing" |
||||
) |
||||
|
||||
var ldapServer = "ldap.itd.umich.edu" |
||||
var ldapPort = uint16(389) |
||||
var baseDN = "dc=umich,dc=edu" |
||||
var filter = []string{ |
||||
"(cn=cis-fac)", |
||||
"(&(objectclass=rfc822mailgroup)(cn=*Computer*))", |
||||
"(&(objectclass=rfc822mailgroup)(cn=*Mathematics*))"} |
||||
var attributes = []string{ |
||||
"cn", |
||||
"description"} |
||||
|
||||
func TestConnect(t *testing.T) { |
||||
fmt.Printf("TestConnect: starting...\n") |
||||
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
defer l.Close() |
||||
fmt.Printf("TestConnect: finished...\n") |
||||
} |
||||
|
||||
func TestSearch(t *testing.T) { |
||||
fmt.Printf("TestSearch: starting...\n") |
||||
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
defer l.Close() |
||||
|
||||
searchRequest := NewSearchRequest( |
||||
baseDN, |
||||
ScopeWholeSubtree, DerefAlways, 0, 0, false, |
||||
filter[0], |
||||
attributes, |
||||
nil) |
||||
|
||||
sr, err := l.Search(searchRequest) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
|
||||
fmt.Printf("TestSearch: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) |
||||
} |
||||
|
||||
func TestSearchWithPaging(t *testing.T) { |
||||
fmt.Printf("TestSearchWithPaging: starting...\n") |
||||
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
defer l.Close() |
||||
|
||||
err = l.Bind("", "") |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
|
||||
searchRequest := NewSearchRequest( |
||||
baseDN, |
||||
ScopeWholeSubtree, DerefAlways, 0, 0, false, |
||||
filter[1], |
||||
attributes, |
||||
nil) |
||||
sr, err := l.SearchWithPaging(searchRequest, 5) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
|
||||
fmt.Printf("TestSearchWithPaging: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries)) |
||||
} |
||||
|
||||
func testMultiGoroutineSearch(t *testing.T, l *Conn, results chan *SearchResult, i int) { |
||||
searchRequest := NewSearchRequest( |
||||
baseDN, |
||||
ScopeWholeSubtree, DerefAlways, 0, 0, false, |
||||
filter[i], |
||||
attributes, |
||||
nil) |
||||
sr, err := l.Search(searchRequest) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
results <- nil |
||||
return |
||||
} |
||||
results <- sr |
||||
} |
||||
|
||||
func TestMultiGoroutineSearch(t *testing.T) { |
||||
fmt.Printf("TestMultiGoroutineSearch: starting...\n") |
||||
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort)) |
||||
if err != nil { |
||||
t.Errorf(err.Error()) |
||||
return |
||||
} |
||||
defer l.Close() |
||||
|
||||
results := make([]chan *SearchResult, len(filter)) |
||||
for i := range filter { |
||||
results[i] = make(chan *SearchResult) |
||||
go testMultiGoroutineSearch(t, l, results[i], i) |
||||
} |
||||
for i := range filter { |
||||
sr := <-results[i] |
||||
if sr == nil { |
||||
t.Errorf("Did not receive results from goroutine for %q", filter[i]) |
||||
} else { |
||||
fmt.Printf("TestMultiGoroutineSearch(%d): %s -> num of entries = %d\n", i, filter[i], len(sr.Entries)) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,156 @@ |
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// File contains Modify functionality
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4511
|
||||
//
|
||||
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||
// object LDAPDN,
|
||||
// changes SEQUENCE OF change SEQUENCE {
|
||||
// operation ENUMERATED {
|
||||
// add (0),
|
||||
// delete (1),
|
||||
// replace (2),
|
||||
// ... },
|
||||
// modification PartialAttribute } }
|
||||
//
|
||||
// PartialAttribute ::= SEQUENCE {
|
||||
// type AttributeDescription,
|
||||
// vals SET OF value AttributeValue }
|
||||
//
|
||||
// AttributeDescription ::= LDAPString
|
||||
// -- Constrained to <attributedescription>
|
||||
// -- [RFC4512]
|
||||
//
|
||||
// AttributeValue ::= OCTET STRING
|
||||
//
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"errors" |
||||
"log" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
const ( |
||||
AddAttribute = 0 |
||||
DeleteAttribute = 1 |
||||
ReplaceAttribute = 2 |
||||
) |
||||
|
||||
type PartialAttribute struct { |
||||
attrType string |
||||
attrVals []string |
||||
} |
||||
|
||||
func (p *PartialAttribute) encode() *ber.Packet { |
||||
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "PartialAttribute") |
||||
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, p.attrType, "Type")) |
||||
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") |
||||
for _, value := range p.attrVals { |
||||
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) |
||||
} |
||||
seq.AppendChild(set) |
||||
return seq |
||||
} |
||||
|
||||
type ModifyRequest struct { |
||||
dn string |
||||
addAttributes []PartialAttribute |
||||
deleteAttributes []PartialAttribute |
||||
replaceAttributes []PartialAttribute |
||||
} |
||||
|
||||
func (m *ModifyRequest) Add(attrType string, attrVals []string) { |
||||
m.addAttributes = append(m.addAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals}) |
||||
} |
||||
|
||||
func (m *ModifyRequest) Delete(attrType string, attrVals []string) { |
||||
m.deleteAttributes = append(m.deleteAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals}) |
||||
} |
||||
|
||||
func (m *ModifyRequest) Replace(attrType string, attrVals []string) { |
||||
m.replaceAttributes = append(m.replaceAttributes, PartialAttribute{attrType: attrType, attrVals: attrVals}) |
||||
} |
||||
|
||||
func (m ModifyRequest) encode() *ber.Packet { |
||||
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationModifyRequest, nil, "Modify Request") |
||||
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, m.dn, "DN")) |
||||
changes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Changes") |
||||
for _, attribute := range m.addAttributes { |
||||
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") |
||||
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(AddAttribute), "Operation")) |
||||
change.AppendChild(attribute.encode()) |
||||
changes.AppendChild(change) |
||||
} |
||||
for _, attribute := range m.deleteAttributes { |
||||
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") |
||||
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(DeleteAttribute), "Operation")) |
||||
change.AppendChild(attribute.encode()) |
||||
changes.AppendChild(change) |
||||
} |
||||
for _, attribute := range m.replaceAttributes { |
||||
change := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Change") |
||||
change.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(ReplaceAttribute), "Operation")) |
||||
change.AppendChild(attribute.encode()) |
||||
changes.AppendChild(change) |
||||
} |
||||
request.AppendChild(changes) |
||||
return request |
||||
} |
||||
|
||||
func NewModifyRequest( |
||||
dn string, |
||||
) *ModifyRequest { |
||||
return &ModifyRequest{ |
||||
dn: dn, |
||||
} |
||||
} |
||||
|
||||
func (l *Conn) Modify(modifyRequest *ModifyRequest) error { |
||||
messageID := l.nextMessageID() |
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") |
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) |
||||
packet.AppendChild(modifyRequest.encode()) |
||||
|
||||
l.Debug.PrintPacket(packet) |
||||
|
||||
channel, err := l.sendMessage(packet) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if channel == nil { |
||||
return NewError(ErrorNetwork, errors.New("ldap: could not send message")) |
||||
} |
||||
defer l.finishMessage(messageID) |
||||
|
||||
l.Debug.Printf("%d: waiting for response", messageID) |
||||
packet = <-channel |
||||
l.Debug.Printf("%d: got response %p", messageID, packet) |
||||
if packet == nil { |
||||
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve message")) |
||||
} |
||||
|
||||
if l.Debug { |
||||
if err := addLDAPDescriptions(packet); err != nil { |
||||
return err |
||||
} |
||||
ber.PrintPacket(packet) |
||||
} |
||||
|
||||
if packet.Children[1].Tag == ApplicationModifyResponse { |
||||
resultCode, resultDescription := getLDAPResultCode(packet) |
||||
if resultCode != 0 { |
||||
return NewError(resultCode, errors.New(resultDescription)) |
||||
} |
||||
} else { |
||||
log.Printf("Unexpected Response: %d", packet.Children[1].Tag) |
||||
} |
||||
|
||||
l.Debug.Printf("%d: returning", messageID) |
||||
return nil |
||||
} |
@ -0,0 +1,350 @@ |
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// File contains Search functionality
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4511
|
||||
//
|
||||
// SearchRequest ::= [APPLICATION 3] SEQUENCE {
|
||||
// baseObject LDAPDN,
|
||||
// scope ENUMERATED {
|
||||
// baseObject (0),
|
||||
// singleLevel (1),
|
||||
// wholeSubtree (2),
|
||||
// ... },
|
||||
// derefAliases ENUMERATED {
|
||||
// neverDerefAliases (0),
|
||||
// derefInSearching (1),
|
||||
// derefFindingBaseObj (2),
|
||||
// derefAlways (3) },
|
||||
// sizeLimit INTEGER (0 .. maxInt),
|
||||
// timeLimit INTEGER (0 .. maxInt),
|
||||
// typesOnly BOOLEAN,
|
||||
// filter Filter,
|
||||
// attributes AttributeSelection }
|
||||
//
|
||||
// AttributeSelection ::= SEQUENCE OF selector LDAPString
|
||||
// -- The LDAPString is constrained to
|
||||
// -- <attributeSelector> in Section 4.5.1.8
|
||||
//
|
||||
// Filter ::= CHOICE {
|
||||
// and [0] SET SIZE (1..MAX) OF filter Filter,
|
||||
// or [1] SET SIZE (1..MAX) OF filter Filter,
|
||||
// not [2] Filter,
|
||||
// equalityMatch [3] AttributeValueAssertion,
|
||||
// substrings [4] SubstringFilter,
|
||||
// greaterOrEqual [5] AttributeValueAssertion,
|
||||
// lessOrEqual [6] AttributeValueAssertion,
|
||||
// present [7] AttributeDescription,
|
||||
// approxMatch [8] AttributeValueAssertion,
|
||||
// extensibleMatch [9] MatchingRuleAssertion,
|
||||
// ... }
|
||||
//
|
||||
// SubstringFilter ::= SEQUENCE {
|
||||
// type AttributeDescription,
|
||||
// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
|
||||
// initial [0] AssertionValue, -- can occur at most once
|
||||
// any [1] AssertionValue,
|
||||
// final [2] AssertionValue } -- can occur at most once
|
||||
// }
|
||||
//
|
||||
// MatchingRuleAssertion ::= SEQUENCE {
|
||||
// matchingRule [1] MatchingRuleId OPTIONAL,
|
||||
// type [2] AttributeDescription OPTIONAL,
|
||||
// matchValue [3] AssertionValue,
|
||||
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||
//
|
||||
//
|
||||
|
||||
package ldap |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"strings" |
||||
|
||||
"github.com/gogits/gogs/modules/asn1-ber" |
||||
) |
||||
|
||||
const ( |
||||
ScopeBaseObject = 0 |
||||
ScopeSingleLevel = 1 |
||||
ScopeWholeSubtree = 2 |
||||
) |
||||
|
||||
var ScopeMap = map[int]string{ |
||||
ScopeBaseObject: "Base Object", |
||||
ScopeSingleLevel: "Single Level", |
||||
ScopeWholeSubtree: "Whole Subtree", |
||||
} |
||||
|
||||
const ( |
||||
NeverDerefAliases = 0 |
||||
DerefInSearching = 1 |
||||
DerefFindingBaseObj = 2 |
||||
DerefAlways = 3 |
||||
) |
||||
|
||||
var DerefMap = map[int]string{ |
||||
NeverDerefAliases: "NeverDerefAliases", |
||||
DerefInSearching: "DerefInSearching", |
||||
DerefFindingBaseObj: "DerefFindingBaseObj", |
||||
DerefAlways: "DerefAlways", |
||||
} |
||||
|
||||
type Entry struct { |
||||
DN string |
||||
Attributes []*EntryAttribute |
||||
} |
||||
|
||||
func (e *Entry) GetAttributeValues(attribute string) []string { |
||||
for _, attr := range e.Attributes { |
||||
if attr.Name == attribute { |
||||
return attr.Values |
||||
} |
||||
} |
||||
return []string{} |
||||
} |
||||
|
||||
func (e *Entry) GetAttributeValue(attribute string) string { |
||||
values := e.GetAttributeValues(attribute) |
||||
if len(values) == 0 { |
||||
return "" |
||||
} |
||||
return values[0] |
||||
} |
||||
|
||||
func (e *Entry) Print() { |
||||
fmt.Printf("DN: %s\n", e.DN) |
||||
for _, attr := range e.Attributes { |
||||
attr.Print() |
||||
} |
||||
} |
||||
|
||||
func (e *Entry) PrettyPrint(indent int) { |
||||
fmt.Printf("%sDN: %s\n", strings.Repeat(" ", indent), e.DN) |
||||
for _, attr := range e.Attributes { |
||||
attr.PrettyPrint(indent + 2) |
||||
} |
||||
} |
||||
|
||||
type EntryAttribute struct { |
||||
Name string |
||||
Values []string |
||||
} |
||||
|
||||
func (e *EntryAttribute) Print() { |
||||
fmt.Printf("%s: %s\n", e.Name, e.Values) |
||||
} |
||||
|
||||
func (e *EntryAttribute) PrettyPrint(indent int) { |
||||
fmt.Printf("%s%s: %s\n", strings.Repeat(" ", indent), e.Name, e.Values) |
||||
} |
||||
|
||||
type SearchResult struct { |
||||
Entries []*Entry |
||||
Referrals []string |
||||
Controls []Control |
||||
} |
||||
|
||||
func (s *SearchResult) Print() { |
||||
for _, entry := range s.Entries { |
||||
entry.Print() |
||||
} |
||||
} |
||||
|
||||
func (s *SearchResult) PrettyPrint(indent int) { |
||||
for _, entry := range s.Entries { |
||||
entry.PrettyPrint(indent) |
||||
} |
||||
} |
||||
|
||||
type SearchRequest struct { |
||||
BaseDN string |
||||
Scope int |
||||
DerefAliases int |
||||
SizeLimit int |
||||
TimeLimit int |
||||
TypesOnly bool |
||||
Filter string |
||||
Attributes []string |
||||
Controls []Control |
||||
} |
||||
|
||||
func (s *SearchRequest) encode() (*ber.Packet, error) { |
||||
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationSearchRequest, nil, "Search Request") |
||||
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, s.BaseDN, "Base DN")) |
||||
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.Scope), "Scope")) |
||||
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, uint64(s.DerefAliases), "Deref Aliases")) |
||||
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.SizeLimit), "Size Limit")) |
||||
request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, uint64(s.TimeLimit), "Time Limit")) |
||||
request.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, s.TypesOnly, "Types Only")) |
||||
// compile and encode filter
|
||||
filterPacket, err := CompileFilter(s.Filter) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
request.AppendChild(filterPacket) |
||||
// encode attributes
|
||||
attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") |
||||
for _, attribute := range s.Attributes { |
||||
attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute")) |
||||
} |
||||
request.AppendChild(attributesPacket) |
||||
return request, nil |
||||
} |
||||
|
||||
func NewSearchRequest( |
||||
BaseDN string, |
||||
Scope, DerefAliases, SizeLimit, TimeLimit int, |
||||
TypesOnly bool, |
||||
Filter string, |
||||
Attributes []string, |
||||
Controls []Control, |
||||
) *SearchRequest { |
||||
return &SearchRequest{ |
||||
BaseDN: BaseDN, |
||||
Scope: Scope, |
||||
DerefAliases: DerefAliases, |
||||
SizeLimit: SizeLimit, |
||||
TimeLimit: TimeLimit, |
||||
TypesOnly: TypesOnly, |
||||
Filter: Filter, |
||||
Attributes: Attributes, |
||||
Controls: Controls, |
||||
} |
||||
} |
||||
|
||||
func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) { |
||||
if searchRequest.Controls == nil { |
||||
searchRequest.Controls = make([]Control, 0) |
||||
} |
||||
|
||||
pagingControl := NewControlPaging(pagingSize) |
||||
searchRequest.Controls = append(searchRequest.Controls, pagingControl) |
||||
searchResult := new(SearchResult) |
||||
for { |
||||
result, err := l.Search(searchRequest) |
||||
l.Debug.Printf("Looking for Paging Control...") |
||||
if err != nil { |
||||
return searchResult, err |
||||
} |
||||
if result == nil { |
||||
return searchResult, NewError(ErrorNetwork, errors.New("ldap: packet not received")) |
||||
} |
||||
|
||||
for _, entry := range result.Entries { |
||||
searchResult.Entries = append(searchResult.Entries, entry) |
||||
} |
||||
for _, referral := range result.Referrals { |
||||
searchResult.Referrals = append(searchResult.Referrals, referral) |
||||
} |
||||
for _, control := range result.Controls { |
||||
searchResult.Controls = append(searchResult.Controls, control) |
||||
} |
||||
|
||||
l.Debug.Printf("Looking for Paging Control...") |
||||
pagingResult := FindControl(result.Controls, ControlTypePaging) |
||||
if pagingResult == nil { |
||||
pagingControl = nil |
||||
l.Debug.Printf("Could not find paging control. Breaking...") |
||||
break |
||||
} |
||||
|
||||
cookie := pagingResult.(*ControlPaging).Cookie |
||||
if len(cookie) == 0 { |
||||
pagingControl = nil |
||||
l.Debug.Printf("Could not find cookie. Breaking...") |
||||
break |
||||
} |
||||
pagingControl.SetCookie(cookie) |
||||
} |
||||
|
||||
if pagingControl != nil { |
||||
l.Debug.Printf("Abandoning Paging...") |
||||
pagingControl.PagingSize = 0 |
||||
l.Search(searchRequest) |
||||
} |
||||
|
||||
return searchResult, nil |
||||
} |
||||
|
||||
func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { |
||||
messageID := l.nextMessageID() |
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") |
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID")) |
||||
// encode search request
|
||||
encodedSearchRequest, err := searchRequest.encode() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
packet.AppendChild(encodedSearchRequest) |
||||
// encode search controls
|
||||
if searchRequest.Controls != nil { |
||||
packet.AppendChild(encodeControls(searchRequest.Controls)) |
||||
} |
||||
|
||||
l.Debug.PrintPacket(packet) |
||||
|
||||
channel, err := l.sendMessage(packet) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if channel == nil { |
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not send message")) |
||||
} |
||||
defer l.finishMessage(messageID) |
||||
|
||||
result := &SearchResult{ |
||||
Entries: make([]*Entry, 0), |
||||
Referrals: make([]string, 0), |
||||
Controls: make([]Control, 0)} |
||||
|
||||
foundSearchResultDone := false |
||||
for !foundSearchResultDone { |
||||
l.Debug.Printf("%d: waiting for response", messageID) |
||||
packet = <-channel |
||||
l.Debug.Printf("%d: got response %p", messageID, packet) |
||||
if packet == nil { |
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message")) |
||||
} |
||||
|
||||
if l.Debug { |
||||
if err := addLDAPDescriptions(packet); err != nil { |
||||
return nil, err |
||||
} |
||||
ber.PrintPacket(packet) |
||||
} |
||||
|
||||
switch packet.Children[1].Tag { |
||||
case 4: |
||||
entry := new(Entry) |
||||
entry.DN = packet.Children[1].Children[0].Value.(string) |
||||
for _, child := range packet.Children[1].Children[1].Children { |
||||
attr := new(EntryAttribute) |
||||
attr.Name = child.Children[0].Value.(string) |
||||
for _, value := range child.Children[1].Children { |
||||
attr.Values = append(attr.Values, value.Value.(string)) |
||||
} |
||||
entry.Attributes = append(entry.Attributes, attr) |
||||
} |
||||
result.Entries = append(result.Entries, entry) |
||||
case 5: |
||||
resultCode, resultDescription := getLDAPResultCode(packet) |
||||
if resultCode != 0 { |
||||
return result, NewError(resultCode, errors.New(resultDescription)) |
||||
} |
||||
if len(packet.Children) == 3 { |
||||
for _, child := range packet.Children[2].Children { |
||||
result.Controls = append(result.Controls, DecodeControl(child)) |
||||
} |
||||
} |
||||
foundSearchResultDone = true |
||||
case 19: |
||||
result.Referrals = append(result.Referrals, packet.Children[1].Children[0].Value.(string)) |
||||
} |
||||
} |
||||
l.Debug.Printf("%d: returning", messageID) |
||||
return result, nil |
||||
} |
Loading…
Reference in new issue