Compare commits

...

23 commits

Author SHA1 Message Date
MrArmonius
5f0f6a730a Add goldap source files + modified import 2021-07-13 15:23:50 +02:00
MrArmonius
7c8bd455e6 Delete goldap, switch to another branch 2021-07-13 15:09:16 +02:00
MrArmonius
2f09e10933 suppress kill in the script(provoked error 143) 2021-07-09 16:46:00 +02:00
MrArmonius
49fcbcae7c Exit 0 in the script 2021-07-09 16:43:43 +02:00
MrArmonius
3f8e633045 Change return code 143 to 0 2021-07-09 16:36:23 +02:00
MrArmonius
e4778a9a89 Add launch script and remove openssl 2021-07-09 16:34:27 +02:00
MrArmonius
eb7f00c526 Add config.json.test to use in Go test 2021-07-09 16:32:02 +02:00
MrArmonius
a9b04a255f Test bottin comportment 2021-07-09 16:27:15 +02:00
MrArmonius
3e658ac201 Add sign Drone and suppress opennssl package in script 2021-07-09 16:24:03 +02:00
MrArmonius
a9d9484f23 Add sign and transform bash to ash script 2021-07-09 16:04:19 +02:00
MrArmonius
974d8806d8 Add sign and bash command 2021-07-09 15:55:59 +02:00
MrArmonius
986ef6d59d Add sign to .drone 2021-07-09 15:53:12 +02:00
MrArmonius
09512a271d Test .drone ls step bootin 2021-07-09 15:47:54 +02:00
MrArmonius
139a40bdf3 Ajout d'une step pour lancer le test integration 2021-07-09 15:37:07 +02:00
MrArmonius
bfd12a48c4 Add test_automatic/integration binarie 2021-07-09 15:29:09 +02:00
MrArmonius
aeac8faf15 Ajout Nouvelle Version integration.go 2021-07-09 15:27:13 +02:00
MrArmonius
2f350965ce Modification server init()
Lecture Password
Ajout d'une lecture de password à travers la variable d'environnement
"BOTTIN_DEFAULT_ADMIN_PW" sinon génère aléatoirement
2021-07-09 15:24:33 +02:00
MrArmonius
5fbe39c38a Création script bash de test 2021-07-09 15:23:21 +02:00
MrArmonius
f93f9e6958 Rajout script bash pour drone 2021-07-08 16:56:09 +02:00
MrArmonius
f294b66874 test_automatique fini et fonctionnel 2021-07-08 16:22:30 +02:00
MrArmonius
32cce4c4d3 Ajout des fichiers WireSharks scan pour problème messageID 2021-07-08 10:45:05 +02:00
MrArmonius
9e4b4a0162 Mise en place du code source goldap
Changement des imports dans les fichiers go de bottin et ldapserver.
Rajout d'un fichier txt dans test-automatique pour reproduire le bug des
requêtes ldap.
2021-07-07 18:26:02 +02:00
MrArmonius
75f4a916c3 Tests écrit en go sur la réaction du serveur Bottin
Tests réalisés:
- création aléatoires des Users et Groups- LDAP ADD
- vérifications des attributs sur Consul et ceux qu'on a ajouté - LDAP
  Search
- modifications des attributs des users et groups puis vérifications -
  LDAP Modify
2021-07-07 01:49:33 +02:00
95 changed files with 12402 additions and 21 deletions

View file

@ -1,13 +1,26 @@
---
pipeline:
build:
image: golang:stretch
commands:
- go get -d -v
- go build -v
kind: pipeline
name: bottin
steps:
- name: build
image: golang:stretch
commands:
- go get -d -v
- go build -v
- cd test_automatic
- go get -d -v
- go build -v
- name: test_bottin
image: consul:latest
environment:
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
commands:
- ash test_automatic/start_test.sh
---
kind: signature
hmac: 8f49fdf0e4abb0790827eed7cac8eedd5e11705d1fa01954a84929933eb7b254
hmac: a4455c124ee87ca8b0ef1779560703573f3a3f24d406e4cb281b9e0dab4ceeda
...

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
bottin
bottin.static
config.json
test_automatic/integration

1
go.mod
View file

@ -3,6 +3,7 @@ module bottin
go 1.13
require (
github.com/go-ldap/ldap/v3 v3.3.0
github.com/google/uuid v1.1.1
github.com/hashicorp/consul/api v1.3.0
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3

14
go.sum
View file

@ -1,3 +1,5 @@
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -7,6 +9,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@ -79,12 +86,19 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

25
goldap/abandon_request.go Normal file
View file

@ -0,0 +1,25 @@
package message
import "fmt"
//
// AbandonRequest ::= [APPLICATION 16] MessageID
func readAbandonRequest(bytes *Bytes) (ret AbandonRequest, err error) {
var mes MessageID
mes, err = readTaggedMessageID(bytes, classApplication, TagAbandonRequest)
if err != nil {
err = LdapError{fmt.Sprintf("readAbandonRequest:\n%s", err.Error())}
return
}
ret = AbandonRequest(mes)
return
}
func (abandon AbandonRequest) size() int {
return MessageID(abandon).sizeTagged(TagAbandonRequest)
}
func (abandon AbandonRequest) write(bytes *Bytes) int {
return MessageID(abandon).writeTagged(bytes, classApplication, TagAbandonRequest)
}

53
goldap/add_request.go Normal file
View file

@ -0,0 +1,53 @@
package message
import "fmt"
//
// AddRequest ::= [APPLICATION 8] SEQUENCE {
// entry LDAPDN,
// attributes AttributeList }
func (add *AddRequest) Entry() LDAPDN {
return add.entry
}
func (add *AddRequest) Attributes() AttributeList {
return add.attributes
}
func readAddRequest(bytes *Bytes) (ret AddRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagAddRequest, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readAddRequest:\n%s", err.Error())}
return
}
return
}
func (add *AddRequest) readComponents(bytes *Bytes) (err error) {
add.entry, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
add.attributes, err = readAttributeList(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (add AddRequest) size() (size int) {
size += add.entry.size()
size += add.attributes.size()
size += sizeTagAndLength(TagAddRequest, size)
return
}
func (add AddRequest) write(bytes *Bytes) (size int) {
size += add.attributes.write(bytes)
size += add.entry.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagAddRequest, size)
return
}

28
goldap/add_response.go Normal file
View file

@ -0,0 +1,28 @@
package message
import "fmt"
//
// AddResponse ::= [APPLICATION 9] LDAPResult
func (l *AddResponse) SetResultCode(code int) {
l.resultCode = ENUMERATED(code)
}
func readAddResponse(bytes *Bytes) (ret AddResponse, err error) {
var res LDAPResult
res, err = readTaggedLDAPResult(bytes, classApplication, TagAddResponse)
if err != nil {
err = LdapError{fmt.Sprintf("readAddResponse:\n%s", err.Error())}
return
}
ret = AddResponse(res)
return
}
func (a AddResponse) size() int {
return LDAPResult(a).sizeTagged(TagAddResponse)
}
func (a AddResponse) write(bytes *Bytes) int {
return LDAPResult(a).writeTagged(bytes, classApplication, TagAddResponse)
}

740
goldap/asn1.go Normal file
View file

@ -0,0 +1,740 @@
package message
// Below code is largely inspired from the standard golang library encoding/asn
// If put BEGIN / END tags in the comments to give the original library name
import (
// "errors"
"fmt"
"math/big"
// "strconv"
// "time"
)
//
// BEGIN: encoding/asn1/common.go
//
// 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.
const (
tagBoolean = 1
tagInteger = 2
// tagBitString = 3
tagOctetString = 4
// tagOID = 6
tagEnum = 10
// tagUTF8String = 12
tagSequence = 16
tagSet = 17
// tagPrintableString = 19
// tagT61String = 20
// tagIA5String = 22
// tagUTCTime = 23
// tagGeneralizedTime = 24
tagGeneralString = 27
)
var tagNames = map[int]string{
tagBoolean: "BOOLEAN",
tagInteger: "INTEGER",
tagOctetString: "OCTET STRING",
tagEnum: "ENUM",
tagSequence: "SEQUENCE",
tagSet: "SET",
}
const (
classUniversal = 0
classApplication = 1
classContextSpecific = 2
// classPrivate = 3
)
var classNames = map[int]string{
classUniversal: "UNIVERSAL",
classApplication: "APPLICATION",
classContextSpecific: "CONTEXT SPECIFIC",
}
const (
isCompound = true
isNotCompound = false
)
var compoundNames = map[bool]string{
isCompound: "COMPOUND",
isNotCompound: "NOT COMPOUND",
}
type TagAndLength struct {
Class, Tag, Length int
IsCompound bool
}
//
// END: encoding/asn1/common.go
//
func (t *TagAndLength) Expect(class int, tag int, isCompound bool) (err error) {
err = t.ExpectClass(class)
if err != nil {
return LdapError{fmt.Sprintf("Expect: %s.", err)}
}
err = t.ExpectTag(tag)
if err != nil {
return LdapError{fmt.Sprintf("Expect: %s.", err)}
}
err = t.ExpectCompound(isCompound)
if err != nil {
return LdapError{fmt.Sprintf("Expect: %s.", err)}
}
return
}
func (t *TagAndLength) ExpectClass(class int) (err error) {
if class != t.Class {
err = SyntaxError{fmt.Sprintf("ExpectClass: wrong tag class: got %d (%s), expected %d (%s)", t.Class, classNames[t.Class], class, classNames[class])}
}
return
}
func (t *TagAndLength) ExpectTag(tag int) (err error) {
if tag != t.Tag {
err = SyntaxError{fmt.Sprintf("ExpectTag: wrong tag value: got %d (%s), expected %d (%s)", t.Tag, tagNames[t.Tag], tag, tagNames[tag])}
}
return
}
func (t *TagAndLength) ExpectCompound(isCompound bool) (err error) {
if isCompound != t.IsCompound {
err = SyntaxError{fmt.Sprintf("ExpectCompound: wrong tag compound: got %t (%s), expected %t (%s)", t.IsCompound, compoundNames[t.IsCompound], isCompound, compoundNames[isCompound])}
}
return
}
func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
ret, offset, err = parseTagAndLength(bytes, initOffset)
return
}
//
// BEGIN encoding/asn1/asn1.go
//
// 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.
// Package asn1 implements parsing of DER-encoded ASN.1 data structures,
// as defined in ITU-T Rec X.690.
//
// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
// package asn1
// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
// are different encoding formats for those objects. Here, we'll be dealing
// with DER, the Distinguished Encoding Rules. DER is used in X.509 because
// it's fast to parse and, unlike BER, has a unique encoding for every object.
// When calculating hashes over objects, it's important that the resulting
// bytes be the same at both ends and DER removes this margin of error.
//
// ASN.1 is very complex and this package doesn't attempt to implement
// everything by any means.
//import (
// "fmt"
// "math/big"
// "reflect"
// "strconv"
// "time"
//)
// A StructuralError suggests that the ASN.1 data is valid, but the Go type
// which is receiving it doesn't match.
type StructuralError struct {
Msg string
}
func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError struct {
Msg string
}
func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
// We start by dealing with each of the primitive types in turn.
// BOOLEAN
func parseBool(bytes []byte) (ret bool, err error) {
if len(bytes) > 1 {
err = SyntaxError{"invalid boolean: should be encoded on one byte only"}
return
} else if len(bytes) == 0 {
err = SyntaxError{"invalid boolean: no data to read"}
}
// DER demands that "If the encoding represents the boolean value TRUE,
// its single contents octet shall have all eight bits set to one."
// Thus only 0 and 255 are valid encoded values.
switch bytes[0] {
case 0:
ret = false
case 0xff:
ret = true
default:
err = SyntaxError{"invalid boolean: should be 0x00 of 0xFF"}
}
return
}
func sizeBool(b bool) int {
return 1
}
func writeBool(bytes *Bytes, b bool) int {
if b == false {
return bytes.writeBytes([]byte{0x00})
} else {
return bytes.writeBytes([]byte{0xff})
}
}
// INTEGER
// parseInt64 treats the given bytes as a big-endian, signed integer and
// returns the result.
func parseInt64(bytes []byte) (ret int64, err error) {
if len(bytes) > 8 {
// We'll overflow an int64 in this case.
err = StructuralError{"integer too large"}
return
}
for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
ret <<= 8
ret |= int64(bytes[bytesRead])
}
// Shift up and down in order to sign extend the result.
ret <<= 64 - uint8(len(bytes))*8
ret >>= 64 - uint8(len(bytes))*8
return
}
func sizeInt64(i int64) (size int) {
for ; i != 0 || size == 0; i >>= 8 {
size++
}
return
}
func writeInt64(bytes *Bytes, i int64) (size int) {
for ; i != 0 || size == 0; i >>= 8 { // Write at least one byte even if the value is 0
bytes.writeBytes([]byte{byte(i)})
size++
}
return
}
// parseInt treats the given bytes as a big-endian, signed integer and returns
// the result.
func parseInt32(bytes []byte) (int32, error) {
ret64, err := parseInt64(bytes)
if err != nil {
return 0, err
}
if ret64 != int64(int32(ret64)) {
return 0, StructuralError{"integer too large"}
}
return int32(ret64), nil
}
func sizeInt32(i int32) int {
return sizeInt64(int64(i))
}
func writeInt32(bytes *Bytes, i int32) int {
return writeInt64(bytes, int64(i))
}
var bigOne = big.NewInt(1)
// // parseBigInt treats the given bytes as a big-endian, signed integer and returns
// // the result.
// func parseBigInt(bytes []byte) *big.Int {
// ret := new(big.Int)
// if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
// // This is a negative number.
// notBytes := make([]byte, len(bytes))
// for i := range notBytes {
// notBytes[i] = ^bytes[i]
// }
// ret.SetBytes(notBytes)
// ret.Add(ret, bigOne)
// ret.Neg(ret)
// return ret
// }
// ret.SetBytes(bytes)
// return ret
// }
// // BIT STRING
// // BitString is the structure to use when you want an ASN.1 BIT STRING type. A
// // bit string is padded up to the nearest byte in memory and the number of
// // valid bits is recorded. Padding bits will be zero.
// type BitString struct {
// Bytes []byte // bits packed into bytes.
// BitLength int // length in bits.
// }
// // At returns the bit at the given index. If the index is out of range it
// // returns false.
// func (b BitString) At(i int) int {
// if i < 0 || i >= b.BitLength {
// return 0
// }
// x := i / 8
// y := 7 - uint(i%8)
// return int(b.Bytes[x]>>y) & 1
// }
// // RightAlign returns a slice where the padding bits are at the beginning. The
// // slice may share memory with the BitString.
// func (b BitString) RightAlign() []byte {
// shift := uint(8 - (b.BitLength % 8))
// if shift == 8 || len(b.Bytes) == 0 {
// return b.Bytes
// }
// a := make([]byte, len(b.Bytes))
// a[0] = b.Bytes[0] >> shift
// for i := 1; i < len(b.Bytes); i++ {
// a[i] = b.Bytes[i-1] << (8 - shift)
// a[i] |= b.Bytes[i] >> shift
// }
// return a
// }
// // parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
// func parseBitString(bytes []byte) (ret BitString, err error) {
// if len(bytes) == 0 {
// err = SyntaxError{"zero length BIT STRING"}
// return
// }
// paddingBits := int(bytes[0])
// if paddingBits > 7 ||
// len(bytes) == 1 && paddingBits > 0 ||
// bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
// err = SyntaxError{"invalid padding bits in BIT STRING"}
// return
// }
// ret.BitLength = (len(bytes)-1)*8 - paddingBits
// ret.Bytes = bytes[1:]
// return
// }
// OBJECT IDENTIFIER
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
// type ObjectIdentifier []int
// // Equal reports whether oi and other represent the same identifier.
// func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
// if len(oi) != len(other) {
// return false
// }
// for i := 0; i < len(oi); i++ {
// if oi[i] != other[i] {
// return false
// }
// }
// return true
// }
// func (oi ObjectIdentifier) String() string {
// var s string
// for i, v := range oi {
// if i > 0 {
// s += "."
// }
// s += strconv.Itoa(v)
// }
// return s
// }
// // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
// // returns it. An object identifier is a sequence of variable length integers
// // that are assigned in a hierarchy.
// func parseObjectIdentifier(bytes []byte) (s []int, err error) {
// if len(bytes) == 0 {
// err = SyntaxError{"zero length OBJECT IDENTIFIER"}
// return
// }
// // In the worst case, we get two elements from the first byte (which is
// // encoded differently) and then every varint is a single byte long.
// s = make([]int, len(bytes)+1)
// // The first varint is 40*value1 + value2:
// // According to this packing, value1 can take the values 0, 1 and 2 only.
// // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
// // then there are no restrictions on value2.
// v, offset, err := parseBase128Int(bytes, 0)
// if err != nil {
// return
// }
// if v < 80 {
// s[0] = v / 40
// s[1] = v % 40
// } else {
// s[0] = 2
// s[1] = v - 80
// }
// i := 2
// for ; offset < len(bytes); i++ {
// v, offset, err = parseBase128Int(bytes, offset)
// if err != nil {
// return
// }
// s[i] = v
// }
// s = s[0:i]
// return
// }
// ENUMERATED
// An Enumerated is represented as a plain int.
type Enumerated int
// FLAG
// A Flag accepts any data and is set to true if present.
type Flag bool
// parseBase128Int parses a base-128 encoded int from the given offset in the
// given byte slice. It returns the value and the new offset.
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
offset = initOffset
for shifted := 0; offset < len(bytes); shifted++ {
if shifted > 4 {
err = StructuralError{"base 128 integer too large"}
return
}
ret <<= 7
b := bytes[offset]
ret |= int(b & 0x7f)
offset++
if b&0x80 == 0 {
return
}
}
err = SyntaxError{"truncated base 128 integer"}
return
}
func sizeBase128Int(value int) (size int) {
for i := value; i > 0; i >>= 7 {
size++
}
return
}
// Write start as the end of the slice and goes back
// We assume we have enough size
func writeBase128Int(bytes *Bytes, value int) (size int) {
for ; value > 0 || size == 0; value >>= 7 { // Write at least one byte even if the value is 0
// Get the 7 lowest bits
b := byte(value) & 0x7f
if value < 128 {
b |= 0x80
}
bytes.writeBytes([]byte{b})
size++
}
return
}
// // UTCTime
// func parseUTCTime(bytes []byte) (ret time.Time, err error) {
// s := string(bytes)
// ret, err = time.Parse("0601021504Z0700", s)
// if err != nil {
// ret, err = time.Parse("060102150405Z0700", s)
// }
// if err == nil && ret.Year() >= 2050 {
// // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
// ret = ret.AddDate(-100, 0, 0)
// }
// return
// }
// // parseGeneralizedTime parses the GeneralizedTime from the given byte slice
// // and returns the resulting time.
// func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
// return time.Parse("20060102150405Z0700", string(bytes))
// }
// // PrintableString
// // parsePrintableString parses a ASN.1 PrintableString from the given byte
// // array and returns it.
// func parsePrintableString(bytes []byte) (ret string, err error) {
// for _, b := range bytes {
// if !isPrintable(b) {
// err = SyntaxError{"PrintableString contains invalid character"}
// return
// }
// }
// ret = string(bytes)
// return
// }
// // isPrintable returns true iff the given b is in the ASN.1 PrintableString set.
// func isPrintable(b byte) bool {
// return 'a' <= b && b <= 'z' ||
// 'A' <= b && b <= 'Z' ||
// '0' <= b && b <= '9' ||
// '\'' <= b && b <= ')' ||
// '+' <= b && b <= '/' ||
// b == ' ' ||
// b == ':' ||
// b == '=' ||
// b == '?' ||
// // This is technically not allowed in a PrintableString.
// // However, x509 certificates with wildcard strings don't
// // always use the correct string type so we permit it.
// b == '*'
// }
// // IA5String
// // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
// // byte slice and returns it.
// func parseIA5String(bytes []byte) (ret string, err error) {
// for _, b := range bytes {
// if b >= 0x80 {
// err = SyntaxError{"IA5String contains invalid character"}
// return
// }
// }
// ret = string(bytes)
// return
// }
// // T61String
// // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
// // byte slice and returns it.
// func parseT61String(bytes []byte) (ret string, err error) {
// return string(bytes), nil
// }
// UTF8String
// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
// array and returns it.
// func parseUTF8String(bytes []byte) (ret string, err error) {
// return string(bytes), nil
// }
// func sizeUTF8String(s string) int {
// return len(s)
// }
// func writeUTF8String(bytes *Bytes, s string) int {
// return bytes.writeString(s)
// }
// Octet string
func parseOctetString(bytes []byte) (ret []byte, err error) {
return bytes, nil
}
func sizeOctetString(s []byte) int {
return len(s)
}
func writeOctetString(bytes *Bytes, s []byte) int {
return bytes.writeBytes(s)
}
// A RawValue represents an undecoded ASN.1 object.
type RawValue struct {
Class, Tag int
IsCompound bool
Bytes []byte
FullBytes []byte // includes the tag and length
}
// RawContent is used to signal that the undecoded, DER data needs to be
// preserved for a struct. To use it, the first field of the struct must have
// this type. It's an error for any of the other fields to have this type.
type RawContent []byte
// Tagging
// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
// into a byte slice. It returns the parsed data and the new offset. SET and
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
// don't distinguish between ordered and unordered objects in this code.
func parseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
offset = initOffset
b := bytes[offset]
offset++
ret.Class = int(b >> 6)
ret.IsCompound = b&0x20 == 0x20
ret.Tag = int(b & 0x1f)
// If the bottom five bits are set, then the tag number is actually base 128
// encoded afterwards
if ret.Tag == 0x1f {
ret.Tag, offset, err = parseBase128Int(bytes, offset)
if err != nil {
return
}
}
if offset >= len(bytes) {
err = SyntaxError{"truncated tag or length"}
return
}
b = bytes[offset]
offset++
if b&0x80 == 0 {
// The length is encoded in the bottom 7 bits.
ret.Length = int(b & 0x7f)
} else {
// Bottom 7 bits give the number of length bytes to follow.
numBytes := int(b & 0x7f)
if numBytes == 0 {
err = SyntaxError{"indefinite length found (not DER)"}
return
}
ret.Length = 0
for i := 0; i < numBytes; i++ {
if offset >= len(bytes) {
err = SyntaxError{"truncated tag or length"}
return
}
b = bytes[offset]
offset++
if ret.Length >= 1<<23 {
// We can't shift ret.length up without
// overflowing.
err = StructuralError{"length too large"}
return
}
ret.Length <<= 8
ret.Length |= int(b)
if ret.Length == 0 {
// DER requires that lengths be minimal.
err = StructuralError{"superfluous leading zeros in length"}
return
}
}
}
return
}
// func writeTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
// b := uint8(t.class) << 6
// if t.isCompound {
// b |= 0x20
// }
// if t.tag >= 31 {
// b |= 0x1f
// err = out.WriteByte(b)
// if err != nil {
// return
// }
// err = marshalBase128Int(out, int64(t.tag))
// if err != nil {
// return
// }
// } else {
// b |= uint8(t.tag)
// err = out.WriteByte(b)
// if err != nil {
// return
// }
// }
// if t.length >= 128 {
// l := lengthLength(t.length)
// err = out.WriteByte(0x80 | byte(l))
// if err != nil {
// return
// }
// err = marshalLength(out, t.length)
// if err != nil {
// return
// }
// } else {
// err = out.WriteByte(byte(t.length))
// if err != nil {
// return
// }
// }
// return nil
// }
func sizeTagAndLength(tag int, length int) (size int) {
// Compute the size of the tag
size = 1
if tag >= 31 {
// Long-form identifier if the tag is greater than 30
// http://en.wikipedia.org/wiki/X.690#Identifier_tags_greater_than_30
size += sizeBase128Int(tag)
}
// Compute the size of the length using the definite form
// http://en.wikipedia.org/wiki/X.690#The_definite_form
size += 1
if length >= 128 {
size += 1
for length > 255 {
size++
length >>= 8
}
}
return
}
func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) {
// We are writing backward, so write the length bytes first
if t.Length < 0 {
panic("Can't have a negative length")
} else if t.Length >= 128 {
lengthBytes := writeInt64(bytes, int64(t.Length))
bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))})
size += lengthBytes + 1
} else if t.Length < 128 {
size += bytes.writeBytes([]byte{byte(t.Length)})
}
// Then write the tag
b := uint8(t.Class) << 6
if t.IsCompound {
b |= 0x20
}
if t.Tag >= 31 {
b |= 0x1f
size += writeBase128Int(bytes, t.Tag)
} else {
b |= uint8(t.Tag)
}
size += bytes.writeBytes([]byte{byte(b)})
return
}
//
// END encoding/asn1/asn1.go
//

44
goldap/assertion_value.go Normal file
View file

@ -0,0 +1,44 @@
package message
import "fmt"
//
// AssertionValue ::= OCTET STRING
func readAssertionValue(bytes *Bytes) (assertionvalue AssertionValue, err error) {
var octetstring OCTETSTRING
octetstring, err = readOCTETSTRING(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readAssertionValue:\n%s", err.Error())}
return
}
assertionvalue = AssertionValue(octetstring)
return
}
func readTaggedAssertionValue(bytes *Bytes, class int, tag int) (assertionvalue AssertionValue, err error) {
var octetstring OCTETSTRING
octetstring, err = readTaggedOCTETSTRING(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedAssertionValue:\n%s", err.Error())}
return
}
assertionvalue = AssertionValue(octetstring)
return
}
func (assertion AssertionValue) size() int {
return OCTETSTRING(assertion).size()
}
func (assertion AssertionValue) sizeTagged(tag int) int {
return OCTETSTRING(assertion).sizeTagged(tag)
}
func (assertion AssertionValue) write(bytes *Bytes) int {
return OCTETSTRING(assertion).write(bytes)
}
func (assertion AssertionValue) writeTagged(bytes *Bytes, class int, tag int) int {
return OCTETSTRING(assertion).writeTagged(bytes, class, tag)
}

40
goldap/attribute.go Normal file
View file

@ -0,0 +1,40 @@
package message
import "fmt"
//
// Attribute ::= PartialAttribute(WITH COMPONENTS {
// ...,
// vals (SIZE(1..MAX))})
func (attribute *Attribute) Type_() AttributeDescription {
return attribute.type_
}
func (attribute *Attribute) Vals() []AttributeValue {
return attribute.vals
}
func readAttribute(bytes *Bytes) (ret Attribute, err error) {
var par PartialAttribute
par, err = readPartialAttribute(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readAttribute:\n%s", err.Error())}
return
}
if len(par.vals) == 0 {
err = LdapError{"readAttribute: expecting at least one value"}
return
}
ret = Attribute(par)
return
}
func (attribute Attribute) size() (size int) {
return PartialAttribute(attribute).size()
}
func (attribute Attribute) write(bytes *Bytes) (size int) {
return PartialAttribute(attribute).write(bytes)
}

View file

@ -0,0 +1,50 @@
package message
import "fmt"
//
// AttributeDescription ::= LDAPString
// -- Constrained to <attributedescription>
// -- [RFC4512]
func (description AttributeDescription) Pointer() *AttributeDescription { return &description }
func readAttributeDescription(bytes *Bytes) (ret AttributeDescription, err error) {
var ldapstring LDAPString
ldapstring, err = readLDAPString(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readAttributeDescription:\n%s", err.Error())}
return
}
// @TODO: check RFC4512
ret = AttributeDescription(ldapstring)
return
}
func readTaggedAttributeDescription(bytes *Bytes, class int, tag int) (ret AttributeDescription, err error) {
var ldapstring LDAPString
ldapstring, err = readTaggedLDAPString(bytes, class, tag)
// @TODO: check RFC4512
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedAttributeDescription:\n%s", err.Error())}
return
}
ret = AttributeDescription(ldapstring)
return
}
func (description AttributeDescription) size() int {
return LDAPString(description).size()
}
func (description AttributeDescription) sizeTagged(tag int) int {
return LDAPString(description).sizeTagged(tag)
}
func (description AttributeDescription) write(bytes *Bytes) int {
return LDAPString(description).write(bytes)
}
func (description AttributeDescription) writeTagged(bytes *Bytes, class int, tag int) int {
return LDAPString(description).writeTagged(bytes, class, tag)
}

43
goldap/attribute_list.go Normal file
View file

@ -0,0 +1,43 @@
package message
import "fmt"
//
// AttributeList ::= SEQUENCE OF attribute Attribute
func readAttributeList(bytes *Bytes) (ret AttributeList, err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readAttributeList:\n%s", err.Error())}
return
}
return
}
func (list *AttributeList) readComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var attr Attribute
attr, err = readAttribute(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
*list = append(*list, attr)
}
return
}
func (list AttributeList) size() (size int) {
for _, att := range list {
size += att.size()
}
size += sizeTagAndLength(tagSequence, size)
return
}
func (list AttributeList) write(bytes *Bytes) (size int) {
for i := len(list) - 1; i >= 0; i-- {
size += list[i].write(bytes)
}
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}

View file

@ -0,0 +1,46 @@
package message
import "fmt"
//
// AttributeSelection ::= SEQUENCE OF selector LDAPString
// -- The LDAPString is constrained to
// -- <attributeSelector> in Section 4.5.1.8
func readAttributeSelection(bytes *Bytes) (attributeSelection AttributeSelection, err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, attributeSelection.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readAttributeSelection:\n%s", err.Error())}
return
}
return
}
func (selection *AttributeSelection) readComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var ldapstring LDAPString
ldapstring, err = readLDAPString(bytes)
// @TOTO: check <attributeSelector> in Section 4.5.1.8
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
*selection = append(*selection, ldapstring)
}
return
}
func (selection AttributeSelection) write(bytes *Bytes) (size int) {
for i := len(selection) - 1; i >= 0; i-- {
size += selection[i].write(bytes)
}
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
func (selection AttributeSelection) size() (size int) {
for _, selector := range selection {
size += selector.size()
}
size += sizeTagAndLength(tagSequence, size)
return
}

24
goldap/attribute_value.go Normal file
View file

@ -0,0 +1,24 @@
package message
import "fmt"
//
// AttributeValue ::= OCTET STRING
func readAttributeValue(bytes *Bytes) (ret AttributeValue, err error) {
octetstring, err := readOCTETSTRING(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readAttributeValue:\n%s", err.Error())}
return
}
ret = AttributeValue(octetstring)
return
}
func (value AttributeValue) write(bytes *Bytes) int {
return OCTETSTRING(value).write(bytes)
}
func (value AttributeValue) size() int {
return OCTETSTRING(value).size()
}

View file

@ -0,0 +1,77 @@
package message
import "fmt"
//
// AttributeValueAssertion ::= SEQUENCE {
// attributeDesc AttributeDescription,
// assertionValue AssertionValue }
func (assertion *AttributeValueAssertion) AttributeDesc() AttributeDescription {
return assertion.attributeDesc
}
func (assertion *AttributeValueAssertion) AssertionValue() AssertionValue {
return assertion.assertionValue
}
func readAttributeValueAssertion(bytes *Bytes) (ret AttributeValueAssertion, err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readAttributeValueAssertion:\n%s", err.Error())}
return
}
return
}
func readTaggedAttributeValueAssertion(bytes *Bytes, class int, tag int) (ret AttributeValueAssertion, err error) {
err = bytes.ReadSubBytes(class, tag, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedAttributeValueAssertion:\n%s", err.Error())}
return
}
return
}
func (assertion *AttributeValueAssertion) readComponents(bytes *Bytes) (err error) {
assertion.attributeDesc, err = readAttributeDescription(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
assertion.assertionValue, err = readAssertionValue(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (assertion AttributeValueAssertion) write(bytes *Bytes) (size int) {
size += assertion.assertionValue.write(bytes)
size += assertion.attributeDesc.write(bytes)
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
func (assertion AttributeValueAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) {
size += assertion.assertionValue.write(bytes)
size += assertion.attributeDesc.write(bytes)
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
func (assertion AttributeValueAssertion) size() (size int) {
size += assertion.attributeDesc.size()
size += assertion.assertionValue.size()
size += sizeTagAndLength(tagSequence, size)
return
}
func (assertion AttributeValueAssertion) sizeTagged(tag int) (size int) {
size += assertion.attributeDesc.size()
size += assertion.assertionValue.size()
size += sizeTagAndLength(tag, size)
return
}

View file

@ -0,0 +1,37 @@
package message
import "fmt"
//
// AuthenticationChoice ::= CHOICE {
// simple [0] OCTET STRING,
// -- 1 and 2 reserved
// sasl [3] SaslCredentials,
// ... }
func readAuthenticationChoice(bytes *Bytes) (ret AuthenticationChoice, err error) {
tagAndLength, err := bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())}
return
}
err = tagAndLength.ExpectClass(classContextSpecific)
if err != nil {
err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())}
return
}
switch tagAndLength.Tag {
case TagAuthenticationChoiceSimple:
ret, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagAuthenticationChoiceSimple)
case TagAuthenticationChoiceSaslCredentials:
ret, err = readSaslCredentials(bytes)
default:
err = LdapError{fmt.Sprintf("readAuthenticationChoice: invalid tag value %d for AuthenticationChoice", tagAndLength.Tag)}
return
}
if err != nil {
err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())}
return
}
return
}

93
goldap/bind_request.go Normal file
View file

@ -0,0 +1,93 @@
package message
import "fmt"
// BindRequest ::= [APPLICATION 0] SEQUENCE {
// version INTEGER (1 .. 127),
// name LDAPDN,
// authentication AuthenticationChoice }
func (request *BindRequest) Name() LDAPDN {
return request.name
}
func (request *BindRequest) Authentication() AuthenticationChoice {
return request.authentication
}
func (request *BindRequest) AuthenticationSimple() OCTETSTRING {
return request.Authentication().(OCTETSTRING)
}
func (request *BindRequest) AuthenticationChoice() string {
switch request.Authentication().(type) {
case OCTETSTRING:
return "simple"
case SaslCredentials:
return "sasl"
}
return ""
}
func readBindRequest(bytes *Bytes) (bindrequest BindRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagBindRequest, bindrequest.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readBindRequest:\n%s", err.Error())}
return
}
return
}
func (request *BindRequest) readComponents(bytes *Bytes) (err error) {
request.version, err = readINTEGER(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if !(request.version >= BindRequestVersionMin && request.version <= BindRequestVersionMax) {
err = LdapError{fmt.Sprintf("readComponents: invalid version %d, must be between %d and %d", request.version, BindRequestVersionMin, BindRequestVersionMax)}
return
}
request.name, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
request.authentication, err = readAuthenticationChoice(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (request BindRequest) write(bytes *Bytes) (size int) {
switch request.authentication.(type) {
case OCTETSTRING:
size += request.authentication.(OCTETSTRING).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSimple)
case SaslCredentials:
size += request.authentication.(SaslCredentials).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSaslCredentials)
default:
panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication))
}
size += request.name.write(bytes)
size += request.version.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindRequest, size)
return
}
func (request BindRequest) size() (size int) {
size += request.version.size()
size += request.name.size()
switch request.authentication.(type) {
case OCTETSTRING:
size += request.authentication.(OCTETSTRING).sizeTagged(TagAuthenticationChoiceSimple)
case SaslCredentials:
size += request.authentication.(SaslCredentials).sizeTagged(TagAuthenticationChoiceSaslCredentials)
default:
panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication))
}
size += sizeTagAndLength(TagBindRequest, size)
return
}

56
goldap/bind_response.go Normal file
View file

@ -0,0 +1,56 @@
package message
import "fmt"
// BindResponse ::= [APPLICATION 1] SEQUENCE {
// COMPONENTS OF LDAPResult,
// serverSaslCreds [7] OCTET STRING OPTIONAL }
func readBindResponse(bytes *Bytes) (bindresponse BindResponse, err error) {
err = bytes.ReadSubBytes(classApplication, TagBindResponse, bindresponse.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readBindResponse:\n%s", err.Error())}
return
}
return
}
func (response *BindResponse) readComponents(bytes *Bytes) (err error) {
response.LDAPResult.readComponents(bytes)
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagBindResponseServerSaslCreds {
var serverSaslCreds OCTETSTRING
serverSaslCreds, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagBindResponseServerSaslCreds)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
response.serverSaslCreds = serverSaslCreds.Pointer()
}
}
return
}
func (response BindResponse) write(bytes *Bytes) (size int) {
if response.serverSaslCreds != nil {
size += response.serverSaslCreds.writeTagged(bytes, classContextSpecific, TagBindResponseServerSaslCreds)
}
size += response.LDAPResult.writeComponents(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindResponse, size)
return
}
func (response BindResponse) size() (size int) {
if response.serverSaslCreds != nil {
size += response.serverSaslCreds.sizeTagged(TagBindResponseServerSaslCreds)
}
size += response.LDAPResult.sizeComponents()
size += sizeTagAndLength(TagBindResponse, size)
return
}

62
goldap/boolean.go Normal file
View file

@ -0,0 +1,62 @@
package message
import "fmt"
func readBOOLEAN(bytes *Bytes) (ret BOOLEAN, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagBoolean, tagBoolean)
if err != nil {
err = LdapError{fmt.Sprintf("readBOOLEAN:\n%s", err.Error())}
return
}
ret = BOOLEAN(value.(bool))
return
}
func (boolean BOOLEAN) write(bytes *Bytes) int {
return bytes.WritePrimitiveSubBytes(classUniversal, tagBoolean, boolean)
}
func (boolean BOOLEAN) writeTagged(bytes *Bytes, class int, tag int) int {
return bytes.WritePrimitiveSubBytes(class, tag, boolean)
}
func readTaggedBOOLEAN(bytes *Bytes, class int, tag int) (ret BOOLEAN, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagBoolean)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedBOOLEAN:\n%s", err.Error())}
return
}
ret = BOOLEAN(value.(bool))
return
}
func SizePrimitiveSubBytes(tag int, value interface{}) (size int) {
switch value.(type) {
case BOOLEAN:
size = sizeBool(bool(value.(BOOLEAN)))
case INTEGER:
size = sizeInt32(int32(value.(INTEGER)))
case ENUMERATED:
size = sizeInt32(int32(value.(ENUMERATED)))
case OCTETSTRING:
size = sizeOctetString([]byte(string(value.(OCTETSTRING))))
default:
panic(fmt.Sprintf("SizePrimitiveSubBytes: invalid value type %v", value))
}
size += sizeTagAndLength(tag, size)
return
}
func (boolean BOOLEAN) size() int {
return SizePrimitiveSubBytes(tagBoolean, boolean)
}
func (boolean BOOLEAN) sizeTagged(tag int) int {
return SizePrimitiveSubBytes(tag, boolean)
}
func (boolean BOOLEAN) Bool() bool {
return bool(boolean)
}

199
goldap/bytes.go Normal file
View file

@ -0,0 +1,199 @@
package message
import (
"fmt"
)
type Bytes struct {
offset int
bytes []byte
}
func (bytes *Bytes) getBytes() []byte {
return bytes.bytes
}
func NewBytes(offset int, bytes []byte) (ret *Bytes) {
return &Bytes{offset: offset, bytes: bytes}
}
func (bytes Bytes) Debug() {
fmt.Printf("Offset: %d, Bytes: %+v\n", bytes.offset, bytes.bytes)
}
// Return a string with the hex dump of the bytes around the current offset
// The current offset byte is put in brackets
// Example: 0x01, [0x02], 0x03
func (bytes *Bytes) DumpCurrentBytes() (ret string) {
var strings [3]string
for i := -1; i <= 1; i++ {
if bytes.offset+i >= 0 && bytes.offset+i < len(bytes.bytes) {
strings[i+1] = fmt.Sprintf("%#x", bytes.bytes[bytes.offset+i])
}
}
ret = fmt.Sprintf("%s, [%s], %s", strings[0], strings[1], strings[2])
return
}
func (bytes *Bytes) HasMoreData() bool {
return bytes.offset < len(bytes.bytes)
}
func (bytes *Bytes) ParseTagAndLength() (ret TagAndLength, err error) {
var offset int
ret, offset, err = ParseTagAndLength(bytes.bytes, bytes.offset)
if err != nil {
err = LdapError{fmt.Sprintf("ParseTagAndLength: %s", err.Error())}
return
} else {
bytes.offset = offset
}
return
}
func (bytes *Bytes) ReadSubBytes(class int, tag int, callback func(bytes *Bytes) error) (err error) {
// Check tag
tagAndLength, err := bytes.ParseTagAndLength()
if err != nil {
return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())}
}
err = tagAndLength.Expect(class, tag, isCompound)
if err != nil {
return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())}
}
start := bytes.offset
end := bytes.offset + tagAndLength.Length
// Check we got enough bytes to process
if end > len(bytes.bytes) {
return LdapError{fmt.Sprintf("ReadSubBytes: data truncated: expecting %d bytes at offset %d", tagAndLength.Length, bytes.offset)}
}
// Process sub-bytes
subBytes := Bytes{offset: 0, bytes: bytes.bytes[start:end]}
err = callback(&subBytes)
if err != nil {
bytes.offset += subBytes.offset
err = LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())}
return
}
// Check we got no more bytes to process
if subBytes.HasMoreData() {
return LdapError{fmt.Sprintf("ReadSubBytes: data too long: %d more bytes to read at offset %d", end-bytes.offset, bytes.offset)}
}
// Move offset
bytes.offset = end
return
}
func SizeSubBytes(tag int, callback func() int) (size int) {
size = callback()
size += sizeTagAndLength(tag, size)
return
}
func (bytes *Bytes) WritePrimitiveSubBytes(class int, tag int, value interface{}) (size int) {
switch value.(type) {
case BOOLEAN:
size = writeBool(bytes, bool(value.(BOOLEAN)))
case INTEGER:
size = writeInt32(bytes, int32(value.(INTEGER)))
case ENUMERATED:
size = writeInt32(bytes, int32(value.(ENUMERATED)))
case OCTETSTRING:
size = writeOctetString(bytes, []byte(string(value.(OCTETSTRING))))
default:
panic(fmt.Sprintf("WritePrimitiveSubBytes: invalid value type %v", value))
}
size += bytes.WriteTagAndLength(class, isNotCompound, tag, size)
return
}
func (bytes *Bytes) WriteTagAndLength(class int, compound bool, tag int, length int) int {
return writeTagAndLength(bytes, TagAndLength{Class: class, IsCompound: compound, Tag: tag, Length: length})
}
func (bytes *Bytes) writeString(s string) (size int) {
size = len(s)
start := bytes.offset - size
if start < 0 {
panic("Not enough space for string")
}
copy(bytes.bytes[start:], s)
bytes.offset = start
return
}
func (bytes *Bytes) writeBytes(b []byte) (size int) {
size = len(b)
start := bytes.offset - size
if start < 0 {
panic("Not enough space for bytes")
}
copy(bytes.bytes[start:], b)
bytes.offset = start
return
}
//
// Parse tag, length and read the a primitive value
// Supported types are:
// - boolean
// - integer (parsed as int32)
// - enumerated (parsed as int32)
// - UTF8 string
// - Octet string
//
// Parameters:
// - class: the expected class value(classUniversal, classApplication, classContextSpecific)
// - tag: the expected tag value
// - typeTag: the real primitive type to parse (tagBoolean, tagInteger, tagEnym, tagUTF8String, tagOctetString)
//
func (bytes *Bytes) ReadPrimitiveSubBytes(class int, tag int, typeTag int) (value interface{}, err error) {
// Check tag
tagAndLength, err := bytes.ParseTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())}
return
}
err = tagAndLength.Expect(class, tag, isNotCompound)
if err != nil {
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())}
return
}
start := bytes.offset
end := bytes.offset + tagAndLength.Length
// Check we got enough bytes to process
if end > len(bytes.bytes) {
// err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining (start: %d, length: %d, end: %d, len(b): %d, bytes: %#+v)", tagAndLength.Length, *b.offset, len(b.bytes)-start, start, tagAndLength.Length, end, len(b.bytes), b.bytes)}
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining", tagAndLength.Length, bytes.offset, len(bytes.bytes)-start)}
return
}
// Process sub-bytes
subBytes := bytes.bytes[start:end]
switch typeTag {
case tagBoolean:
value, err = parseBool(subBytes)
case tagInteger:
value, err = parseInt32(subBytes)
case tagEnum:
value, err = parseInt32(subBytes)
case tagOctetString:
value, err = parseOctetString(subBytes)
default:
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: invalid type tag value %d", typeTag)}
return
}
if err != nil {
err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())}
return
}
// Move offset
bytes.offset = end
return
}
func (bytes *Bytes) Bytes() []byte {
return bytes.bytes
}

172
goldap/bytes_test.go Normal file
View file

@ -0,0 +1,172 @@
package message
import (
"reflect"
"testing"
)
func TestReadPrimitiveSubBytesTestData(t *testing.T) {
for i, test := range PrimitiveSubBytesTestData() {
value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag)
if err != nil {
t.Errorf("#%d failed: %s", i+1, err)
} else if !reflect.DeepEqual(test.value, value) {
t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value)
} else if test.offset != test.bytes.offset {
t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset)
}
}
}
func TestSizePrimitiveSubBytesTestData(t *testing.T) {
for i, test := range PrimitiveSubBytesTestData() {
value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag)
if err != nil {
t.Errorf("#%d failed: %s", i+1, err)
} else if !reflect.DeepEqual(test.value, value) {
t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value)
} else if test.offset != test.bytes.offset {
t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset)
}
}
}
func NewInt(value int) (ret *int) {
ret = &value
return
}
type PrimitiveSubBytesTestSingleData struct {
bytes Bytes // Input
class int // Expected class
tag int // Expected tag
typeTag int // Expected type
value interface{} // Expected output
offset int // Expected offset after processing
}
func PrimitiveSubBytesTestData() []PrimitiveSubBytesTestSingleData {
return []PrimitiveSubBytesTestSingleData{
// Test 1
{
bytes: Bytes{
offset: 0,
bytes: []byte{0x02, 0x01, 0x09},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x09),
offset: 3,
},
// Test 2
{
bytes: Bytes{
offset: 0,
bytes: []byte{0x02, 0x02, 0x09, 0x87},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x0987),
offset: 4,
},
// Test 3
{
bytes: Bytes{
offset: 0,
bytes: []byte{0x02, 0x03, 0x09, 0x87, 0x65},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x098765),
offset: 5,
},
// Test 4
{
bytes: Bytes{
offset: 0,
bytes: []byte{0x02, 0x04, 0x09, 0x87, 0x65, 0x43},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x09876543),
offset: 6,
},
// Test 5
{
bytes: Bytes{
offset: 2,
bytes: []byte{0x30, 0x03, 0x02, 0x01, 0x0f},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x0f),
offset: 5,
},
// Test 6
{
bytes: Bytes{
offset: 2,
bytes: []byte{0x30, 0x16, 0x02, 0x01, 0x0f, 0x60, 0x11, 0x02, 0x01, 0x03, 0x04, 0x00, 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x0f),
offset: 5,
},
// Test 7
{
bytes: Bytes{
offset: 2,
bytes: []byte{0x30, 0x19, 0x02, 0x04, 0x7f, 0xff, 0xff, 0xff, 0x60, 0x11, 0x02, 0x01, 0x03, 0x04, 0x00, 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35},
},
class: classUniversal,
tag: tagInteger,
typeTag: tagInteger,
value: int32(0x07fffffff),
offset: 8,
},
// Test 8
{
bytes: Bytes{
offset: 0,
bytes: []byte{0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35},
},
class: classUniversal,
tag: tagOctetString,
typeTag: tagOctetString,
value: []byte("CRAM-MD5"),
offset: 10,
},
// Test 9
{
bytes: Bytes{
offset: 0,
bytes: []byte{0x04, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c},
},
class: classUniversal,
tag: tagOctetString,
typeTag: tagOctetString,
value: []byte("Hello, 世界"),
offset: 15,
},
// Test 10
{
bytes: Bytes{
offset: 10,
bytes: []byte{0x30, 0x1d, 0x02, 0x01, 0x05, 0x60, 0x18, 0x02, 0x01, 0x03, 0x04, 0x07, 0x6d, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x80, 0x0a, 0x6d, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64},
},
class: classUniversal,
tag: tagOctetString,
typeTag: tagOctetString,
value: []byte("myLogin"),
offset: 19,
},
}
}

53
goldap/compare_request.go Normal file
View file

@ -0,0 +1,53 @@
package message
import "fmt"
//
// CompareRequest ::= [APPLICATION 14] SEQUENCE {
// entry LDAPDN,
// ava AttributeValueAssertion }
func (request *CompareRequest) Entry() LDAPDN {
return request.entry
}
func (request *CompareRequest) Ava() *AttributeValueAssertion {
return &request.ava
}
func readCompareRequest(bytes *Bytes) (ret CompareRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagCompareRequest, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readCompareRequest:\n%s", err.Error())}
return
}
return
}
func (request *CompareRequest) readComponents(bytes *Bytes) (err error) {
request.entry, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
request.ava, err = readAttributeValueAssertion(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (request CompareRequest) write(bytes *Bytes) (size int) {
size += request.ava.write(bytes)
size += request.entry.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagCompareRequest, size)
return
}
func (request CompareRequest) size() (size int) {
size += request.entry.size()
size += request.ava.size()
size += sizeTagAndLength(TagCompareRequest, size)
return
}

View file

@ -0,0 +1,29 @@
package message
import "fmt"
//
// CompareResponse ::= [APPLICATION 15] LDAPResult
func (response *CompareResponse) SetResultCode(code int) {
response.resultCode = ENUMERATED(code)
}
func readCompareResponse(bytes *Bytes) (ret CompareResponse, err error) {
var res LDAPResult
res, err = readTaggedLDAPResult(bytes, classApplication, TagCompareResponse)
if err != nil {
err = LdapError{fmt.Sprintf("readCompareResponse:\n%s", err.Error())}
return
}
ret = CompareResponse(res)
return
}
func (response CompareResponse) write(bytes *Bytes) int {
return LDAPResult(response).writeTagged(bytes, classApplication, TagCompareResponse)
}
func (response CompareResponse) size() int {
return LDAPResult(response).sizeTagged(TagCompareResponse)
}

94
goldap/control.go Normal file
View file

@ -0,0 +1,94 @@
package message
import (
"errors"
"fmt"
)
//
// Control ::= SEQUENCE {
// controlType LDAPOID,
// criticality BOOLEAN DEFAULT FALSE,
// controlValue OCTET STRING OPTIONAL }
func (control *Control) ControlType() LDAPOID {
return control.controlType
}
func (control *Control) Criticality() BOOLEAN {
return control.criticality
}
func (control *Control) ControlValue() *OCTETSTRING {
return control.controlValue
}
func readControl(bytes *Bytes) (control Control, err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, control.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readControl:\n%s", err.Error())}
return
}
return
}
func (control *Control) readComponents(bytes *Bytes) (err error) {
control.controlType, err = readLDAPOID(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == tagBoolean {
control.criticality, err = readBOOLEAN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if control.criticality == false {
err = errors.New(fmt.Sprintf("readComponents: criticality default value FALSE should not be specified"))
return
}
}
}
if bytes.HasMoreData() {
var octetstring OCTETSTRING
octetstring, err = readOCTETSTRING(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
control.controlValue = octetstring.Pointer()
}
return
}
func (control Control) write(bytes *Bytes) (size int) {
if control.controlValue != nil {
size += control.controlValue.write(bytes)
}
if control.criticality != BOOLEAN(false) {
size += control.criticality.write(bytes)
}
size += control.controlType.write(bytes)
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
func (control Control) size() (size int) {
if control.controlValue != nil {
size += control.controlValue.size()
}
if control.criticality != BOOLEAN(false) {
size += control.criticality.size()
}
size += control.controlType.size()
size += sizeTagAndLength(tagSequence, size)
return
}

44
goldap/controls.go Normal file
View file

@ -0,0 +1,44 @@
package message
import "fmt"
//
// Controls ::= SEQUENCE OF control Control
func readTaggedControls(bytes *Bytes, class int, tag int) (controls Controls, err error) {
err = bytes.ReadSubBytes(class, tag, controls.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedControls:\n%s", err.Error())}
return
}
return
}
func (controls *Controls) readComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var control Control
control, err = readControl(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
*controls = append(*controls, control)
}
return
}
func (controls Controls) Pointer() *Controls { return &controls }
func (controls Controls) writeTagged(bytes *Bytes, class int, tag int) (size int) {
for i := len(controls) - 1; i >= 0; i-- {
size += controls[i].write(bytes)
}
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
func (controls Controls) sizeTagged(tag int) (size int) {
for _, control := range controls {
size += control.size()
}
size += sizeTagAndLength(tag, size)
return
}

24
goldap/del_request.go Normal file
View file

@ -0,0 +1,24 @@
package message
import "fmt"
//
// DelRequest ::= [APPLICATION 10] LDAPDN
func readDelRequest(bytes *Bytes) (ret DelRequest, err error) {
var res LDAPDN
res, err = readTaggedLDAPDN(bytes, classApplication, TagDelRequest)
if err != nil {
err = LdapError{fmt.Sprintf("readDelRequest:\n%s", err.Error())}
return
}
ret = DelRequest(res)
return
}
func (del DelRequest) write(bytes *Bytes) int {
return LDAPDN(del).writeTagged(bytes, classApplication, TagDelRequest)
}
func (del DelRequest) size() int {
return LDAPDN(del).sizeTagged(TagDelRequest)
}

29
goldap/del_response.go Normal file
View file

@ -0,0 +1,29 @@
package message
import "fmt"
//
// DelResponse ::= [APPLICATION 11] LDAPResult
func (del *DelResponse) SetResultCode(code int) {
del.resultCode = ENUMERATED(code)
}
func readDelResponse(bytes *Bytes) (ret DelResponse, err error) {
var res LDAPResult
res, err = readTaggedLDAPResult(bytes, classApplication, TagDelResponse)
if err != nil {
err = LdapError{fmt.Sprintf("readDelResponse:\n%s", err.Error())}
return
}
ret = DelResponse(res)
return
}
func (del DelResponse) write(bytes *Bytes) int {
return LDAPResult(del).writeTagged(bytes, classApplication, TagDelResponse)
}
func (del DelResponse) size() int {
return LDAPResult(del).sizeTagged(TagDelResponse)
}

60
goldap/dn.go Normal file
View file

@ -0,0 +1,60 @@
package message
import "fmt"
//
// LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
// -- [RFC4514]
func readLDAPDN(bytes *Bytes) (ret LDAPDN, err error) {
var str LDAPString
str, err = readLDAPString(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readLDAPDN:\n%s", err.Error())}
return
}
ret = LDAPDN(str)
return
}
func readTaggedLDAPDN(bytes *Bytes, class int, tag int) (ret LDAPDN, err error) {
var ldapstring LDAPString
ldapstring, err = readTaggedLDAPString(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedLDAPDN:\n%s", err.Error())}
return
}
// @TODO: check RFC4514
ret = LDAPDN(ldapstring)
return
}
func (l LDAPDN) Pointer() *LDAPDN { return &l }
func readRelativeLDAPDN(bytes *Bytes) (ret RelativeLDAPDN, err error) {
var ldapstring LDAPString
ldapstring, err = readLDAPString(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readRelativeLDAPDN:\n%s", err.Error())}
return
}
// @TODO: check RFC4514
ret = RelativeLDAPDN(ldapstring)
return
}
func (l LDAPDN) write(bytes *Bytes) int {
return LDAPString(l).write(bytes)
}
func (l LDAPDN) writeTagged(bytes *Bytes, class int, tag int) int {
return LDAPString(l).writeTagged(bytes, class, tag)
}
func (l LDAPDN) size() int {
return LDAPString(l).size()
}
func (l LDAPDN) sizeTagged(tag int) int {
return LDAPString(l).sizeTagged(tag)
}

34
goldap/enumerated.go Normal file
View file

@ -0,0 +1,34 @@
package message
import "fmt"
func (enum ENUMERATED) Int() int {
return int(enum)
}
func readENUMERATED(bytes *Bytes, allowedValues map[ENUMERATED]string) (ret ENUMERATED, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagEnum, tagEnum)
if err != nil {
err = LdapError{fmt.Sprintf("readENUMERATED:\n%s", err.Error())}
return
}
ret = ENUMERATED(value.(int32))
if _, ok := allowedValues[ret]; !ok {
err = LdapError{fmt.Sprintf("readENUMERATED: Invalid ENUMERATED VALUE %d", ret)}
return
}
return
}
func (enum ENUMERATED) write(bytes *Bytes) int {
return bytes.WritePrimitiveSubBytes(classUniversal, tagEnum, enum)
}
func (enum ENUMERATED) writeTagged(bytes *Bytes, class int, tag int) int {
return bytes.WritePrimitiveSubBytes(class, tag, enum)
}
func (enum ENUMERATED) size() int {
return SizePrimitiveSubBytes(tagEnum, enum)
}

9
goldap/error.go Normal file
View file

@ -0,0 +1,9 @@
package message
type LdapError struct {
Msg string
}
func (err LdapError) Error() string {
return err.Msg
}

View file

@ -0,0 +1,69 @@
package message
import "fmt"
//
// ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
// requestName [0] LDAPOID,
// requestValue [1] OCTET STRING OPTIONAL }
func (extended *ExtendedRequest) RequestName() LDAPOID {
return extended.requestName
}
func (extended *ExtendedRequest) RequestValue() *OCTETSTRING {
return extended.requestValue
}
func readExtendedRequest(bytes *Bytes) (ret ExtendedRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagExtendedRequest, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readExtendedRequest:\n%s", err.Error())}
return
}
return
}
func (extended *ExtendedRequest) readComponents(bytes *Bytes) (err error) {
extended.requestName, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedRequestName)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagExtendedRequestValue {
var requestValue OCTETSTRING
requestValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedRequestValue)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
extended.requestValue = requestValue.Pointer()
}
}
return
}
func (extended ExtendedRequest) write(bytes *Bytes) (size int) {
if extended.requestValue != nil {
size += extended.requestValue.writeTagged(bytes, classContextSpecific, TagExtendedRequestValue)
}
size += extended.requestName.writeTagged(bytes, classContextSpecific, TagExtendedRequestName)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedRequest, size)
return
}
func (extended ExtendedRequest) size() (size int) {
size += extended.requestName.sizeTagged(TagExtendedRequestName)
if extended.requestValue != nil {
size += extended.requestValue.sizeTagged(TagExtendedRequestValue)
}
size += sizeTagAndLength(TagExtendedRequest, size)
return
}

View file

@ -0,0 +1,85 @@
package message
import "fmt"
//
// ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
// COMPONENTS OF LDAPResult,
// responseName [10] LDAPOID OPTIONAL,
// responseValue [11] OCTET STRING OPTIONAL }
func (extended *ExtendedResponse) SetResponseName(name LDAPOID) {
extended.responseName = &name
}
func readExtendedResponse(bytes *Bytes) (ret ExtendedResponse, err error) {
err = bytes.ReadSubBytes(classApplication, TagExtendedResponse, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readExtendedResponse:\n%s", err.Error())}
return
}
return
}
func (extended *ExtendedResponse) readComponents(bytes *Bytes) (err error) {
extended.LDAPResult.readComponents(bytes)
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagExtendedResponseName {
var oid LDAPOID
oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedResponseName)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
extended.responseName = oid.Pointer()
}
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagExtendedResponseValue {
var responseValue OCTETSTRING
responseValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedResponseValue)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
extended.responseValue = responseValue.Pointer()
}
}
return
}
func (extended ExtendedResponse) write(bytes *Bytes) (size int) {
if extended.responseValue != nil {
size += extended.responseValue.writeTagged(bytes, classContextSpecific, TagExtendedResponseValue)
}
if extended.responseName != nil {
size += extended.responseName.writeTagged(bytes, classContextSpecific, TagExtendedResponseName)
}
size += extended.LDAPResult.writeComponents(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedResponse, size)
return
}
func (extended ExtendedResponse) size() (size int) {
size += extended.LDAPResult.sizeComponents()
if extended.responseName != nil {
size += extended.responseName.sizeTagged(TagExtendedResponseName)
}
if extended.responseValue != nil {
size += extended.responseValue.sizeTagged(TagExtendedResponseValue)
}
size += sizeTagAndLength(TagExtendedResponse, size)
return
}

70
goldap/filter.go Normal file
View file

@ -0,0 +1,70 @@
package message
import "fmt"
//
// 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,
//
//
//
//Sermersheim Standards Track [Page 57]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// substrings [4] SubstringFilter,
// greaterOrEqual [5] AttributeValueAssertion,
// lessOrEqual [6] AttributeValueAssertion,
// present [7] AttributeDescription,
// approxMatch [8] AttributeValueAssertion,
// extensibleMatch [9] MatchingRuleAssertion,
// ... }
func readFilter(bytes *Bytes) (filter Filter, err error) {
var tagAndLength TagAndLength
tagAndLength, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())}
return
}
err = tagAndLength.ExpectClass(classContextSpecific)
if err != nil {
err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())}
return
}
switch tagAndLength.Tag {
case TagFilterAnd:
filter, err = readFilterAnd(bytes)
case TagFilterOr:
filter, err = readFilterOr(bytes)
case TagFilterNot:
filter, err = readFilterNot(bytes)
case TagFilterEqualityMatch:
filter, err = readFilterEqualityMatch(bytes)
case TagFilterSubstrings:
filter, err = readFilterSubstrings(bytes)
case TagFilterGreaterOrEqual:
filter, err = readFilterGreaterOrEqual(bytes)
case TagFilterLessOrEqual:
filter, err = readFilterLessOrEqual(bytes)
case TagFilterPresent:
filter, err = readFilterPresent(bytes)
case TagFilterApproxMatch:
filter, err = readFilterApproxMatch(bytes)
case TagFilterExtensibleMatch:
filter, err = readFilterExtensibleMatch(bytes)
default:
err = LdapError{fmt.Sprintf("readFilter: invalid tag value %d for filter", tagAndLength.Tag)}
return
}
if err != nil {
err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())}
return
}
return
}

54
goldap/filter_and.go Normal file
View file

@ -0,0 +1,54 @@
package message
import "fmt"
// and [0] SET SIZE (1..MAX) OF filter Filter,
func (filterAnd FilterAnd) getFilterTag() int {
return TagFilterAnd
}
func (filterAnd FilterAnd) size() (size int) {
for _, filter := range filterAnd {
size += filter.size()
}
size += sizeTagAndLength(TagFilterAnd, size)
return
}
func (filterAnd *FilterAnd) readComponents(bytes *Bytes) (err error) {
count := 0
for bytes.HasMoreData() {
count++
var filter Filter
filter, err = readFilter(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents (filter %d):\n%s", count, err.Error())}
return
}
*filterAnd = append(*filterAnd, filter)
}
if len(*filterAnd) == 0 {
err = LdapError{"readComponents: expecting at least one Filter"}
return
}
return
}
func (filterAnd FilterAnd) write(bytes *Bytes) (size int) {
for i := len(filterAnd) - 1; i >= 0; i-- {
size += filterAnd[i].write(bytes)
}
size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterAnd, size)
return
}
func readFilterAnd(bytes *Bytes) (filterand FilterAnd, err error) {
err = bytes.ReadSubBytes(classContextSpecific, TagFilterAnd, filterand.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterAnd:\n%s", err.Error())}
return
}
return
}

View file

@ -0,0 +1,34 @@
package message
import "fmt"
// approxMatch [8] AttributeValueAssertion,
func readFilterApproxMatch(bytes *Bytes) (ret FilterApproxMatch, err error) {
var attributevalueassertion AttributeValueAssertion
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterApproxMatch)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterApproxMatch:\n%s", err.Error())}
return
}
ret = FilterApproxMatch(attributevalueassertion)
return
}
// approxMatch [8] AttributeValueAssertion,
func (f FilterApproxMatch) write(bytes *Bytes) int {
return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterApproxMatch)
}
func (filterAnd FilterApproxMatch) getFilterTag() int {
return TagFilterApproxMatch
}
// approxMatch [8] AttributeValueAssertion,
func (f FilterApproxMatch) size() int {
return AttributeValueAssertion(f).sizeTagged(TagFilterApproxMatch)
}
func (a *FilterApproxMatch) AttributeDesc() AttributeDescription {
return a.attributeDesc
}
func (a *FilterApproxMatch) AssertionValue() AssertionValue {
return a.assertionValue
}

View file

@ -0,0 +1,34 @@
package message
import "fmt"
// equalityMatch [3] AttributeValueAssertion,
func readFilterEqualityMatch(bytes *Bytes) (ret FilterEqualityMatch, err error) {
var attributevalueassertion AttributeValueAssertion
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterEqualityMatch)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterEqualityMatch:\n%s", err.Error())}
return
}
ret = FilterEqualityMatch(attributevalueassertion)
return
}
// equalityMatch [3] AttributeValueAssertion,
func (f FilterEqualityMatch) write(bytes *Bytes) int {
return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterEqualityMatch)
}
func (filter FilterEqualityMatch) getFilterTag() int {
return TagFilterEqualityMatch
}
// equalityMatch [3] AttributeValueAssertion,
func (f FilterEqualityMatch) size() int {
return AttributeValueAssertion(f).sizeTagged(TagFilterEqualityMatch)
}
func (a *FilterEqualityMatch) AttributeDesc() AttributeDescription {
return a.attributeDesc
}
func (a *FilterEqualityMatch) AssertionValue() AssertionValue {
return a.assertionValue
}

View file

@ -0,0 +1,28 @@
package message
import "fmt"
// extensibleMatch [9] MatchingRuleAssertion,
func readFilterExtensibleMatch(bytes *Bytes) (filterextensiblematch FilterExtensibleMatch, err error) {
var matchingruleassertion MatchingRuleAssertion
matchingruleassertion, err = readTaggedMatchingRuleAssertion(bytes, classContextSpecific, TagFilterExtensibleMatch)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterExtensibleMatch:\n%s", err.Error())}
return
}
filterextensiblematch = FilterExtensibleMatch(matchingruleassertion)
return
}
// extensibleMatch [9] MatchingRuleAssertion,
func (f FilterExtensibleMatch) write(bytes *Bytes) int {
return MatchingRuleAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterExtensibleMatch)
}
func (filterAnd FilterExtensibleMatch) getFilterTag() int {
return TagFilterExtensibleMatch
}
// extensibleMatch [9] MatchingRuleAssertion,
func (f FilterExtensibleMatch) size() int {
return MatchingRuleAssertion(f).sizeTagged(TagFilterExtensibleMatch)
}

View file

@ -0,0 +1,34 @@
package message
import "fmt"
// greaterOrEqual [5] AttributeValueAssertion,
func readFilterGreaterOrEqual(bytes *Bytes) (ret FilterGreaterOrEqual, err error) {
var attributevalueassertion AttributeValueAssertion
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterGreaterOrEqual)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterGreaterOrEqual:\n%s", err.Error())}
return
}
ret = FilterGreaterOrEqual(attributevalueassertion)
return
}
// greaterOrEqual [5] AttributeValueAssertion,
func (filter FilterGreaterOrEqual) write(bytes *Bytes) int {
return AttributeValueAssertion(filter).writeTagged(bytes, classContextSpecific, TagFilterGreaterOrEqual)
}
func (filter FilterGreaterOrEqual) getFilterTag() int {
return TagFilterGreaterOrEqual
}
// greaterOrEqual [5] AttributeValueAssertion,
func (filter FilterGreaterOrEqual) size() int {
return AttributeValueAssertion(filter).sizeTagged(TagFilterGreaterOrEqual)
}
func (filter *FilterGreaterOrEqual) AttributeDesc() AttributeDescription {
return filter.attributeDesc
}
func (filter *FilterGreaterOrEqual) AssertionValue() AssertionValue {
return filter.assertionValue
}

View file

@ -0,0 +1,34 @@
package message
import "fmt"
// lessOrEqual [6] AttributeValueAssertion,
func readFilterLessOrEqual(bytes *Bytes) (ret FilterLessOrEqual, err error) {
var attributevalueassertion AttributeValueAssertion
attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterLessOrEqual)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterLessOrEqual:\n%s", err.Error())}
return
}
ret = FilterLessOrEqual(attributevalueassertion)
return
}
// lessOrEqual [6] AttributeValueAssertion,
func (f FilterLessOrEqual) write(bytes *Bytes) int {
return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterLessOrEqual)
}
func (filterAnd FilterLessOrEqual) getFilterTag() int {
return TagFilterLessOrEqual
}
// lessOrEqual [6] AttributeValueAssertion,
func (f FilterLessOrEqual) size() int {
return AttributeValueAssertion(f).sizeTagged(TagFilterLessOrEqual)
}
func (a *FilterLessOrEqual) AttributeDesc() AttributeDescription {
return a.attributeDesc
}
func (a *FilterLessOrEqual) AssertionValue() AssertionValue {
return a.assertionValue
}

40
goldap/filter_not.go Normal file
View file

@ -0,0 +1,40 @@
package message
import "fmt"
func (filterNot FilterNot) getFilterTag() int {
return TagFilterNot
}
// not [2] Filter,
func (filterNot FilterNot) size() (size int) {
size = filterNot.Filter.size()
size += sizeTagAndLength(tagSequence, size)
return
}
func (filterNot *FilterNot) readComponents(bytes *Bytes) (err error) {
filterNot.Filter, err = readFilter(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
// not [2] Filter,
func (filterNot FilterNot) write(bytes *Bytes) (size int) {
size = filterNot.Filter.write(bytes)
size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterNot, size)
return
}
// not [2] Filter,
func readFilterNot(bytes *Bytes) (filternot FilterNot, err error) {
err = bytes.ReadSubBytes(classContextSpecific, TagFilterNot, filternot.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterNot:\n%s", err.Error())}
return
}
return
}

52
goldap/filter_or.go Normal file
View file

@ -0,0 +1,52 @@
package message
import "fmt"
// or [1] SET SIZE (1..MAX) OF filter Filter,
func readFilterOr(bytes *Bytes) (filteror FilterOr, err error) {
err = bytes.ReadSubBytes(classContextSpecific, TagFilterOr, filteror.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterOr:\n%s", err.Error())}
return
}
return
}
func (filteror *FilterOr) readComponents(bytes *Bytes) (err error) {
count := 0
for bytes.HasMoreData() {
count++
var filter Filter
filter, err = readFilter(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents (filter %d): %s", count, err.Error())}
return
}
*filteror = append(*filteror, filter)
}
if len(*filteror) == 0 {
err = LdapError{"readComponents: expecting at least one Filter"}
return
}
return
}
// or [1] SET SIZE (1..MAX) OF filter Filter,
func (f FilterOr) write(bytes *Bytes) (size int) {
for i := len(f) - 1; i >= 0; i-- {
size += f[i].write(bytes)
}
size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterOr, size)
return
}
func (filter FilterOr) getFilterTag() int {
return TagFilterOr
}
// or [1] SET SIZE (1..MAX) OF filter Filter,
func (f FilterOr) size() (size int) {
for _, filter := range f {
size += filter.size()
}
size += sizeTagAndLength(TagFilterOr, size)
return
}

28
goldap/filter_present.go Normal file
View file

@ -0,0 +1,28 @@
package message
import "fmt"
// present [7] AttributeDescription,
func readFilterPresent(bytes *Bytes) (ret FilterPresent, err error) {
var attributedescription AttributeDescription
attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagFilterPresent)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterPresent:\n%s", err.Error())}
return
}
ret = FilterPresent(attributedescription)
return
}
// present [7] AttributeDescription,
func (f FilterPresent) write(bytes *Bytes) int {
return AttributeDescription(f).writeTagged(bytes, classContextSpecific, TagFilterPresent)
}
func (filterAnd FilterPresent) getFilterTag() int {
return TagFilterPresent
}
// present [7] AttributeDescription,
func (f FilterPresent) size() int {
return AttributeDescription(f).sizeTagged(TagFilterPresent)
}

187
goldap/filter_substring.go Normal file
View file

@ -0,0 +1,187 @@
package message
import "fmt"
// substrings [4] SubstringFilter,
func readFilterSubstrings(bytes *Bytes) (filtersubstrings FilterSubstrings, err error) {
var substringfilter SubstringFilter
substringfilter, err = readTaggedSubstringFilter(bytes, classContextSpecific, TagFilterSubstrings)
if err != nil {
err = LdapError{fmt.Sprintf("readFilterSubstrings:\n%s", err.Error())}
return
}
filtersubstrings = FilterSubstrings(substringfilter)
return
}
//
// 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
// }
func readTaggedSubstringFilter(bytes *Bytes, class int, tag int) (substringfilter SubstringFilter, err error) {
err = bytes.ReadSubBytes(class, tag, substringfilter.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedSubstringFilter:\n%s", err.Error())}
return
}
return
}
func (substringfilter *SubstringFilter) readComponents(bytes *Bytes) (err error) {
substringfilter.type_, err = readAttributeDescription(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
err = substringfilter.readSubstrings(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (substringfilter *SubstringFilter) readSubstrings(bytes *Bytes) (err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, substringfilter.readSubstringsComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readSubstrings:\n%s", err.Error())}
return
}
return
}
func (substringfilter *SubstringFilter) readSubstringsComponents(bytes *Bytes) (err error) {
var foundInitial = 0
var foundFinal = 0
var tagAndLength TagAndLength
for bytes.HasMoreData() {
tagAndLength, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
return
}
var assertionvalue AssertionValue
switch tagAndLength.Tag {
case TagSubstringInitial:
foundInitial++
if foundInitial > 1 {
err = LdapError{"readSubstringsComponents: initial can occur at most once"}
return
}
assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringInitial)
if err != nil {
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
return
}
substringfilter.substrings = append(substringfilter.substrings, SubstringInitial(assertionvalue))
case TagSubstringAny:
assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringAny)
if err != nil {
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
return
}
substringfilter.substrings = append(substringfilter.substrings, SubstringAny(assertionvalue))
case TagSubstringFinal:
foundFinal++
if foundFinal > 1 {
err = LdapError{"readSubstringsComponents: final can occur at most once"}
return
}
assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringFinal)
if err != nil {
err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())}
return
}
substringfilter.substrings = append(substringfilter.substrings, SubstringFinal(assertionvalue))
default:
err = LdapError{fmt.Sprintf("readSubstringsComponents: invalid tag %d", tagAndLength.Tag)}
return
}
}
if len(substringfilter.substrings) == 0 {
err = LdapError{"readSubstringsComponents: expecting at least one substring"}
return
}
return
}
// substrings [4] SubstringFilter,
func (f FilterSubstrings) write(bytes *Bytes) int {
return SubstringFilter(f).writeTagged(bytes, classContextSpecific, TagFilterSubstrings)
}
func (s SubstringFilter) writeTagged(bytes *Bytes, class int, tag int) (size int) {
for i := len(s.substrings) - 1; i >= 0; i-- {
substring := s.substrings[i]
switch substring.(type) {
case SubstringInitial:
size += AssertionValue(substring.(SubstringInitial)).writeTagged(bytes, classContextSpecific, TagSubstringInitial)
case SubstringAny:
size += AssertionValue(substring.(SubstringAny)).writeTagged(bytes, classContextSpecific, TagSubstringAny)
case SubstringFinal:
size += AssertionValue(substring.(SubstringFinal)).writeTagged(bytes, classContextSpecific, TagSubstringFinal)
default:
panic("Unknown type for SubstringFilter substring")
}
}
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
size += s.type_.write(bytes)
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
//
// 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
// }
func (s SubstringFilter) write(bytes *Bytes) (size int) {
return s.writeTagged(bytes, classUniversal, tagSequence)
}
func (filter FilterSubstrings) getFilterTag() int {
return TagFilterSubstrings
}
// substrings [4] SubstringFilter,
func (f FilterSubstrings) size() int {
return SubstringFilter(f).sizeTagged(TagFilterSubstrings)
}
//
// 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
// }
func (s SubstringFilter) size() (size int) {
return s.sizeTagged(tagSequence)
}
func (s SubstringFilter) sizeTagged(tag int) (size int) {
for _, substring := range s.substrings {
switch substring.(type) {
case SubstringInitial:
size += AssertionValue(substring.(SubstringInitial)).sizeTagged(TagSubstringInitial)
case SubstringAny:
size += AssertionValue(substring.(SubstringAny)).sizeTagged(TagSubstringAny)
case SubstringFinal:
size += AssertionValue(substring.(SubstringFinal)).sizeTagged(TagSubstringFinal)
default:
panic("Unknown type for SubstringFilter substring")
}
}
size += sizeTagAndLength(tagSequence, size)
size += s.type_.size()
size += sizeTagAndLength(tag, size)
return
}
func (s *FilterSubstrings) Type_() AttributeDescription {
return s.type_
}
func (s *FilterSubstrings) Substrings() []Substring {
return s.substrings
}

53
goldap/integer.go Normal file
View file

@ -0,0 +1,53 @@
package message
import "fmt"
func readINTEGER(bytes *Bytes) (ret INTEGER, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagInteger, tagInteger)
if err != nil {
err = LdapError{fmt.Sprintf("readINTEGER:\n%s", err.Error())}
return
}
ret = INTEGER(value.(int32))
return
}
func readTaggedINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagInteger)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedINTEGER:\n%s", err.Error())}
return
}
ret = INTEGER(value.(int32))
return
}
func readTaggedPositiveINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) {
ret, err = readTaggedINTEGER(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER:\n%s", err.Error())}
return
}
if !(ret >= 0 && ret <= maxInt) {
err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER: Invalid INTEGER value %d ! Expected value between 0 and %d", ret, maxInt)}
}
return
}
func readPositiveINTEGER(bytes *Bytes) (ret INTEGER, err error) {
return readTaggedPositiveINTEGER(bytes, classUniversal, tagInteger)
}
func (i INTEGER) write(bytes *Bytes) int {
return bytes.WritePrimitiveSubBytes(classUniversal, tagInteger, i)
}
func (i INTEGER) writeTagged(bytes *Bytes, class int, tag int) int {
return bytes.WritePrimitiveSubBytes(class, tag, i)
}
func (i INTEGER) size() int {
return SizePrimitiveSubBytes(tagInteger, i)
}
func (i INTEGER) sizeTagged(tag int) int {
return SizePrimitiveSubBytes(tag, i)
}
func (l INTEGER) Int() int {
return int(l)
}

View file

@ -0,0 +1,89 @@
package message
import "fmt"
//
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
// responseName [0] LDAPOID OPTIONAL,
// responseValue [1] OCTET STRING OPTIONAL }
func readIntermediateResponse(bytes *Bytes) (ret IntermediateResponse, err error) {
err = bytes.ReadSubBytes(classApplication, TagIntermediateResponse, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readIntermediateResponse:\n%s", err.Error())}
return
}
return
}
func (bytes *Bytes) PreviewTagAndLength() (tagAndLength TagAndLength, err error) {
previousOffset := bytes.offset // Save offset
tagAndLength, err = bytes.ParseTagAndLength()
bytes.offset = previousOffset // Restore offset
return
}
func (res *IntermediateResponse) readComponents(bytes *Bytes) (err error) {
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagIntermediateResponseName {
var oid LDAPOID
oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagIntermediateResponseName)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
res.responseName = oid.Pointer()
}
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagIntermediateResponseValue {
var str OCTETSTRING
str, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagIntermediateResponseValue)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
res.responseValue = str.Pointer()
}
}
return
}
//
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
// responseName [0] LDAPOID OPTIONAL,
// responseValue [1] OCTET STRING OPTIONAL }
func (i IntermediateResponse) write(bytes *Bytes) (size int) {
if i.responseValue != nil {
size += i.responseValue.writeTagged(bytes, classContextSpecific, TagIntermediateResponseValue)
}
if i.responseName != nil {
size += i.responseName.writeTagged(bytes, classContextSpecific, TagIntermediateResponseName)
}
size += bytes.WriteTagAndLength(classApplication, isCompound, TagIntermediateResponse, size)
return
}
//
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
// responseName [0] LDAPOID OPTIONAL,
// responseValue [1] OCTET STRING OPTIONAL }
func (i IntermediateResponse) size() (size int) {
if i.responseName != nil {
size += i.responseName.sizeTagged(TagIntermediateResponseName)
}
if i.responseValue != nil {
size += i.responseValue.sizeTagged(TagIntermediateResponseValue)
}
size += sizeTagAndLength(TagIntermediateResponse, size)
return
}

View file

@ -0,0 +1,117 @@
package message
import "fmt"
//
// MatchingRuleAssertion ::= SEQUENCE {
// matchingRule [1] MatchingRuleId OPTIONAL,
// type [2] AttributeDescription OPTIONAL,
// matchValue [3] AssertionValue,
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
func readTaggedMatchingRuleAssertion(bytes *Bytes, class int, tag int) (ret MatchingRuleAssertion, err error) {
err = bytes.ReadSubBytes(class, tag, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedMatchingRuleAssertion:\n%s", err.Error())}
return
}
return
}
func (matchingruleassertion *MatchingRuleAssertion) readComponents(bytes *Bytes) (err error) {
err = matchingruleassertion.readMatchingRule(bytes)
if err != nil {
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
}
err = matchingruleassertion.readType(bytes)
if err != nil {
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
}
matchingruleassertion.matchValue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue)
if err != nil {
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
}
matchingruleassertion.dnAttributes, err = readTaggedBOOLEAN(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes)
if err != nil {
return LdapError{fmt.Sprintf("readComponents: %s", err.Error())}
}
return
}
func (matchingruleassertion *MatchingRuleAssertion) readMatchingRule(bytes *Bytes) (err error) {
var tagAndLength TagAndLength
tagAndLength, err = bytes.PreviewTagAndLength()
if err != nil {
return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())}
}
if tagAndLength.Tag == TagMatchingRuleAssertionMatchingRule {
var matchingRule MatchingRuleId
matchingRule, err = readTaggedMatchingRuleId(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule)
if err != nil {
return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())}
}
matchingruleassertion.matchingRule = matchingRule.Pointer()
}
return
}
func (matchingruleassertion *MatchingRuleAssertion) readType(bytes *Bytes) (err error) {
var tagAndLength TagAndLength
tagAndLength, err = bytes.PreviewTagAndLength()
if err != nil {
return LdapError{fmt.Sprintf("readType: %s", err.Error())}
}
if tagAndLength.Tag == TagMatchingRuleAssertionType {
var attributedescription AttributeDescription
attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagMatchingRuleAssertionType)
if err != nil {
return LdapError{fmt.Sprintf("readType: %s", err.Error())}
}
matchingruleassertion.type_ = &attributedescription
}
return
}
func (m MatchingRuleAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) {
if m.dnAttributes != BOOLEAN(false) {
size += m.dnAttributes.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes)
}
size += m.matchValue.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue)
if m.type_ != nil {
size += m.type_.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionType)
}
if m.matchingRule != nil {
size += m.matchingRule.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule)
}
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
//
// MatchingRuleAssertion ::= SEQUENCE {
// matchingRule [1] MatchingRuleId OPTIONAL,
// type [2] AttributeDescription OPTIONAL,
// matchValue [3] AssertionValue,
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
func (m MatchingRuleAssertion) write(bytes *Bytes) (size int) {
return m.writeTagged(bytes, classUniversal, tagSequence)
}
//
// MatchingRuleAssertion ::= SEQUENCE {
// matchingRule [1] MatchingRuleId OPTIONAL,
// type [2] AttributeDescription OPTIONAL,
// matchValue [3] AssertionValue,
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
func (m MatchingRuleAssertion) size() (size int) {
return m.sizeTagged(tagSequence)
}
func (m MatchingRuleAssertion) sizeTagged(tag int) (size int) {
if m.matchingRule != nil {
size += m.matchingRule.sizeTagged(TagMatchingRuleAssertionMatchingRule)
}
if m.type_ != nil {
size += m.type_.sizeTagged(TagMatchingRuleAssertionType)
}
size += m.matchValue.sizeTagged(TagMatchingRuleAssertionMatchValue)
if m.dnAttributes != BOOLEAN(false) {
size += m.dnAttributes.sizeTagged(TagMatchingRuleAssertionDnAttributes)
}
size += sizeTagAndLength(tag, size)
return
}

View file

@ -0,0 +1,29 @@
package message
import "fmt"
//
// MatchingRuleId ::= LDAPString
func readTaggedMatchingRuleId(bytes *Bytes, class int, tag int) (matchingruleid MatchingRuleId, err error) {
var ldapstring LDAPString
ldapstring, err = readTaggedLDAPString(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedMatchingRuleId:\n%s", err.Error())}
return
}
matchingruleid = MatchingRuleId(ldapstring)
return
}
func (m MatchingRuleId) Pointer() *MatchingRuleId { return &m }
//
// MatchingRuleId ::= LDAPString
func (m MatchingRuleId) writeTagged(bytes *Bytes, class int, tag int) int {
return LDAPString(m).writeTagged(bytes, class, tag)
}
//
// MatchingRuleId ::= LDAPString
func (m MatchingRuleId) sizeTagged(tag int) int {
return LDAPString(m).sizeTagged(tag)
}

139
goldap/message.go Normal file
View file

@ -0,0 +1,139 @@
package message
import (
"fmt"
"reflect"
)
// This appendix is normative.
//
// Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18}
// -- Copyright (C) The Internet Society (2006). This version of
// -- this ASN.1 module is part of RFC 4511; see the RFC itself
// -- for full legal notices.
// DEFINITIONS
// IMPLICIT TAGS
// EXTENSIBILITY IMPLIED ::=
//
// BEGIN
//
// LDAPMessage ::= SEQUENCE {
// messageID MessageID,
// protocolOp CHOICE {
// bindRequest BindRequest,
// bindResponse BindResponse,
// unbindRequest UnbindRequest,
// searchRequest SearchRequest,
// searchResEntry SearchResultEntry,
// searchResDone SearchResultDone,
// searchResRef SearchResultReference,
// modifyRequest ModifyRequest,
// modifyResponse ModifyResponse,
// addRequest AddRequest,
// addResponse AddResponse,
// delRequest DelRequest,
// delResponse DelResponse,
// modDNRequest ModifyDNRequest,
// modDNResponse ModifyDNResponse,
// compareRequest CompareRequest,
// compareResponse CompareResponse,
// abandonRequest AbandonRequest,
// extendedReq ExtendedRequest,
// extendedResp ExtendedResponse,
// ...,
// intermediateResponse IntermediateResponse },
// controls [0] Controls OPTIONAL }
//
func NewLDAPMessage() *LDAPMessage { return &LDAPMessage{} }
func (message *LDAPMessage) readComponents(bytes *Bytes) (err error) {
message.messageID, err = readMessageID(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
message.protocolOp, err = readProtocolOp(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagLDAPMessageControls {
var controls Controls
controls, err = readTaggedControls(bytes, classContextSpecific, TagLDAPMessageControls)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
message.controls = controls.Pointer()
}
}
return
}
func (m *LDAPMessage) Write() (bytes *Bytes, err error) {
defer func() {
if e := recover(); e != nil {
err = LdapError{fmt.Sprintf("Error in LDAPMessage.Write: %s", e)}
}
}()
// Compute the needed size
totalSize := m.size()
// Initialize the structure
bytes = &Bytes{
bytes: make([]byte, totalSize),
offset: totalSize,
}
// Go !
size := 0
if m.controls != nil {
size += m.controls.writeTagged(bytes, classContextSpecific, TagLDAPMessageControls)
}
size += m.protocolOp.write(bytes)
size += m.messageID.write(bytes)
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
// Check
if size != totalSize || bytes.offset != 0 {
err = LdapError{fmt.Sprintf("Something went wrong while writing the message ! Size is %d instead of %d, final offset is %d instead of 0", size, totalSize, bytes.offset)}
}
return
}
func (m *LDAPMessage) size() (size int) {
size += m.messageID.size()
size += m.protocolOp.size()
if m.controls != nil {
size += m.controls.sizeTagged(TagLDAPMessageControls)
}
size += sizeTagAndLength(tagSequence, size)
return
}
func (l *LDAPMessage) MessageID() MessageID {
return l.messageID
}
func (l *LDAPMessage) SetMessageID(ID int) {
l.messageID = MessageID(ID)
}
func (l *LDAPMessage) Controls() *Controls {
return l.controls
}
func (l *LDAPMessage) ProtocolOp() ProtocolOp {
return l.protocolOp
}
func (l *LDAPMessage) ProtocolOpName() string {
return reflect.TypeOf(l.ProtocolOp()).Name()
}
func (l *LDAPMessage) ProtocolOpType() int {
switch l.protocolOp.(type) {
case BindRequest:
return TagBindRequest
}
return 0
}

46
goldap/message_id.go Normal file
View file

@ -0,0 +1,46 @@
package message
import "fmt"
func readTaggedMessageID(bytes *Bytes, class int, tag int) (ret MessageID, err error) {
var integer INTEGER
integer, err = readTaggedPositiveINTEGER(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedMessageID:\n%s", err.Error())}
return
}
return MessageID(integer), err
}
// MessageID ::= INTEGER (0 .. maxInt)
//
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
//
func readMessageID(bytes *Bytes) (ret MessageID, err error) {
return readTaggedMessageID(bytes, classUniversal, tagInteger)
}
// MessageID ::= INTEGER (0 .. maxInt)
//
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
//
func (m MessageID) write(bytes *Bytes) int {
return INTEGER(m).write(bytes)
}
func (m MessageID) writeTagged(bytes *Bytes, class int, tag int) int {
return INTEGER(m).writeTagged(bytes, class, tag)
}
// MessageID ::= INTEGER (0 .. maxInt)
//
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
//
func (m MessageID) size() int {
return INTEGER(m).size()
}
func (m MessageID) sizeTagged(tag int) int {
return INTEGER(m).sizeTagged(tag)
}
func (l MessageID) Int() int {
return int(l)
}

View file

@ -0,0 +1,87 @@
package message
import "fmt"
//
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
// entry LDAPDN,
// newrdn RelativeLDAPDN,
// deleteoldrdn BOOLEAN,
// newSuperior [0] LDAPDN OPTIONAL }
func readModifyDNRequest(bytes *Bytes) (ret ModifyDNRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagModifyDNRequest, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readModifyDNRequest:\n%s", err.Error())}
return
}
return
}
func (req *ModifyDNRequest) readComponents(bytes *Bytes) (err error) {
req.entry, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
req.newrdn, err = readRelativeLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
req.deleteoldrdn, err = readBOOLEAN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagModifyDNRequestNewSuperior {
var ldapdn LDAPDN
ldapdn, err = readTaggedLDAPDN(bytes, classContextSpecific, TagModifyDNRequestNewSuperior)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
req.newSuperior = ldapdn.Pointer()
}
}
return
}
//
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
// entry LDAPDN,
// newrdn RelativeLDAPDN,
// deleteoldrdn BOOLEAN,
// newSuperior [0] LDAPDN OPTIONAL }
func (m ModifyDNRequest) write(bytes *Bytes) (size int) {
if m.newSuperior != nil {
size += m.newSuperior.writeTagged(bytes, classContextSpecific, TagModifyDNRequestNewSuperior)
}
size += m.deleteoldrdn.write(bytes)
size += m.newrdn.write(bytes)
size += m.entry.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyDNRequest, size)
return
}
//
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
// entry LDAPDN,
// newrdn RelativeLDAPDN,
// deleteoldrdn BOOLEAN,
// newSuperior [0] LDAPDN OPTIONAL }
func (m ModifyDNRequest) size() (size int) {
size += m.entry.size()
size += m.newrdn.size()
size += m.deleteoldrdn.size()
if m.newSuperior != nil {
size += m.newSuperior.sizeTagged(TagModifyDNRequestNewSuperior)
}
size += sizeTagAndLength(TagModifyDNRequest, size)
return
}

View file

@ -0,0 +1,28 @@
package message
import "fmt"
//
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
func readModifyDNResponse(bytes *Bytes) (ret ModifyDNResponse, err error) {
var res LDAPResult
res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyDNResponse)
if err != nil {
err = LdapError{fmt.Sprintf("readModifyDNResponse:\n%s", err.Error())}
return
}
ret = ModifyDNResponse(res)
return
}
//
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
func (m ModifyDNResponse) write(bytes *Bytes) int {
return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyDNResponse)
}
//
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
func (m ModifyDNResponse) size() int {
return LDAPResult(m).sizeTagged(TagModifyDNResponse)
}

89
goldap/modify_request.go Normal file
View file

@ -0,0 +1,89 @@
package message
import "fmt"
//
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
// object LDAPDN,
// changes SEQUENCE OF change SEQUENCE {
// operation ENUMERATED {
// add (0),
// delete (1),
// replace (2),
// ... },
// modification PartialAttribute } }
func readModifyRequest(bytes *Bytes) (ret ModifyRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagModifyRequest, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readModifyRequest:\n%s", err.Error())}
return
}
return
}
func (m *ModifyRequest) readComponents(bytes *Bytes) (err error) {
m.object, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
err = bytes.ReadSubBytes(classUniversal, tagSequence, m.readChanges)
return
}
func (m *ModifyRequest) readChanges(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var c ModifyRequestChange
c, err = readModifyRequestChange(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readChanges:\n%s", err.Error())}
return
}
m.changes = append(m.changes, c)
}
return
}
//
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
// object LDAPDN,
// changes SEQUENCE OF change SEQUENCE {
// operation ENUMERATED {
// add (0),
// delete (1),
// replace (2),
// ... },
// modification PartialAttribute } }
func (m ModifyRequest) write(bytes *Bytes) (size int) {
for i := len(m.changes) - 1; i >= 0; i-- {
size += m.changes[i].write(bytes)
}
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
size += m.object.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyRequest, size)
return
}
//
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
// object LDAPDN,
// changes SEQUENCE OF change SEQUENCE {
// operation ENUMERATED {
// add (0),
// delete (1),
// replace (2),
// ... },
// modification PartialAttribute } }
func (m ModifyRequest) size() (size int) {
for _, change := range m.changes {
size += change.size()
}
size += sizeTagAndLength(tagSequence, size)
size += m.object.size()
size += sizeTagAndLength(TagModifyRequest, size)
return
}
func (m *ModifyRequest) Object() LDAPDN {
return m.object
}
func (m *ModifyRequest) Changes() []ModifyRequestChange {
return m.changes
}

View file

@ -0,0 +1,43 @@
package message
import "fmt"
func readModifyRequestChange(bytes *Bytes) (ret ModifyRequestChange, err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readModifyRequestChange:\n%s", err.Error())}
return
}
return
}
func (m *ModifyRequestChange) readComponents(bytes *Bytes) (err error) {
m.operation, err = readENUMERATED(bytes, EnumeratedModifyRequestChangeOperation)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
m.modification, err = readPartialAttribute(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (m ModifyRequestChange) write(bytes *Bytes) (size int) {
size += m.modification.write(bytes)
size += m.operation.write(bytes)
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
func (m ModifyRequestChange) size() (size int) {
size += m.operation.size()
size += m.modification.size()
size += sizeTagAndLength(tagSequence, size)
return
}
func (m *ModifyRequestChange) Operation() ENUMERATED {
return m.operation
}
func (m *ModifyRequestChange) Modification() *PartialAttribute {
return &m.modification
}

36
goldap/modify_response.go Normal file
View file

@ -0,0 +1,36 @@
package message
import "fmt"
//
// ModifyResponse ::= [APPLICATION 7] LDAPResult
func readModifyResponse(bytes *Bytes) (ret ModifyResponse, err error) {
var res LDAPResult
res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyResponse)
if err != nil {
err = LdapError{fmt.Sprintf("readModifyResponse:\n%s", err.Error())}
return
}
ret = ModifyResponse(res)
return
}
func (l LDAPResult) writeTagged(bytes *Bytes, class int, tag int) (size int) {
size += l.writeComponents(bytes)
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
//
// ModifyResponse ::= [APPLICATION 7] LDAPResult
func (m ModifyResponse) write(bytes *Bytes) int {
return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyResponse)
}
//
// ModifyResponse ::= [APPLICATION 7] LDAPResult
func (m ModifyResponse) size() int {
return LDAPResult(m).sizeTagged(TagModifyResponse)
}
func (l *ModifyResponse) SetResultCode(code int) {
l.resultCode = ENUMERATED(code)
}

44
goldap/octetstring.go Normal file
View file

@ -0,0 +1,44 @@
package message
import "fmt"
func readOCTETSTRING(bytes *Bytes) (ret OCTETSTRING, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagOctetString, tagOctetString)
if err != nil {
err = LdapError{fmt.Sprintf("readOCTETSTRING:\n%s", err.Error())}
return
}
ret = OCTETSTRING(value.([]byte))
return
}
func readTaggedOCTETSTRING(bytes *Bytes, class int, tag int) (ret OCTETSTRING, err error) {
var value interface{}
value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagOctetString)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedOCTETSTRING:\n%s", err.Error())}
return
}
ret = OCTETSTRING(value.([]byte))
return
}
func (o OCTETSTRING) Pointer() *OCTETSTRING { return &o }
func (o OCTETSTRING) write(bytes *Bytes) int {
return bytes.WritePrimitiveSubBytes(classUniversal, tagOctetString, o)
}
func (o OCTETSTRING) writeTagged(bytes *Bytes, class int, tag int) int {
return bytes.WritePrimitiveSubBytes(class, tag, o)
}
func (o OCTETSTRING) size() int {
return SizePrimitiveSubBytes(tagOctetString, o)
}
func (o OCTETSTRING) sizeTagged(tag int) int {
return SizePrimitiveSubBytes(tag, o)
}
func (l OCTETSTRING) String() string {
return string(l)
}
func (l OCTETSTRING) Bytes() []byte {
return []byte(l)
}

50
goldap/oid.go Normal file
View file

@ -0,0 +1,50 @@
package message
import "fmt"
//
//
// LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
// -- [RFC4512]
func (l LDAPOID) String() string {
return string(l)
}
func (l LDAPOID) Bytes() []byte {
return []byte(l)
}
func (l LDAPOID) Pointer() *LDAPOID { return &l }
func readTaggedLDAPOID(bytes *Bytes, class int, tag int) (ret LDAPOID, err error) {
var octetstring OCTETSTRING
octetstring, err = readTaggedOCTETSTRING(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedLDAPOID:\n%s", err.Error())}
return
}
// @TODO: check RFC4512 for <numericoid>
ret = LDAPOID(octetstring)
return
}
func readLDAPOID(bytes *Bytes) (ret LDAPOID, err error) {
return readTaggedLDAPOID(bytes, classUniversal, tagOctetString)
}
func (l LDAPOID) write(bytes *Bytes) int {
return OCTETSTRING(l).write(bytes)
}
func (l LDAPOID) writeTagged(bytes *Bytes, class int, tag int) int {
return OCTETSTRING(l).writeTagged(bytes, class, tag)
}
func (l LDAPOID) size() int {
return OCTETSTRING(l).size()
}
func (l LDAPOID) sizeTagged(tag int) int {
return OCTETSTRING(l).sizeTagged(tag)
}

View file

@ -0,0 +1,76 @@
package message
import "fmt"
//
// PartialAttribute ::= SEQUENCE {
// type AttributeDescription,
// vals SET OF value AttributeValue }
func readPartialAttribute(bytes *Bytes) (ret PartialAttribute, err error) {
ret = PartialAttribute{vals: make([]AttributeValue, 0, 10)}
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readPartialAttribute:\n%s", err.Error())}
return
}
return
}
func (partialattribute *PartialAttribute) readComponents(bytes *Bytes) (err error) {
partialattribute.type_, err = readAttributeDescription(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
err = bytes.ReadSubBytes(classUniversal, tagSet, partialattribute.readValsComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
func (partialattribute *PartialAttribute) readValsComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var attributevalue AttributeValue
attributevalue, err = readAttributeValue(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readValsComponents:\n%s", err.Error())}
return
}
partialattribute.vals = append(partialattribute.vals, attributevalue)
}
return
}
//
// PartialAttribute ::= SEQUENCE {
// type AttributeDescription,
// vals SET OF value AttributeValue }
func (p PartialAttribute) write(bytes *Bytes) (size int) {
for i := len(p.vals) - 1; i >= 0; i-- {
size += p.vals[i].write(bytes)
}
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSet, size)
size += p.type_.write(bytes)
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
//
// PartialAttribute ::= SEQUENCE {
// type AttributeDescription,
// vals SET OF value AttributeValue }
func (p PartialAttribute) size() (size int) {
for _, value := range p.vals {
size += value.size()
}
size += sizeTagAndLength(tagSet, size)
size += p.type_.size()
size += sizeTagAndLength(tagSequence, size)
return
}
func (p *PartialAttribute) Type_() AttributeDescription {
return p.type_
}
func (p *PartialAttribute) Vals() []AttributeValue {
return p.vals
}

View file

@ -0,0 +1,53 @@
package message
import "fmt"
//
// PartialAttributeList ::= SEQUENCE OF
// partialAttribute PartialAttribute
func readPartialAttributeList(bytes *Bytes) (ret PartialAttributeList, err error) {
ret = PartialAttributeList(make([]PartialAttribute, 0, 10))
err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readPartialAttributeList:\n%s", err.Error())}
return
}
return
}
func (partialattributelist *PartialAttributeList) readComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var partialattribute PartialAttribute
partialattribute, err = readPartialAttribute(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
*partialattributelist = append(*partialattributelist, partialattribute)
}
return
}
//
// PartialAttributeList ::= SEQUENCE OF
// partialAttribute PartialAttribute
func (p PartialAttributeList) write(bytes *Bytes) (size int) {
for i := len(p) - 1; i >= 0; i-- {
size += p[i].write(bytes)
}
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
//
// PartialAttributeList ::= SEQUENCE OF
// partialAttribute PartialAttribute
func (p PartialAttributeList) size() (size int) {
for _, att := range p {
size += att.size()
}
size += sizeTagAndLength(tagSequence, size)
return
}
func (p *PartialAttributeList) add(a PartialAttribute) {
*p = append(*p, a)
}

63
goldap/protocol_op.go Normal file
View file

@ -0,0 +1,63 @@
package message
import "fmt"
func readProtocolOp(bytes *Bytes) (ret ProtocolOp, err error) {
tagAndLength, err := bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())}
return
}
switch tagAndLength.Tag {
case TagBindRequest:
ret, err = readBindRequest(bytes)
case TagBindResponse:
ret, err = readBindResponse(bytes)
case TagUnbindRequest:
ret, err = readUnbindRequest(bytes)
case TagSearchRequest:
ret, err = readSearchRequest(bytes)
case TagSearchResultEntry:
ret, err = readSearchResultEntry(bytes)
case TagSearchResultDone:
ret, err = readSearchResultDone(bytes)
case TagSearchResultReference:
ret, err = readSearchResultReference(bytes)
case TagModifyRequest:
ret, err = readModifyRequest(bytes)
case TagModifyResponse:
ret, err = readModifyResponse(bytes)
case TagAddRequest:
ret, err = readAddRequest(bytes)
case TagAddResponse:
ret, err = readAddResponse(bytes)
case TagDelRequest:
ret, err = readDelRequest(bytes)
case TagDelResponse:
ret, err = readDelResponse(bytes)
case TagModifyDNRequest:
ret, err = readModifyDNRequest(bytes)
case TagModifyDNResponse:
ret, err = readModifyDNResponse(bytes)
case TagCompareRequest:
ret, err = readCompareRequest(bytes)
case TagCompareResponse:
ret, err = readCompareResponse(bytes)
case TagAbandonRequest:
ret, err = readAbandonRequest(bytes)
case TagExtendedRequest:
ret, err = readExtendedRequest(bytes)
case TagExtendedResponse:
ret, err = readExtendedResponse(bytes)
case TagIntermediateResponse:
ret, err = readIntermediateResponse(bytes)
default:
err = LdapError{fmt.Sprintf("readProtocolOp: invalid tag value %d for protocolOp", tagAndLength.Tag)}
return
}
if err != nil {
err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())}
return
}
return
}

18
goldap/read.go Normal file
View file

@ -0,0 +1,18 @@
package message
import (
"fmt"
)
func ReadLDAPMessage(bytes *Bytes) (message LDAPMessage, err error) {
err = bytes.ReadSubBytes(classUniversal, tagSequence, message.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("ReadLDAPMessage:\n%s", err.Error())}
return
}
return
}
//
// END
//

3146
goldap/read_error_test.go Normal file

File diff suppressed because one or more lines are too long

2936
goldap/read_test.go Normal file

File diff suppressed because one or more lines are too long

50
goldap/referral.go Normal file
View file

@ -0,0 +1,50 @@
package message
import "fmt"
//
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
func readTaggedReferral(bytes *Bytes, class int, tag int) (referral Referral, err error) {
err = bytes.ReadSubBytes(class, tag, referral.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedReferral:\n%s", err.Error())}
return
}
return
}
func (referral *Referral) readComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var uri URI
uri, err = readURI(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
*referral = append(*referral, uri)
}
if len(*referral) == 0 {
return LdapError{"readComponents: expecting at least one URI"}
}
return
}
func (referral Referral) Pointer() *Referral { return &referral }
//
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
func (r Referral) writeTagged(bytes *Bytes, class int, tag int) (size int) {
for i := len(r) - 1; i >= 0; i-- {
size += r[i].write(bytes)
}
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
//
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
func (r Referral) sizeTagged(tag int) (size int) {
for _, uri := range r {
size += uri.size()
}
size += sizeTagAndLength(tag, size)
return
}

View file

@ -0,0 +1,15 @@
package message
//
// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
// -- [RFC4514]
func (r RelativeLDAPDN) write(bytes *Bytes) int {
return LDAPString(r).write(bytes)
}
//
// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
// -- [RFC4514]
func (r RelativeLDAPDN) size() int {
return LDAPString(r).size()
}

282
goldap/result.go Normal file
View file

@ -0,0 +1,282 @@
package message
import "fmt"
//
// LDAPResult ::= SEQUENCE {
// resultCode ENUMERATED {
// success (0),
// operationsError (1),
// protocolError (2),
// timeLimitExceeded (3),
// sizeLimitExceeded (4),
// compareFalse (5),
// compareTrue (6),
// authMethodNotSupported (7),
// strongerAuthRequired (8),
// -- 9 reserved --
// referral (10),
// adminLimitExceeded (11),
// unavailableCriticalExtension (12),
// confidentialityRequired (13),
// saslBindInProgress (14),
//
//
//
//Sermersheim Standards Track [Page 55]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// noSuchAttribute (16),
// undefinedAttributeType (17),
// inappropriateMatching (18),
// constraintViolation (19),
// attributeOrValueExists (20),
// invalidAttributeSyntax (21),
// -- 22-31 unused --
// noSuchObject (32),
// aliasProblem (33),
// invalidDNSyntax (34),
// -- 35 reserved for undefined isLeaf --
// aliasDereferencingProblem (36),
// -- 37-47 unused --
// inappropriateAuthentication (48),
// invalidCredentials (49),
// insufficientAccessRights (50),
// busy (51),
// unavailable (52),
// unwillingToPerform (53),
// loopDetect (54),
// -- 55-63 unused --
// namingViolation (64),
// objectClassViolation (65),
// notAllowedOnNonLeaf (66),
// notAllowedOnRDN (67),
// entryAlreadyExists (68),
// objectClassModsProhibited (69),
// -- 70 reserved for CLDAP --
// affectsMultipleDSAs (71),
// -- 72-79 unused --
// other (80),
// ... },
// matchedDN LDAPDN,
// diagnosticMessage LDAPString,
// referral [3] Referral OPTIONAL }
func readTaggedLDAPResult(bytes *Bytes, class int, tag int) (ret LDAPResult, err error) {
err = bytes.ReadSubBytes(class, tag, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedLDAPResult:\n%s", err.Error())}
}
return
}
func readLDAPResult(bytes *Bytes) (ldapresult LDAPResult, err error) {
return readTaggedLDAPResult(bytes, classUniversal, tagSequence)
}
func (ldapresult *LDAPResult) readComponents(bytes *Bytes) (err error) {
ldapresult.resultCode, err = readENUMERATED(bytes, EnumeratedLDAPResultCode)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
ldapresult.matchedDN, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
ldapresult.diagnosticMessage, err = readLDAPString(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if bytes.HasMoreData() {
var tag TagAndLength
tag, err = bytes.PreviewTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if tag.Tag == TagLDAPResultReferral {
var referral Referral
referral, err = readTaggedReferral(bytes, classContextSpecific, TagLDAPResultReferral)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
ldapresult.referral = referral.Pointer()
}
}
return
}
//
// LDAPResult ::= SEQUENCE {
// resultCode ENUMERATED {
// success (0),
// operationsError (1),
// protocolError (2),
// timeLimitExceeded (3),
// sizeLimitExceeded (4),
// compareFalse (5),
// compareTrue (6),
// authMethodNotSupported (7),
// strongerAuthRequired (8),
// -- 9 reserved --
// referral (10),
// adminLimitExceeded (11),
// unavailableCriticalExtension (12),
// confidentialityRequired (13),
// saslBindInProgress (14),
//
//
//
//Sermersheim Standards Track [Page 55]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// noSuchAttribute (16),
// undefinedAttributeType (17),
// inappropriateMatching (18),
// constraintViolation (19),
// attributeOrValueExists (20),
// invalidAttributeSyntax (21),
// -- 22-31 unused --
// noSuchObject (32),
// aliasProblem (33),
// invalidDNSyntax (34),
// -- 35 reserved for undefined isLeaf --
// aliasDereferencingProblem (36),
// -- 37-47 unused --
// inappropriateAuthentication (48),
// invalidCredentials (49),
// insufficientAccessRights (50),
// busy (51),
// unavailable (52),
// unwillingToPerform (53),
// loopDetect (54),
// -- 55-63 unused --
// namingViolation (64),
// objectClassViolation (65),
// notAllowedOnNonLeaf (66),
// notAllowedOnRDN (67),
// entryAlreadyExists (68),
// objectClassModsProhibited (69),
// -- 70 reserved for CLDAP --
// affectsMultipleDSAs (71),
// -- 72-79 unused --
// other (80),
// ... },
// matchedDN LDAPDN,
// diagnosticMessage LDAPString,
// referral [3] Referral OPTIONAL }
func (l LDAPResult) write(bytes *Bytes) (size int) {
size += l.writeComponents(bytes)
size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size)
return
}
func (l LDAPResult) writeComponents(bytes *Bytes) (size int) {
if l.referral != nil {
size += l.referral.writeTagged(bytes, classContextSpecific, TagLDAPResultReferral)
}
size += l.diagnosticMessage.write(bytes)
size += l.matchedDN.write(bytes)
size += l.resultCode.write(bytes)
return
}
//
// LDAPResult ::= SEQUENCE {
// resultCode ENUMERATED {
// success (0),
// operationsError (1),
// protocolError (2),
// timeLimitExceeded (3),
// sizeLimitExceeded (4),
// compareFalse (5),
// compareTrue (6),
// authMethodNotSupported (7),
// strongerAuthRequired (8),
// -- 9 reserved --
// referral (10),
// adminLimitExceeded (11),
// unavailableCriticalExtension (12),
// confidentialityRequired (13),
// saslBindInProgress (14),
//
//
//
//Sermersheim Standards Track [Page 55]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// noSuchAttribute (16),
// undefinedAttributeType (17),
// inappropriateMatching (18),
// constraintViolation (19),
// attributeOrValueExists (20),
// invalidAttributeSyntax (21),
// -- 22-31 unused --
// noSuchObject (32),
// aliasProblem (33),
// invalidDNSyntax (34),
// -- 35 reserved for undefined isLeaf --
// aliasDereferencingProblem (36),
// -- 37-47 unused --
// inappropriateAuthentication (48),
// invalidCredentials (49),
// insufficientAccessRights (50),
// busy (51),
// unavailable (52),
// unwillingToPerform (53),
// loopDetect (54),
// -- 55-63 unused --
// namingViolation (64),
// objectClassViolation (65),
// notAllowedOnNonLeaf (66),
// notAllowedOnRDN (67),
// entryAlreadyExists (68),
// objectClassModsProhibited (69),
// -- 70 reserved for CLDAP --
// affectsMultipleDSAs (71),
// -- 72-79 unused --
// other (80),
// ... },
// matchedDN LDAPDN,
// diagnosticMessage LDAPString,
// referral [3] Referral OPTIONAL }
func (l LDAPResult) size() (size int) {
size += l.sizeComponents()
size += sizeTagAndLength(tagSequence, size)
return
}
func (l LDAPResult) sizeTagged(tag int) (size int) {
size += l.sizeComponents()
size += sizeTagAndLength(tag, size)
return
}
func (l LDAPResult) sizeComponents() (size int) {
if l.referral != nil {
size += l.referral.sizeTagged(TagLDAPResultReferral)
}
size += l.diagnosticMessage.size()
size += l.matchedDN.size()
size += l.resultCode.size()
return
}
func (l *LDAPResult) SetResultCode(code int) {
l.resultCode = ENUMERATED(code)
}
func (l *LDAPResult) SeMatchedDN(code string) {
l.matchedDN = LDAPDN(code)
}
func (l *LDAPResult) SetDiagnosticMessage(code string) {
l.diagnosticMessage = LDAPString(code)
}
func (l *LDAPResult) SetReferral(r *Referral) {
l.referral = r
}

View file

@ -0,0 +1,63 @@
package message
import "fmt"
//
// SaslCredentials ::= SEQUENCE {
// mechanism LDAPString,
// credentials OCTET STRING OPTIONAL }
//
func readSaslCredentials(bytes *Bytes) (authentication SaslCredentials, err error) {
authentication = SaslCredentials{}
err = bytes.ReadSubBytes(classContextSpecific, TagAuthenticationChoiceSaslCredentials, authentication.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readSaslCredentials:\n%s", err.Error())}
return
}
return
}
func (authentication *SaslCredentials) readComponents(bytes *Bytes) (err error) {
authentication.mechanism, err = readLDAPString(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
if bytes.HasMoreData() {
var credentials OCTETSTRING
credentials, err = readOCTETSTRING(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
authentication.credentials = credentials.Pointer()
}
return
}
//
// SaslCredentials ::= SEQUENCE {
// mechanism LDAPString,
// credentials OCTET STRING OPTIONAL }
//
func (s SaslCredentials) writeTagged(bytes *Bytes, class int, tag int) (size int) {
if s.credentials != nil {
size += s.credentials.write(bytes)
}
size += s.mechanism.write(bytes)
size += bytes.WriteTagAndLength(class, isCompound, tag, size)
return
}
//
// SaslCredentials ::= SEQUENCE {
// mechanism LDAPString,
// credentials OCTET STRING OPTIONAL }
//
func (s SaslCredentials) sizeTagged(tag int) (size int) {
if s.credentials != nil {
size += s.credentials.size()
}
size += s.mechanism.size()
size += sizeTagAndLength(tag, size)
return
}

246
goldap/search_request.go Normal file
View file

@ -0,0 +1,246 @@
package message
import (
"errors"
"fmt"
)
//
// 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 }
func readSearchRequest(bytes *Bytes) (searchrequest SearchRequest, err error) {
err = bytes.ReadSubBytes(classApplication, TagSearchRequest, searchrequest.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readSearchRequest:\n%s", err.Error())}
return
}
return
}
func (searchrequest *SearchRequest) readComponents(bytes *Bytes) (err error) {
searchrequest.baseObject, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.scope, err = readENUMERATED(bytes, EnumeratedSearchRequestScope)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.derefAliases, err = readENUMERATED(bytes, EnumeratedSearchRequestDerefAliases)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.sizeLimit, err = readPositiveINTEGER(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.timeLimit, err = readPositiveINTEGER(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.typesOnly, err = readBOOLEAN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.filter, err = readFilter(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchrequest.attributes, err = readAttributeSelection(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
//
// 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 }
func (s SearchRequest) write(bytes *Bytes) (size int) {
size += s.attributes.write(bytes)
size += s.filter.write(bytes)
size += s.typesOnly.write(bytes)
size += s.timeLimit.write(bytes)
size += s.sizeLimit.write(bytes)
size += s.derefAliases.write(bytes)
size += s.scope.write(bytes)
size += s.baseObject.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchRequest, size)
return
}
//
// 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 }
func (s SearchRequest) size() (size int) {
size += s.baseObject.size()
size += s.scope.size()
size += s.derefAliases.size()
size += s.sizeLimit.size()
size += s.timeLimit.size()
size += s.typesOnly.size()
size += s.filter.size()
size += s.attributes.size()
size += sizeTagAndLength(TagSearchRequest, size)
return
}
func (s *SearchRequest) BaseObject() LDAPDN {
return s.baseObject
}
func (s *SearchRequest) Scope() ENUMERATED {
return s.scope
}
func (s *SearchRequest) DerefAliases() ENUMERATED {
return s.derefAliases
}
func (s *SearchRequest) SizeLimit() INTEGER {
return s.sizeLimit
}
func (s *SearchRequest) TimeLimit() INTEGER {
return s.timeLimit
}
func (s *SearchRequest) TypesOnly() BOOLEAN {
return s.typesOnly
}
func (s *SearchRequest) Attributes() AttributeSelection {
return s.attributes
}
func (s *SearchRequest) Filter() Filter {
return s.filter
}
func (s *SearchRequest) FilterString() string {
str, _ := s.decompileFilter(s.Filter())
return str
}
func (s *SearchRequest) decompileFilter(packet Filter) (ret string, err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("error decompiling filter")
}
}()
ret = "("
err = nil
childStr := ""
switch f := packet.(type) {
case FilterAnd:
ret += "&"
for _, child := range f {
childStr, err = s.decompileFilter(child)
if err != nil {
return
}
ret += childStr
}
case FilterOr:
ret += "|"
for _, child := range f {
childStr, err = s.decompileFilter(child)
if err != nil {
return
}
ret += childStr
}
case FilterNot:
ret += "!"
childStr, err = s.decompileFilter(f.Filter)
if err != nil {
return
}
ret += childStr
case FilterSubstrings:
ret += string(f.Type_())
ret += "="
for _, fs := range f.Substrings() {
switch fsv := fs.(type) {
case SubstringInitial:
ret += string(fsv) + "*"
case SubstringAny:
ret += "*" + string(fsv) + "*"
case SubstringFinal:
ret += "*" + string(fsv)
}
}
case FilterEqualityMatch:
ret += string(f.AttributeDesc())
ret += "="
ret += string(f.AssertionValue())
case FilterGreaterOrEqual:
ret += string(f.AttributeDesc())
ret += ">="
ret += string(f.AssertionValue())
case FilterLessOrEqual:
ret += string(f.AttributeDesc())
ret += "<="
ret += string(f.AssertionValue())
case FilterPresent:
// if 0 == len(packet.Children) {
// ret += ber.DecodeString(packet.Data.Bytes())
// } else {
// ret += ber.DecodeString(packet.Children[0].Data.Bytes())
// }
ret += string(f)
ret += "=*"
case FilterApproxMatch:
ret += string(f.AttributeDesc())
ret += "~="
ret += string(f.AssertionValue())
}
ret += ")"
return
}

View file

@ -0,0 +1,31 @@
package message
import "fmt"
//
// SearchResultDone ::= [APPLICATION 5] LDAPResult
func readSearchResultDone(bytes *Bytes) (ret SearchResultDone, err error) {
var ldapresult LDAPResult
ldapresult, err = readTaggedLDAPResult(bytes, classApplication, TagSearchResultDone)
if err != nil {
err = LdapError{fmt.Sprintf("readSearchResultDone:\n%s", err.Error())}
return
}
ret = SearchResultDone(ldapresult)
return
}
//
// SearchResultDone ::= [APPLICATION 5] LDAPResult
func (s SearchResultDone) write(bytes *Bytes) int {
return LDAPResult(s).writeTagged(bytes, classApplication, TagSearchResultDone)
}
//
// SearchResultDone ::= [APPLICATION 5] LDAPResult
func (s SearchResultDone) size() int {
return LDAPResult(s).sizeTagged(TagSearchResultDone)
}
func (l *SearchResultDone) SetResultCode(code int) {
l.resultCode = ENUMERATED(code)
}

View file

@ -0,0 +1,58 @@
package message
import "fmt"
//
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
// objectName LDAPDN,
// attributes PartialAttributeList }
func readSearchResultEntry(bytes *Bytes) (searchresultentry SearchResultEntry, err error) {
err = bytes.ReadSubBytes(classApplication, TagSearchResultEntry, searchresultentry.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readSearchResultEntry:\n%s", err.Error())}
return
}
return
}
func (searchresultentry *SearchResultEntry) readComponents(bytes *Bytes) (err error) {
searchresultentry.objectName, err = readLDAPDN(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
searchresultentry.attributes, err = readPartialAttributeList(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
return
}
//
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
// objectName LDAPDN,
// attributes PartialAttributeList }
func (s SearchResultEntry) write(bytes *Bytes) (size int) {
size += s.attributes.write(bytes)
size += s.objectName.write(bytes)
size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultEntry, size)
return
}
//
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
// objectName LDAPDN,
// attributes PartialAttributeList }
func (s SearchResultEntry) size() (size int) {
size += s.objectName.size()
size += s.attributes.size()
size += sizeTagAndLength(tagSequence, size)
return
}
func (s *SearchResultEntry) SetObjectName(on string) {
s.objectName = LDAPDN(on)
}
func (s *SearchResultEntry) AddAttribute(name AttributeDescription, values ...AttributeValue) {
var ea = PartialAttribute{type_: name, vals: values}
s.attributes.add(ea)
}

View file

@ -0,0 +1,53 @@
package message
import "fmt"
//
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
// SIZE (1..MAX) OF uri URI
func readSearchResultReference(bytes *Bytes) (ret SearchResultReference, err error) {
err = bytes.ReadSubBytes(classApplication, TagSearchResultReference, ret.readComponents)
if err != nil {
err = LdapError{fmt.Sprintf("readSearchResultReference:\n%s", err.Error())}
return
}
return
}
func (s *SearchResultReference) readComponents(bytes *Bytes) (err error) {
for bytes.HasMoreData() {
var uri URI
uri, err = readURI(bytes)
if err != nil {
err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())}
return
}
*s = append(*s, uri)
}
if len(*s) == 0 {
err = LdapError{"readComponents: expecting at least one URI"}
return
}
return
}
//
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
// SIZE (1..MAX) OF uri URI
func (s SearchResultReference) write(bytes *Bytes) (size int) {
for i := len(s) - 1; i >= 0; i-- {
size += s[i].write(bytes)
}
size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultReference, size)
return
}
//
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
// SIZE (1..MAX) OF uri URI
func (s SearchResultReference) size() (size int) {
for _, uri := range s {
size += uri.size()
}
size += sizeTagAndLength(tagSequence, size)
return
}

85
goldap/size_test.go Normal file
View file

@ -0,0 +1,85 @@
package message
import (
"testing"
)
func TestSizeLDAPMessage(t *testing.T) {
var testData = getLDAPMessageTestData()
for i, test := range testData {
message, err := ReadLDAPMessage(&test.bytes)
if err != nil {
t.Errorf("#%d error at offset %d (%s): %s", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err)
}
size := message.size()
expected := len(test.bytes.bytes)
if size != expected {
t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, expected)
}
}
}
type tagAndLengthTestData struct {
tag int
length int
expectedSize int
}
func getSizeTagAndLengthTestData() (ret []tagAndLengthTestData) {
return []tagAndLengthTestData{
// Length between 0 and 127 are encoded on one byte
{
tag: tagSequence,
length: 0,
expectedSize: 2,
},
{
tag: tagSequence,
length: 127,
expectedSize: 2,
},
// Length between 128 and 255 are encoded on two bytes
{
tag: tagSequence,
length: 128,
expectedSize: 3,
},
{
tag: tagSequence,
length: 255,
expectedSize: 3,
},
// Length between 256 (2^8) and 65535 (2^16-1) are encoded on three bytes
{
tag: tagSequence,
length: 256,
expectedSize: 4,
},
{
tag: tagSequence,
length: 65535,
expectedSize: 4,
},
// Length between 65536 (2^16) and 16777215 (2^24-1) are encoded on four bytes
{
tag: tagSequence,
length: 65536,
expectedSize: 5,
},
{
tag: tagSequence,
length: 16777215,
expectedSize: 5,
},
}
}
func TestSizeTagAndLength(t *testing.T) {
for i, test := range getSizeTagAndLengthTestData() {
size := sizeTagAndLength(test.tag, test.length)
if test.expectedSize != size {
t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, test.expectedSize)
}
}
}

38
goldap/string.go Normal file
View file

@ -0,0 +1,38 @@
package message
import "fmt"
func readTaggedLDAPString(bytes *Bytes, class int, tag int) (ldapstring LDAPString, err error) {
var octetstring OCTETSTRING
octetstring, err = readTaggedOCTETSTRING(bytes, class, tag)
if err != nil {
err = LdapError{fmt.Sprintf("readTaggedLDAPString:\n%s", err.Error())}
return
}
ldapstring = LDAPString(octetstring)
return
}
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
// -- [ISO10646] characters
func readLDAPString(bytes *Bytes) (ldapstring LDAPString, err error) {
return readTaggedLDAPString(bytes, classUniversal, tagOctetString)
}
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
// -- [ISO10646] characters
func (s LDAPString) write(bytes *Bytes) int {
return OCTETSTRING(s).write(bytes)
}
func (s LDAPString) writeTagged(bytes *Bytes, class int, tag int) int {
return OCTETSTRING(s).writeTagged(bytes, class, tag)
}
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
// -- [ISO10646] characters
func (s LDAPString) size() int {
return OCTETSTRING(s).size()
}
func (s LDAPString) sizeTagged(tag int) int {
return OCTETSTRING(s).sizeTagged(tag)
}

739
goldap/struct.go Normal file
View file

@ -0,0 +1,739 @@
package message
type OCTETSTRING string
type INTEGER int32 // In this RFC the max INTEGER value is 2^31 - 1, so int32 is enough
type BOOLEAN bool
type ENUMERATED int32
// This appendix is normative.
//
// Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18}
// -- Copyright (C) The Internet Society (2006). This version of
// -- this ASN.1 module is part of RFC 4511; see the RFC itself
// -- for full legal notices.
// DEFINITIONS
// IMPLICIT TAGS
// EXTENSIBILITY IMPLIED ::=
//
// BEGIN
//
// LDAPMessage ::= SEQUENCE {
// messageID MessageID,
// protocolOp CHOICE {
// bindRequest BindRequest,
// bindResponse BindResponse,
// unbindRequest UnbindRequest,
// searchRequest SearchRequest,
// searchResEntry SearchResultEntry,
// searchResDone SearchResultDone,
// searchResRef SearchResultReference,
// modifyRequest ModifyRequest,
// modifyResponse ModifyResponse,
// addRequest AddRequest,
// addResponse AddResponse,
// delRequest DelRequest,
// delResponse DelResponse,
// modDNRequest ModifyDNRequest,
// modDNResponse ModifyDNResponse,
// compareRequest CompareRequest,
// compareResponse CompareResponse,
// abandonRequest AbandonRequest,
// extendedReq ExtendedRequest,
// extendedResp ExtendedResponse,
// ...,
// intermediateResponse IntermediateResponse },
// controls [0] Controls OPTIONAL }
//
type LDAPMessage struct {
messageID MessageID
protocolOp ProtocolOp
controls *Controls
}
const TagLDAPMessageControls = 0
type ProtocolOp interface {
size() int
write(*Bytes) int
}
// MessageID ::= INTEGER (0 .. maxInt)
//
type MessageID INTEGER
// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
const maxInt = INTEGER(2147483647)
//
// LDAPString ::= OCTET STRING -- UTF-8 encoded,
// -- [ISO10646] characters
type LDAPString OCTETSTRING
//
//
//
//
//Sermersheim Standards Track [Page 54]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
// -- [RFC4512]
type LDAPOID OCTETSTRING
//
// LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
// -- [RFC4514]
type LDAPDN LDAPString
//
// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
// -- [RFC4514]
type RelativeLDAPDN LDAPString
//
// AttributeDescription ::= LDAPString
// -- Constrained to <attributedescription>
// -- [RFC4512]
type AttributeDescription LDAPString
//
// AttributeValue ::= OCTET STRING
type AttributeValue OCTETSTRING
//
// AttributeValueAssertion ::= SEQUENCE {
// attributeDesc AttributeDescription,
// assertionValue AssertionValue }
type AttributeValueAssertion struct {
attributeDesc AttributeDescription
assertionValue AssertionValue
}
//
// AssertionValue ::= OCTET STRING
type AssertionValue OCTETSTRING
//
// PartialAttribute ::= SEQUENCE {
// type AttributeDescription,
// vals SET OF value AttributeValue }
type PartialAttribute struct {
type_ AttributeDescription
vals []AttributeValue
}
//
// Attribute ::= PartialAttribute(WITH COMPONENTS {
// ...,
// vals (SIZE(1..MAX))})
type Attribute PartialAttribute
//
// MatchingRuleId ::= LDAPString
type MatchingRuleId LDAPString
//
// LDAPResult ::= SEQUENCE {
// resultCode ENUMERATED {
// success (0),
// operationsError (1),
// protocolError (2),
// timeLimitExceeded (3),
// sizeLimitExceeded (4),
// compareFalse (5),
// compareTrue (6),
// authMethodNotSupported (7),
// strongerAuthRequired (8),
// -- 9 reserved --
// referral (10),
// adminLimitExceeded (11),
// unavailableCriticalExtension (12),
// confidentialityRequired (13),
// saslBindInProgress (14),
//
//
//
//Sermersheim Standards Track [Page 55]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// noSuchAttribute (16),
// undefinedAttributeType (17),
// inappropriateMatching (18),
// constraintViolation (19),
// attributeOrValueExists (20),
// invalidAttributeSyntax (21),
// -- 22-31 unused --
// noSuchObject (32),
// aliasProblem (33),
// invalidDNSyntax (34),
// -- 35 reserved for undefined isLeaf --
// aliasDereferencingProblem (36),
// -- 37-47 unused --
// inappropriateAuthentication (48),
// invalidCredentials (49),
// insufficientAccessRights (50),
// busy (51),
// unavailable (52),
// unwillingToPerform (53),
// loopDetect (54),
// -- 55-63 unused --
// namingViolation (64),
// objectClassViolation (65),
// notAllowedOnNonLeaf (66),
// notAllowedOnRDN (67),
// entryAlreadyExists (68),
// objectClassModsProhibited (69),
// -- 70 reserved for CLDAP --
// affectsMultipleDSAs (71),
// -- 72-79 unused --
// other (80),
// ... },
// matchedDN LDAPDN,
// diagnosticMessage LDAPString,
// referral [3] Referral OPTIONAL }
//
type LDAPResult struct {
resultCode ENUMERATED
matchedDN LDAPDN
diagnosticMessage LDAPString
referral *Referral
}
const TagLDAPResultReferral = 3
const ResultCodeSuccess = 0
const ResultCodeOperationsError = 1
const ResultCodeProtocolError = 2
const ResultCodeTimeLimitExceeded = 3
const ResultCodeSizeLimitExceeded = 4
const ResultCodeCompareFalse = 5
const ResultCodeCompareTrue = 6
const ResultCodeAuthMethodNotSupported = 7
const ResultCodeStrongerAuthRequired = 8
const ResultCodeReferral = 10
const ResultCodeAdminLimitExceeded = 11
const ResultCodeUnavailableCriticalExtension = 12
const ResultCodeConfidentialityRequired = 13
const ResultCodeSaslBindInProgress = 14
const ResultCodeNoSuchAttribute = 16
const ResultCodeUndefinedAttributeType = 17
const ResultCodeInappropriateMatching = 18
const ResultCodeConstraintViolation = 19
const ResultCodeAttributeOrValueExists = 20
const ResultCodeInvalidAttributeSyntax = 21
const ResultCodeNoSuchObject = 32
const ResultCodeAliasProblem = 33
const ResultCodeInvalidDNSyntax = 34
const ResultCodeAliasDereferencingProblem = 36
const ResultCodeInappropriateAuthentication = 48
const ResultCodeInvalidCredentials = 49
const ResultCodeInsufficientAccessRights = 50
const ResultCodeBusy = 51
const ResultCodeUnavailable = 52
const ResultCodeUnwillingToPerform = 53
const ResultCodeLoopDetect = 54
const ResultCodeNamingViolation = 64
const ResultCodeObjectClassViolation = 65
const ResultCodeNotAllowedOnNonLeaf = 66
const ResultCodeNotAllowedOnRDN = 67
const ResultCodeEntryAlreadyExists = 68
const ResultCodeObjectClassModsProhibited = 69
const ResultCodeAffectsMultipleDSAs = 71
const ResultCodeOther = 80
var EnumeratedLDAPResultCode = map[ENUMERATED]string{
ResultCodeSuccess: "success",
ResultCodeOperationsError: "operationsError",
ResultCodeProtocolError: "protocolError",
ResultCodeTimeLimitExceeded: "timeLimitExceeded",
ResultCodeSizeLimitExceeded: "sizeLimitExceeded",
ResultCodeCompareFalse: "compareFalse",
ResultCodeCompareTrue: "compareTrue",
ResultCodeAuthMethodNotSupported: "authMethodNotSupported",
ResultCodeStrongerAuthRequired: "strongerAuthRequired",
// -- 9 reserved --
ResultCodeReferral: "referral",
ResultCodeAdminLimitExceeded: "adminLimitExceeded",
ResultCodeUnavailableCriticalExtension: "unavailableCriticalExtension",
ResultCodeConfidentialityRequired: "confidentialityRequired",
ResultCodeSaslBindInProgress: "saslBindInProgress",
ResultCodeNoSuchAttribute: "noSuchAttribute",
ResultCodeUndefinedAttributeType: "undefinedAttributeType",
ResultCodeInappropriateMatching: "inappropriateMatching",
ResultCodeConstraintViolation: "constraintViolation",
ResultCodeAttributeOrValueExists: "attributeOrValueExists",
ResultCodeInvalidAttributeSyntax: "invalidAttributeSyntax",
// -- 22-31 unused --
ResultCodeNoSuchObject: "noSuchObject",
ResultCodeAliasProblem: "aliasProblem",
ResultCodeInvalidDNSyntax: "invalidDNSyntax",
// -- 35 reserved for undefined isLeaf --
ResultCodeAliasDereferencingProblem: "aliasDereferencingProblem",
// -- 37-47 unused --
ResultCodeInappropriateAuthentication: "inappropriateAuthentication",
ResultCodeInvalidCredentials: "invalidCredentials",
ResultCodeInsufficientAccessRights: "insufficientAccessRights",
ResultCodeBusy: "busy",
ResultCodeUnavailable: "unavailable",
ResultCodeUnwillingToPerform: "unwillingToPerform",
ResultCodeLoopDetect: "loopDetect",
// -- 55-63 unused --
ResultCodeNamingViolation: "namingViolation",
ResultCodeObjectClassViolation: "objectClassViolation",
ResultCodeNotAllowedOnNonLeaf: "notAllowedOnNonLeaf",
ResultCodeNotAllowedOnRDN: "notAllowedOnRDN",
ResultCodeEntryAlreadyExists: "entryAlreadyExists",
ResultCodeObjectClassModsProhibited: "objectClassModsProhibited",
// -- 70 reserved for CLDAP --
ResultCodeAffectsMultipleDSAs: "affectsMultipleDSAs",
// -- 72-79 unused --
ResultCodeOther: "other",
}
// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
type Referral []URI
//
// URI ::= LDAPString -- limited to characters permitted in
// -- URIs
type URI LDAPString
//
// Controls ::= SEQUENCE OF control Control
type Controls []Control
//
// Control ::= SEQUENCE {
// controlType LDAPOID,
// criticality BOOLEAN DEFAULT FALSE,
// controlValue OCTET STRING OPTIONAL }
type Control struct {
controlType LDAPOID
criticality BOOLEAN
controlValue *OCTETSTRING
}
//
//
//
//
//Sermersheim Standards Track [Page 56]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// BindRequest ::= [APPLICATION 0] SEQUENCE {
// version INTEGER (1 .. 127),
// name LDAPDN,
// authentication AuthenticationChoice }
const TagBindRequest = 0
const BindRequestVersionMin = 1
const BindRequestVersionMax = 127
type BindRequest struct {
version INTEGER
name LDAPDN
authentication AuthenticationChoice
}
//
// AuthenticationChoice ::= CHOICE {
// simple [0] OCTET STRING,
// -- 1 and 2 reserved
// sasl [3] SaslCredentials,
// ... }
const TagAuthenticationChoiceSimple = 0
const TagAuthenticationChoiceSaslCredentials = 3
type AuthenticationChoice interface {
sizeTagged(int) int
}
//
// SaslCredentials ::= SEQUENCE {
// mechanism LDAPString,
// credentials OCTET STRING OPTIONAL }
type SaslCredentials struct {
mechanism LDAPString
credentials *OCTETSTRING
}
//
// BindResponse ::= [APPLICATION 1] SEQUENCE {
// COMPONENTS OF LDAPResult,
// serverSaslCreds [7] OCTET STRING OPTIONAL }
const TagBindResponse = 1
const TagBindResponseServerSaslCreds = 7
type BindResponse struct {
LDAPResult
serverSaslCreds *OCTETSTRING
}
//
// UnbindRequest ::= [APPLICATION 2] NULL
const TagUnbindRequest = 2
type UnbindRequest struct {
}
//
// 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 }
const TagSearchRequest = 3
type SearchRequest struct {
baseObject LDAPDN
scope ENUMERATED
derefAliases ENUMERATED
sizeLimit INTEGER
timeLimit INTEGER
typesOnly BOOLEAN
filter Filter
attributes AttributeSelection
}
const SearchRequestScopeBaseObject = 0
const SearchRequestSingleLevel = 1
const SearchRequestHomeSubtree = 2
var EnumeratedSearchRequestScope = map[ENUMERATED]string{
SearchRequestScopeBaseObject: "baseObject",
SearchRequestSingleLevel: "singleLevel",
SearchRequestHomeSubtree: "homeSubtree",
}
const SearchRequetDerefAliasesNeverDerefAliases = 0
const SearchRequetDerefAliasesDerefInSearching = 1
const SearchRequetDerefAliasesDerefFindingBaseObj = 2
const SearchRequetDerefAliasesDerefAlways = 3
var EnumeratedSearchRequestDerefAliases = map[ENUMERATED]string{
SearchRequetDerefAliasesNeverDerefAliases: "neverDerefAliases",
SearchRequetDerefAliasesDerefInSearching: "derefInSearching",
SearchRequetDerefAliasesDerefFindingBaseObj: "derefFindingBaseObj",
SearchRequetDerefAliasesDerefAlways: "derefAlways",
}
//
// AttributeSelection ::= SEQUENCE OF selector LDAPString
// -- The LDAPString is constrained to
// -- <attributeSelector> in Section 4.5.1.8
type AttributeSelection []LDAPString
//
// 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,
//
//
//
//Sermersheim Standards Track [Page 57]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// substrings [4] SubstringFilter,
// greaterOrEqual [5] AttributeValueAssertion,
// lessOrEqual [6] AttributeValueAssertion,
// present [7] AttributeDescription,
// approxMatch [8] AttributeValueAssertion,
// extensibleMatch [9] MatchingRuleAssertion,
// ... }
const TagFilterAnd = 0
const TagFilterOr = 1
const TagFilterNot = 2
const TagFilterEqualityMatch = 3
const TagFilterSubstrings = 4
const TagFilterGreaterOrEqual = 5
const TagFilterLessOrEqual = 6
const TagFilterPresent = 7
const TagFilterApproxMatch = 8
const TagFilterExtensibleMatch = 9
type Filter interface {
size() int
write(*Bytes) int
getFilterTag() int
}
type FilterAnd []Filter
type FilterOr []Filter
type FilterNot struct {
Filter
}
type FilterEqualityMatch AttributeValueAssertion
type FilterSubstrings SubstringFilter
type FilterGreaterOrEqual AttributeValueAssertion
type FilterLessOrEqual AttributeValueAssertion
type FilterPresent AttributeDescription
type FilterApproxMatch AttributeValueAssertion
type FilterExtensibleMatch 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
// }
type SubstringFilter struct {
type_ AttributeDescription
substrings []Substring
}
type Substring interface{}
const TagSubstringInitial = 0
const TagSubstringAny = 1
const TagSubstringFinal = 2
type SubstringInitial AssertionValue
type SubstringAny AssertionValue
type SubstringFinal AssertionValue
//
// MatchingRuleAssertion ::= SEQUENCE {
// matchingRule [1] MatchingRuleId OPTIONAL,
// type [2] AttributeDescription OPTIONAL,
// matchValue [3] AssertionValue,
// dnAttributes [4] BOOLEAN DEFAULT FALSE }
type MatchingRuleAssertion struct {
matchingRule *MatchingRuleId
type_ *AttributeDescription
matchValue AssertionValue
dnAttributes BOOLEAN
}
const TagMatchingRuleAssertionMatchingRule = 1
const TagMatchingRuleAssertionType = 2
const TagMatchingRuleAssertionMatchValue = 3
const TagMatchingRuleAssertionDnAttributes = 4
//
// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
// objectName LDAPDN,
// attributes PartialAttributeList }
const TagSearchResultEntry = 4
type SearchResultEntry struct {
objectName LDAPDN
attributes PartialAttributeList
}
//
// PartialAttributeList ::= SEQUENCE OF
// partialAttribute PartialAttribute
type PartialAttributeList []PartialAttribute
//
// SearchResultReference ::= [APPLICATION 19] SEQUENCE
// SIZE (1..MAX) OF uri URI
const TagSearchResultReference = 19
type SearchResultReference []URI
//
// SearchResultDone ::= [APPLICATION 5] LDAPResult
const TagSearchResultDone = 5
type SearchResultDone LDAPResult
//
// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
// object LDAPDN,
// changes SEQUENCE OF change SEQUENCE {
// operation ENUMERATED {
// add (0),
// delete (1),
// replace (2),
// ... },
// modification PartialAttribute } }
const TagModifyRequest = 6
type ModifyRequest struct {
object LDAPDN
changes []ModifyRequestChange
}
type ModifyRequestChange struct {
operation ENUMERATED
modification PartialAttribute
}
const ModifyRequestChangeOperationAdd = 0
const ModifyRequestChangeOperationDelete = 1
const ModifyRequestChangeOperationReplace = 2
var EnumeratedModifyRequestChangeOperation = map[ENUMERATED]string{
ModifyRequestChangeOperationAdd: "add",
ModifyRequestChangeOperationDelete: "delete",
ModifyRequestChangeOperationReplace: "replace",
}
//
// ModifyResponse ::= [APPLICATION 7] LDAPResult
const TagModifyResponse = 7
type ModifyResponse LDAPResult
//
//
//
//
//
//
//Sermersheim Standards Track [Page 58]
//
//
//RFC 4511 LDAPv3 June 2006
//
//
// AddRequest ::= [APPLICATION 8] SEQUENCE {
// entry LDAPDN,
// attributes AttributeList }
const TagAddRequest = 8
type AddRequest struct {
entry LDAPDN
attributes AttributeList
}
//
// AttributeList ::= SEQUENCE OF attribute Attribute
type AttributeList []Attribute
//
// AddResponse ::= [APPLICATION 9] LDAPResult
const TagAddResponse = 9
type AddResponse LDAPResult
//
// DelRequest ::= [APPLICATION 10] LDAPDN
const TagDelRequest = 10
type DelRequest LDAPDN
//
// DelResponse ::= [APPLICATION 11] LDAPResult
const TagDelResponse = 11
type DelResponse LDAPResult
//
// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
// entry LDAPDN,
// newrdn RelativeLDAPDN,
// deleteoldrdn BOOLEAN,
// newSuperior [0] LDAPDN OPTIONAL }
const TagModifyDNRequest = 12
type ModifyDNRequest struct {
entry LDAPDN
newrdn RelativeLDAPDN
deleteoldrdn BOOLEAN
newSuperior *LDAPDN
}
const TagModifyDNRequestNewSuperior = 0
//
// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
const TagModifyDNResponse = 13
type ModifyDNResponse LDAPResult
//
// CompareRequest ::= [APPLICATION 14] SEQUENCE {
// entry LDAPDN,
// ava AttributeValueAssertion }
const TagCompareRequest = 14
type CompareRequest struct {
entry LDAPDN
ava AttributeValueAssertion
}
// CompareResponse ::= [APPLICATION 15] LDAPResult
const TagCompareResponse = 15
type CompareResponse LDAPResult
//
// AbandonRequest ::= [APPLICATION 16] MessageID
const TagAbandonRequest = 16
type AbandonRequest MessageID
//
// ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
// requestName [0] LDAPOID,
// requestValue [1] OCTET STRING OPTIONAL }
const TagExtendedRequest = 23
type ExtendedRequest struct {
requestName LDAPOID
requestValue *OCTETSTRING
}
const TagExtendedRequestName = 0
const TagExtendedRequestValue = 1
//
// ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
// COMPONENTS OF LDAPResult,
// responseName [10] LDAPOID OPTIONAL,
// responseValue [11] OCTET STRING OPTIONAL }
const TagExtendedResponse = 24
type ExtendedResponse struct {
LDAPResult
responseName *LDAPOID
responseValue *OCTETSTRING
}
const TagExtendedResponseName = 10
const TagExtendedResponseValue = 11
//
// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
// responseName [0] LDAPOID OPTIONAL,
// responseValue [1] OCTET STRING OPTIONAL }
const TagIntermediateResponse = 25
type IntermediateResponse struct {
responseName *LDAPOID
responseValue *OCTETSTRING
}
const TagIntermediateResponseName = 0
const TagIntermediateResponseValue = 1
//
// END
//

7
goldap/struct_methods.go Normal file
View file

@ -0,0 +1,7 @@
package message
func NewLDAPMessageWithProtocolOp(po ProtocolOp) *LDAPMessage {
m := NewLDAPMessage()
m.protocolOp = po
return m
}

38
goldap/unbind_request.go Normal file
View file

@ -0,0 +1,38 @@
package message
import "fmt"
//
// UnbindRequest ::= [APPLICATION 2] NULL
func readUnbindRequest(bytes *Bytes) (unbindrequest UnbindRequest, err error) {
var tagAndLength TagAndLength
tagAndLength, err = bytes.ParseTagAndLength()
if err != nil {
err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())}
return
}
err = tagAndLength.Expect(classApplication, TagUnbindRequest, isNotCompound)
if err != nil {
err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())}
return
}
if tagAndLength.Length != 0 {
err = LdapError{"readUnbindRequest: expecting NULL"}
return
}
return
}
//
// UnbindRequest ::= [APPLICATION 2] NULL
func (u UnbindRequest) write(bytes *Bytes) (size int) {
size += bytes.WriteTagAndLength(classApplication, isNotCompound, TagUnbindRequest, 0)
return
}
//
// UnbindRequest ::= [APPLICATION 2] NULL
func (u UnbindRequest) size() (size int) {
size = sizeTagAndLength(TagUnbindRequest, 0)
return
}

32
goldap/uri.go Normal file
View file

@ -0,0 +1,32 @@
package message
import "fmt"
//
// URI ::= LDAPString -- limited to characters permitted in
// -- URIs
func readURI(bytes *Bytes) (uri URI, err error) {
var ldapstring LDAPString
ldapstring, err = readLDAPString(bytes)
// @TODO: check permitted chars in URI
if err != nil {
err = LdapError{fmt.Sprintf("readURI:\n%s", err.Error())}
return
}
uri = URI(ldapstring)
return
}
//
// URI ::= LDAPString -- limited to characters permitted in
// -- URIs
func (u URI) write(bytes *Bytes) int {
return LDAPString(u).write(bytes)
}
//
// URI ::= LDAPString -- limited to characters permitted in
// -- URIs
func (u URI) size() int {
return LDAPString(u).size()
}

6
goldap/write.go Normal file
View file

@ -0,0 +1,6 @@
package message
type Writable interface {
write(bytes *Bytes) int
writeTagged(bytes *Bytes, class int, tag int) int
}

19
goldap/write_test.go Normal file
View file

@ -0,0 +1,19 @@
package message
import (
"reflect"
"testing"
)
func TestWriteLDAPMessage(t *testing.T) {
var testData = getLDAPMessageTestData()
for i, test := range testData {
bytes, err := test.out.Write()
if err != nil {
t.Errorf("#%d error at offset %d (%s): %s\nEXPECTED BYTES: %#v\nWRITTEN BYTES: %#v\n", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err, test.bytes.getBytes(), bytes.getBytes())
} else if !reflect.DeepEqual(bytes.getBytes(), test.bytes.getBytes()) {
t.Errorf("#%d:\nGOT:\n%#+v\nEXPECTED:\n%#+v", i, bytes.getBytes(), test.bytes.getBytes())
}
}
}

View file

@ -6,7 +6,7 @@ import (
"sync"
"time"
ldap "github.com/lor00x/goldap/message"
ldap "bottin/goldap"
)
type UserState interface{}

View file

@ -1,6 +1,6 @@
package ldapserver
import ldap "github.com/lor00x/goldap/message"
import ldap "bottin/goldap"
// LDAP Application Codes
const (

View file

@ -3,7 +3,7 @@ package ldapserver
import (
"fmt"
ldap "github.com/lor00x/goldap/message"
ldap "bottin/goldap"
)
type Message struct {

View file

@ -5,7 +5,7 @@ import (
"errors"
"fmt"
ldap "github.com/lor00x/goldap/message"
ldap "bottin/goldap"
)
type messagePacket struct {

View file

@ -1,6 +1,6 @@
package ldapserver
import ldap "github.com/lor00x/goldap/message"
import ldap "bottin/goldap"
func NewBindResponse(resultCode int) ldap.BindResponse {
r := ldap.BindResponse{}

View file

@ -3,7 +3,7 @@ package ldapserver
import (
"strings"
ldap "github.com/lor00x/goldap/message"
ldap "bottin/goldap"
)
// Constant to LDAP Request protocol Type names

19
main.go
View file

@ -15,7 +15,7 @@ import (
ldap "bottin/ldapserver"
consul "github.com/hashicorp/consul/api"
message "github.com/lor00x/goldap/message"
message "bottin/goldap"
log "github.com/sirupsen/logrus"
)
@ -320,12 +320,19 @@ func (server *Server) init() error {
return err
}
admin_pass := make([]byte, 8)
_, err = rand.Read(admin_pass)
if err != nil {
return err
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
if !environnement_variable_exist {
admin_pass := make([]byte, 8)
_, err = rand.Read(admin_pass)
if err != nil {
return err
}
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
} else {
server.logger.Printf("It seems that exists a password in environnement variable")
}
admin_pass_str := base64.RawURLEncoding.EncodeToString(admin_pass)
admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
admin_dn := "cn=admin," + server.config.Suffix

View file

@ -6,7 +6,7 @@ import (
ldap "bottin/ldapserver"
message "github.com/lor00x/goldap/message"
message "bottin/goldap"
)
// Generic read utility functions ----------

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,13 @@
{
"suffix": "dc=deuxfleurs,dc=fr",
"bind": "127.0.0.1:1389",
"acl": [
"ANONYMOUS::bind:*,ou=users,dc=deuxfleurs,dc=fr:",
"ANONYMOUS::bind:cn=admin,dc=deuxfleurs,dc=fr:",
"*,dc=deuxfleurs,dc=fr::read:*:* !userpassword",
"*::read modify:SELF:*",
"cn=admin,dc=deuxfleurs,dc=fr::read add modify delete:*:*",
"*:cn=admin,ou=groups,dc=deuxfleurs,dc=fr:read add modify delete:*:*"
]
}

8
test_automatic/go.mod Normal file
View file

@ -0,0 +1,8 @@
module bottin/integration
go 1.14
require (
github.com/go-ldap/ldap/v3 v3.3.0
github.com/sirupsen/logrus v1.4.2
)

23
test_automatic/go.sum Normal file
View file

@ -0,0 +1,23 @@
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View file

@ -0,0 +1,431 @@
package main
import (
"github.com/go-ldap/ldap/v3"
"fmt"
log "github.com/sirupsen/logrus"
"math/rand"
"strings"
"errors"
"os"
)
const bindusername = "cn=admin,dc=deuxfleurs,dc=fr"
const adresse = "127.0.0.1"
const port = 1389
var bindpassword string
var all_names = make(map[string]struct{})
func printError(LDAPError error) {
if LDAPError != nil {
log.Fatal(LDAPError)
}
}
func createOU(l *ldap.Conn) error {
req := ldap.NewAddRequest("ou=groups,dc=deuxfleurs,dc=fr",nil)
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les groupes"})
req.Attribute("objectclass",[]string{"organizationalUnit", "top"})
req.Attribute("ou",[]string{"groups"})
req.Attribute("structuralobjectclass", []string{"organizationalUnit"})
err := l.Add(req)
if err != nil {
return err
}
req = ldap.NewAddRequest("ou=users,dc=deuxfleurs,dc=fr",nil)
req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les utilisateurs"})
req.Attribute("objectclass",[]string{"organizationalUnit", "top"})
req.Attribute("ou",[]string{"users"})
req.Attribute("structuralobjectclass", []string{"organizationalUnit"})
err = l.Add(req)
return err
}
func generateName(r *rand.Rand) (name string) {
for only_one := true; only_one; _, only_one = all_names[name]{
name = fmt.Sprintf("%d",r.Int())
}
all_names[name] = struct{}{}
log.Debug(fmt.Sprintf("Name generated: %s.\n", name))
return
}
func createGroup(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) {
StructuralObjectClass := []string{"groupOfNames"}
ObjectClass := []string{"groupOfNames","top"}
for i := 0; i<20; i++ {
//Generate name and check if he is unique
name := generateName(r)
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=groups,dc=deuxfleurs,dc=fr",name),nil)
req.Attribute("description",[]string{generateName(r)})
req.Attribute("objectclass",ObjectClass)
req.Attribute("structuralobjectclass",StructuralObjectClass)
err = l.Add(req)
if err != nil {
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] groupe.\n",i))
return nil, err
}
tab_AddRequest = append(tab_AddRequest, *req)
}
return
}
func createUser(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) {
StructuralObjectClass := []string{"inetOrgPerson"}
ObjectClass := []string{"inetOrgPerson","organizationalPerson","person","top"}
for i := 0; i<20; i++ {
name := generateName(r)
req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=users,dc=deuxfleurs,dc=fr",name),nil)
req.Attribute("displayname",[]string{generateName(r)})
req.Attribute("objectclass",ObjectClass)
req.Attribute("structuralobjectclass",StructuralObjectClass)
err = l.Add(req)
if err != nil {
log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] user.\n",i))
return nil, err
}
tab_AddRequest = append(tab_AddRequest, *req)
}
return
}
func search_attributes(tab_Attributes []ldap.Attribute, tipe string) (*ldap.Attribute) {
for _,att := range tab_Attributes {
if att.Type == tipe {
return &att
}
}
return nil
// return ldap.Attribute{}
}
func test_attributes(l *ldap.Conn, tab_AddRequest []ldap.AddRequest, filter_objectclass, user_or_group string) (err error) {
for _, addRequest := range tab_AddRequest {
//On prend le cn en supposant qu'il est unique
cn := strings.Split(addRequest.DN,",")[0]
//On crée la requête pour la recherche
search_req := ldap.NewSearchRequest(
fmt.Sprintf("ou=%s,dc=deuxfleurs,dc=fr",user_or_group),
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectclass=%s)(%s))", filter_objectclass,cn),
[]string{"displayname","objectclass","structuralobjectclass"},
nil,
)
//On lance la recherche
result, err := l.Search(search_req)
if err != nil {
return err
}
if len(result.Entries) != 1 {
return errors.New("Test a trouvé plusieurs displaynames en commun ou en a trouvé aucun")
}
//On compare les attributs qu'on a reçu avec les attributs qu'on a envoyé
result_attributes := result.Entries[0].Attributes
log.Debug(fmt.Sprintf("La longueur est de %d, contient : \n %s.\n",len(result_attributes), result_attributes))
//Notre recherche crée un attribut par valeur, même si les valeurs viennent du même nom d'attribut
//Par exemple: objectclass possède 4 valeurs. Alors on aura 4 EntryAttribute qui contient chacune une des 4 valeurs de l'attribut objectclass
//j est l'indice qui représente la j-ème valeur de notre attribut
var j int
var att *ldap.Attribute
for i,attributes := range result_attributes {
//On cherche l'attribut de l'user i qui a le même nom que celui qu'on a reçu et qu'on traite dans cette boucle
if j == 0 {
att = search_attributes(addRequest.Attributes, attributes.Name)
if att == nil {
return errors.New(fmt.Sprintf("Error: test_attributes - Don't find match name attributes. We search %s.\n", attributes.Name))
}
}
log.Debug(fmt.Sprintf("Le nom de l'attribut est %s, sa valeur est: \n %s.", att.Type, att))
if j >= len(att.Vals) || att.Vals[j] != attributes.Values[0] {
return errors.New(fmt.Sprintf("Error: test_attributes - Theses values aren't the same: %d, %d",att.Vals, attributes.Values))
}
if i+1 < len(result_attributes) && result_attributes[i+1].Name == attributes.Name {
j += 1
} else { j = 0}
}
}
return nil
}
func clean(l *ldap.Conn, AddReq_users, AddReq_groups []ldap.AddRequest,user, group bool) (err error){
log.Debug("Debut clean")
if(user) {
for _,req := range AddReq_users {
delReq := ldap.NewDelRequest(req.DN,nil)
err = l.Del(delReq)
if err != nil {
return
}
}
}
if group {
for _,req := range AddReq_groups {
delReq := ldap.NewDelRequest(req.DN, nil)
err = l.Del(delReq)
if err != nil {
return
}
}
}
defer log.Debug("Fin clean")
return
}
func test_modify_attributes(l *ldap.Conn, r *rand.Rand, tab_AddReq []ldap.AddRequest, tab_type_name []string) (err error) {
for _, AddReq := range tab_AddReq {
modReq := ldap.NewModifyRequest(AddReq.DN,nil)
for _, type_name := range tab_type_name {
newName := generateName(r)
modReq.Replace(type_name, []string{newName})
att := search_attributes(AddReq.Attributes, type_name)
att.Vals[0] = newName
}
err = l.Modify(modReq)
if err != nil {
return
}
}
return
}
func add_user_in_groups(l *ldap.Conn, r *rand.Rand, users, groups []ldap.AddRequest) (err error) {
for _,group := range groups {
numberUsers := r.Intn(19) + 1 //Always a minimum of 1 user
list_users := []string{}
for i:=0; i < numberUsers; i++ {
list_users = append(list_users, users[i].DN)
}
modifyReq := ldap.NewModifyRequest( group.DN, nil)
modifyReq.Add("member", list_users)
err = l.Modify(modifyReq)
if err != nil {
log.Warn(fmt.Sprintf("Error: ModifyReq failed, func:add_users_in_groups from group:\n %d",group))
return
}
}
return
}
func delete_groups(l *ldap.Conn, groups []ldap.AddRequest) (list map[string][]string ,err error) {
list = make(map[string][]string)
for _, group := range groups {
//Get lists_users
cn := strings.Split(group.DN,",")[0]
search_req := ldap.NewSearchRequest(
"ou=groups,dc=deuxfleurs,dc=fr",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectclass=groupOfNames)(%s))",cn),
[]string{"member"},
nil,
)
res , err := l.Search(search_req)
if err != nil {
log.Warn(fmt.Sprintf("Error Search: func: delete_groups_and_check_memberOf, from group: \n %d", group))
return list, err
}
if len(res.Entries) != 1 {
err = errors.New(fmt.Sprintf("SearchResult get: %s, SearchResult wanted: 1", len(res.Entries)))
return list, err
}
EntryAtt := res.Entries[0].Attributes
list_users := []string{}
for _, att := range EntryAtt {
list_users = append(list_users ,att.Values[0])
}
//Del group
del := ldap.NewDelRequest( group.DN, nil)
err = l.Del(del)
if err != nil {
return list, err
}
list[group.DN] = list_users
}
return
}
func check_memberOf(l *ldap.Conn, list map[string][]string) (err error) {
//Check the memberOf of all users
for groupeDN,_ := range list{
search_req := ldap.NewSearchRequest(
"ou=users,dc=deuxfleurs,dc=fr",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,0 ,0, false,
fmt.Sprintf("(&(objectclass=inetOrgPerson)(memberOf=%s))",groupeDN),
[]string{"cn"},
nil,
)
res, err := l.Search(search_req)
if err != nil {
return err
}
if len(res.Entries) != 0 {
err = errors.New(fmt.Sprintf("L'user '%s' a encore le DN d'un groupe supprimé: %s",res.Entries[0].Attributes[0].Values[0],groupeDN))
return err
}
}
return err
}
func reconnect(l *ldap.Conn) (l_nouv *ldap.Conn, err error){
l.Close()
l_nouv, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse,port))
if err != nil {
return
}
err = l_nouv.Bind(bindusername, bindpassword)
return
}
func main() {
var ok bool
bindpassword, ok = os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
if !ok {
if len(os.Args) == 2 {
bindpassword = os.Args[1]
} else {
bindpassword = ""
}
}
log.Info(fmt.Sprintf("Password selected: %s",bindpassword))
//log.SetLevel(log.TraceLevel)
//Create a connection with Bottin server
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port))
//l.Debug = true
printError(err)
//Bind with the admin account generated
err = l.Bind(bindusername, bindpassword)
printError(err)
//Create our object Rand, it's important to always have the same values
source := rand.NewSource(666475745)
r := rand.New(source)
log.Info(fmt.Sprintf("The seed of the rand object is %d.\n",r.Seed))
//Create user and groups OrgaUnit
err = createOU(l)
if ldap.IsErrorWithCode(err, uint16(68)) {
log.Warn("Les OrganizationalUnit users et groups sont déjà présents.")
}else {
printError(err)
log.Info("Création des OU de groups et users")
}
//Create random groups
tab_AddRequest_groups, err := createGroup(r, l)
printError(err)
log.Info(fmt.Sprintf("Création des groupes aléatoirement réussi: %d\n", len(tab_AddRequest_groups)))
//Create random users
tab_AddRequest_users, err := createUser(r, l)
printError(err)
log.Info(fmt.Sprintf("Création des users aléatoirement réussi: %d\n", len(tab_AddRequest_users)))
//Search and compare attribute Users. (We keep Attribute object from 'Create random users' and compare with the result of our search)
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson","users")
printError(err)
log.Info("Tous les attributs users insérés dans Consul ont été vérifiés..\n")
//Search and compare attributes Groups
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames","groups")
printError(err)
log.Info("Tous les attributs groups insérés dans Consul ont été vérifiés.\n")
//Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
l,err = reconnect(l)
printError(err)
//Modify attributes users and groups.
//Modify users' attributes and check them
log.Debug(fmt.Sprintf("Les valeurs sont:\n %s", tab_AddRequest_users))
err = test_modify_attributes(l, r, tab_AddRequest_users, []string{"displayname"})
printError(err)
log.Debug("Modifications users faites")
//Check if the attributes are correct:
err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson", "users")
printError(err)
log.Info("Les modifications ont bien été prises en compte")
log.Debug(fmt.Sprintf("Les nouvelles valeurs sont:\n %s", tab_AddRequest_users))
//Modify users' attributes and check them
err = test_modify_attributes(l, r, tab_AddRequest_groups, []string{"description"})
printError(err)
log.Info("Modifications groups faites")
//Check if the attributes are correct:
err = test_attributes(l,tab_AddRequest_groups, "groupOfNames", "groups")
printError(err)
log.Info("Les modifications ont bien été prises en compte")
//Close the connection
l, err = reconnect(l)
printError(err)
//Add users in group, search them, delete several samples and search again to be sur it's good
err = add_user_in_groups(l, r, tab_AddRequest_users, tab_AddRequest_groups)
printError(err)
log.Info("Ajout d'users dans les groupes fait")
//Close the connection
l, err = reconnect(l)
printError(err)
list, err := delete_groups(l, tab_AddRequest_groups)
printError(err)
log.Info("groupe supprimé")
l,err = reconnect(l)
printError(err)
err = check_memberOf(l, list)
printError(err)
log.Info("Le memberOf a été correctement vidé")
//Clean: Delete all users and groups (not OU users and groups)
err = clean(l, tab_AddRequest_users, tab_AddRequest_groups, true, false)
printError(err)
log.Info("Clean succes")
defer os.Exit(0)
return
}

View file

@ -0,0 +1,40 @@
Introduction et Observation premières:
Lors de la réalisation de mon code Go, j'ai trouvé un beug qui provoquait l'arrêt de mon programme car Bottin envoyé une réponse erronée à mon programme.
Dans logs de Bottin je ne voyais aucune erreur, Bottin n'avait pas cessé de fonctionner. Il s'arrêtait à chaque fois sur mes requêtes Del (mes dernières).
Reproduction du beug:
Pour reproduire le beug, il suffit de lancer le programme interrogation.go et de commenter les lignes suivantes :
260 //Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this
261 l.Close()
262 l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse, port))
263 printError(err)
264 err = l.Bind(bindusername, bindpassword)
265 printError(err)
Ainsi on obtient l'erreur suivante:
2021/07/07 01:25:51 Received unexpected message -128, false
Test réalisé pour comprendre la source du problème:
Ma première hypothèses fut que j'envoyais trop de requêtes dans un court laps de temps et ainsi Bottin ou Consul ne pouvait pas suivre.
J'ai placé un sleep de 50 puis de 100 Millisecondes entre chaque requête, j'obtenais toujours la même erreur.
J'ai essayé de mettre un sleep de 10 secondes avant mes requêtes de suppression mais j'obtenais toujours la même chose.
Hack pour résoudre:
La première solution qui a fonctionné était de réduire le nombre de requêtes en n'exécutant pas certains tests random.
La dernière solutions qui est utilisée:
Fermée la connexion puis la réouvrir permet de palier à ce problème
Hypothèses du problème ?:
Existerait-il un Buffer par Bind qui se remplirait ? Et lorsque celui-ci est plein, ne renvoie pas d'erreur juste n'arrive plus à répondre.
Erwan DUFOUR
Deuxfleurs

12
test_automatic/start_test.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
#export BOTTIN_DEFAULT_ADMIN_PW=$(openssl rand -base64 24)
echo $BOTTIN_DEFAULT_ADMIN_PW
consul agent -dev > /dev/null 2>&1 &
sleep 2
cp test_automatic/config.json.test config.json
./bottin > /dev/null 2>&1 &
sleep 1
./test_automatic/integration
rm config.json
exit 0

View file

@ -8,7 +8,7 @@ import (
ldap "bottin/ldapserver"
consul "github.com/hashicorp/consul/api"
message "github.com/lor00x/goldap/message"
message "bottin/goldap"
)
// Generic item modification function --------