Vendor goldap
This commit is contained in:
parent
a53641e773
commit
477d7014ed
74 changed files with 11818 additions and 0 deletions
25
goldap/abandon_request.go
Normal file
25
goldap/abandon_request.go
Normal 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
53
goldap/add_request.go
Normal 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
28
goldap/add_response.go
Normal 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
740
goldap/asn1.go
Normal 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
44
goldap/assertion_value.go
Normal 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
40
goldap/attribute.go
Normal 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)
|
||||
}
|
50
goldap/attribute_description.go
Normal file
50
goldap/attribute_description.go
Normal 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
43
goldap/attribute_list.go
Normal 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
|
||||
}
|
46
goldap/attribute_selection.go
Normal file
46
goldap/attribute_selection.go
Normal 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
24
goldap/attribute_value.go
Normal 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()
|
||||
}
|
77
goldap/attribute_value_assertion.go
Normal file
77
goldap/attribute_value_assertion.go
Normal 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
|
||||
}
|
37
goldap/authentication_choice.go
Normal file
37
goldap/authentication_choice.go
Normal 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
93
goldap/bind_request.go
Normal 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
56
goldap/bind_response.go
Normal 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
62
goldap/boolean.go
Normal 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
199
goldap/bytes.go
Normal 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) {
|
||||