1295 lines
42 KiB
Go
1295 lines
42 KiB
Go
|
package ldap
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
|
||
|
ber "github.com/go-asn1-ber/asn1-ber"
|
||
|
"github.com/google/uuid"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt
|
||
|
ControlTypePaging = "1.2.840.113556.1.4.319"
|
||
|
// ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
|
||
|
ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
|
||
|
// ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
||
|
ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
|
||
|
// ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
||
|
ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
|
||
|
// ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296
|
||
|
ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
|
||
|
// ControlTypeWhoAmI - https://tools.ietf.org/html/rfc4532
|
||
|
ControlTypeWhoAmI = "1.3.6.1.4.1.4203.1.11.3"
|
||
|
// ControlTypeSubtreeDelete - https://datatracker.ietf.org/doc/html/draft-armijo-ldap-treedelete-02
|
||
|
ControlTypeSubtreeDelete = "1.2.840.113556.1.4.805"
|
||
|
|
||
|
// ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
|
||
|
ControlTypeServerSideSorting = "1.2.840.113556.1.4.473"
|
||
|
// ControlTypeServerSideSorting - https://www.ietf.org/rfc/rfc2891.txt
|
||
|
ControlTypeServerSideSortingResult = "1.2.840.113556.1.4.474"
|
||
|
|
||
|
// ControlTypeMicrosoftNotification - https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
|
||
|
ControlTypeMicrosoftNotification = "1.2.840.113556.1.4.528"
|
||
|
// ControlTypeMicrosoftShowDeleted - https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
|
||
|
ControlTypeMicrosoftShowDeleted = "1.2.840.113556.1.4.417"
|
||
|
// ControlTypeMicrosoftServerLinkTTL - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN
|
||
|
ControlTypeMicrosoftServerLinkTTL = "1.2.840.113556.1.4.2309"
|
||
|
// ControlTypeDirSync - Active Directory DirSync - https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
|
||
|
ControlTypeDirSync = "1.2.840.113556.1.4.841"
|
||
|
|
||
|
// ControlTypeSyncRequest - https://www.ietf.org/rfc/rfc4533.txt
|
||
|
ControlTypeSyncRequest = "1.3.6.1.4.1.4203.1.9.1.1"
|
||
|
// ControlTypeSyncState - https://www.ietf.org/rfc/rfc4533.txt
|
||
|
ControlTypeSyncState = "1.3.6.1.4.1.4203.1.9.1.2"
|
||
|
// ControlTypeSyncDone - https://www.ietf.org/rfc/rfc4533.txt
|
||
|
ControlTypeSyncDone = "1.3.6.1.4.1.4203.1.9.1.3"
|
||
|
// ControlTypeSyncInfo - https://www.ietf.org/rfc/rfc4533.txt
|
||
|
ControlTypeSyncInfo = "1.3.6.1.4.1.4203.1.9.1.4"
|
||
|
)
|
||
|
|
||
|
// Flags for DirSync control
|
||
|
const (
|
||
|
DirSyncIncrementalValues int64 = 2147483648
|
||
|
DirSyncPublicDataOnly int64 = 8192
|
||
|
DirSyncAncestorsFirstOrder int64 = 2048
|
||
|
DirSyncObjectSecurity int64 = 1
|
||
|
)
|
||
|
|
||
|
// ControlTypeMap maps controls to text descriptions
|
||
|
var ControlTypeMap = map[string]string{
|
||
|
ControlTypePaging: "Paging",
|
||
|
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
|
||
|
ControlTypeManageDsaIT: "Manage DSA IT",
|
||
|
ControlTypeSubtreeDelete: "Subtree Delete Control",
|
||
|
ControlTypeMicrosoftNotification: "Change Notification - Microsoft",
|
||
|
ControlTypeMicrosoftShowDeleted: "Show Deleted Objects - Microsoft",
|
||
|
ControlTypeMicrosoftServerLinkTTL: "Return TTL-DNs for link values with associated expiry times - Microsoft",
|
||
|
ControlTypeServerSideSorting: "Server Side Sorting Request - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
|
||
|
ControlTypeServerSideSortingResult: "Server Side Sorting Results - LDAP Control Extension for Server Side Sorting of Search Results (RFC2891)",
|
||
|
ControlTypeDirSync: "DirSync",
|
||
|
ControlTypeSyncRequest: "Sync Request",
|
||
|
ControlTypeSyncState: "Sync State",
|
||
|
ControlTypeSyncDone: "Sync Done",
|
||
|
ControlTypeSyncInfo: "Sync Info",
|
||
|
}
|
||
|
|
||
|
// Control defines an interface controls provide to encode and describe themselves
|
||
|
type Control interface {
|
||
|
// GetControlType returns the OID
|
||
|
GetControlType() string
|
||
|
// Encode returns the ber packet representation
|
||
|
Encode() *ber.Packet
|
||
|
// String returns a human-readable description
|
||
|
String() string
|
||
|
}
|
||
|
|
||
|
// ControlString implements the Control interface for simple controls
|
||
|
type ControlString struct {
|
||
|
ControlType string
|
||
|
Criticality bool
|
||
|
ControlValue string
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlString) GetControlType() string {
|
||
|
return c.ControlType
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlString) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
|
||
|
if c.Criticality {
|
||
|
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
|
||
|
}
|
||
|
if c.ControlValue != "" {
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
|
||
|
}
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlString) String() string {
|
||
|
return fmt.Sprintf("Control Type: %s (%q) Criticality: %t Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
|
||
|
}
|
||
|
|
||
|
// ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
|
||
|
type ControlPaging struct {
|
||
|
// PagingSize indicates the page size
|
||
|
PagingSize uint32
|
||
|
// Cookie is an opaque value returned by the server to track a paging cursor
|
||
|
Cookie []byte
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlPaging) GetControlType() string {
|
||
|
return ControlTypePaging
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlPaging) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
|
||
|
|
||
|
p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
|
||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
|
||
|
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size"))
|
||
|
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
|
||
|
cookie.Value = c.Cookie
|
||
|
cookie.Data.Write(c.Cookie)
|
||
|
seq.AppendChild(cookie)
|
||
|
p2.AppendChild(seq)
|
||
|
|
||
|
packet.AppendChild(p2)
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlPaging) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t PagingSize: %d Cookie: %q",
|
||
|
ControlTypeMap[ControlTypePaging],
|
||
|
ControlTypePaging,
|
||
|
false,
|
||
|
c.PagingSize,
|
||
|
c.Cookie)
|
||
|
}
|
||
|
|
||
|
// SetCookie stores the given cookie in the paging control
|
||
|
func (c *ControlPaging) SetCookie(cookie []byte) {
|
||
|
c.Cookie = cookie
|
||
|
}
|
||
|
|
||
|
// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
|
||
|
type ControlBeheraPasswordPolicy struct {
|
||
|
// Expire contains the number of seconds before a password will expire
|
||
|
Expire int64
|
||
|
// Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password
|
||
|
Grace int64
|
||
|
// Error indicates the error code
|
||
|
Error int8
|
||
|
// ErrorString is a human readable error
|
||
|
ErrorString string
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlBeheraPasswordPolicy) GetControlType() string {
|
||
|
return ControlTypeBeheraPasswordPolicy
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlBeheraPasswordPolicy) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t Expire: %d Grace: %d Error: %d, ErrorString: %s",
|
||
|
ControlTypeMap[ControlTypeBeheraPasswordPolicy],
|
||
|
ControlTypeBeheraPasswordPolicy,
|
||
|
false,
|
||
|
c.Expire,
|
||
|
c.Grace,
|
||
|
c.Error,
|
||
|
c.ErrorString)
|
||
|
}
|
||
|
|
||
|
// ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
||
|
type ControlVChuPasswordMustChange struct {
|
||
|
// MustChange indicates if the password is required to be changed
|
||
|
MustChange bool
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlVChuPasswordMustChange) GetControlType() string {
|
||
|
return ControlTypeVChuPasswordMustChange
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlVChuPasswordMustChange) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t MustChange: %v",
|
||
|
ControlTypeMap[ControlTypeVChuPasswordMustChange],
|
||
|
ControlTypeVChuPasswordMustChange,
|
||
|
false,
|
||
|
c.MustChange)
|
||
|
}
|
||
|
|
||
|
// ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
|
||
|
type ControlVChuPasswordWarning struct {
|
||
|
// Expire indicates the time in seconds until the password expires
|
||
|
Expire int64
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlVChuPasswordWarning) GetControlType() string {
|
||
|
return ControlTypeVChuPasswordWarning
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlVChuPasswordWarning) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t Expire: %b",
|
||
|
ControlTypeMap[ControlTypeVChuPasswordWarning],
|
||
|
ControlTypeVChuPasswordWarning,
|
||
|
false,
|
||
|
c.Expire)
|
||
|
}
|
||
|
|
||
|
// ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
|
||
|
type ControlManageDsaIT struct {
|
||
|
// Criticality indicates if this control is required
|
||
|
Criticality bool
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlManageDsaIT) GetControlType() string {
|
||
|
return ControlTypeManageDsaIT
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlManageDsaIT) Encode() *ber.Packet {
|
||
|
// FIXME
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
|
||
|
if c.Criticality {
|
||
|
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
|
||
|
}
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlManageDsaIT) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t",
|
||
|
ControlTypeMap[ControlTypeManageDsaIT],
|
||
|
ControlTypeManageDsaIT,
|
||
|
c.Criticality)
|
||
|
}
|
||
|
|
||
|
// NewControlManageDsaIT returns a ControlManageDsaIT control
|
||
|
func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
|
||
|
return &ControlManageDsaIT{Criticality: Criticality}
|
||
|
}
|
||
|
|
||
|
// ControlMicrosoftNotification implements the control described in https://msdn.microsoft.com/en-us/library/aa366983(v=vs.85).aspx
|
||
|
type ControlMicrosoftNotification struct{}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlMicrosoftNotification) GetControlType() string {
|
||
|
return ControlTypeMicrosoftNotification
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlMicrosoftNotification) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftNotification, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftNotification]+")"))
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlMicrosoftNotification) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q)",
|
||
|
ControlTypeMap[ControlTypeMicrosoftNotification],
|
||
|
ControlTypeMicrosoftNotification)
|
||
|
}
|
||
|
|
||
|
// NewControlMicrosoftNotification returns a ControlMicrosoftNotification control
|
||
|
func NewControlMicrosoftNotification() *ControlMicrosoftNotification {
|
||
|
return &ControlMicrosoftNotification{}
|
||
|
}
|
||
|
|
||
|
// ControlMicrosoftShowDeleted implements the control described in https://msdn.microsoft.com/en-us/library/aa366989(v=vs.85).aspx
|
||
|
type ControlMicrosoftShowDeleted struct{}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlMicrosoftShowDeleted) GetControlType() string {
|
||
|
return ControlTypeMicrosoftShowDeleted
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlMicrosoftShowDeleted) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftShowDeleted, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftShowDeleted]+")"))
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlMicrosoftShowDeleted) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q)",
|
||
|
ControlTypeMap[ControlTypeMicrosoftShowDeleted],
|
||
|
ControlTypeMicrosoftShowDeleted)
|
||
|
}
|
||
|
|
||
|
// NewControlMicrosoftShowDeleted returns a ControlMicrosoftShowDeleted control
|
||
|
func NewControlMicrosoftShowDeleted() *ControlMicrosoftShowDeleted {
|
||
|
return &ControlMicrosoftShowDeleted{}
|
||
|
}
|
||
|
|
||
|
// ControlMicrosoftServerLinkTTL implements the control described in https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/f4f523a8-abc0-4b3a-a471-6b2fef135481?redirectedfrom=MSDN
|
||
|
type ControlMicrosoftServerLinkTTL struct{}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlMicrosoftServerLinkTTL) GetControlType() string {
|
||
|
return ControlTypeMicrosoftServerLinkTTL
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlMicrosoftServerLinkTTL) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeMicrosoftServerLinkTTL, "Control Type ("+ControlTypeMap[ControlTypeMicrosoftServerLinkTTL]+")"))
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlMicrosoftServerLinkTTL) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q)",
|
||
|
ControlTypeMap[ControlTypeMicrosoftServerLinkTTL],
|
||
|
ControlTypeMicrosoftServerLinkTTL)
|
||
|
}
|
||
|
|
||
|
// NewControlMicrosoftServerLinkTTL returns a ControlMicrosoftServerLinkTTL control
|
||
|
func NewControlMicrosoftServerLinkTTL() *ControlMicrosoftServerLinkTTL {
|
||
|
return &ControlMicrosoftServerLinkTTL{}
|
||
|
}
|
||
|
|
||
|
// FindControl returns the first control of the given type in the list, or nil
|
||
|
func FindControl(controls []Control, controlType string) Control {
|
||
|
for _, c := range controls {
|
||
|
if c.GetControlType() == controlType {
|
||
|
return c
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
|
||
|
func DecodeControl(packet *ber.Packet) (Control, error) {
|
||
|
var (
|
||
|
ControlType = ""
|
||
|
Criticality = false
|
||
|
value *ber.Packet
|
||
|
)
|
||
|
|
||
|
switch len(packet.Children) {
|
||
|
case 0:
|
||
|
// at least one child is required for control type
|
||
|
return nil, fmt.Errorf("at least one child is required for control type")
|
||
|
|
||
|
case 1:
|
||
|
// just type, no criticality or value
|
||
|
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
||
|
ControlType = packet.Children[0].Value.(string)
|
||
|
|
||
|
case 2:
|
||
|
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
||
|
if packet.Children[0].Value != nil {
|
||
|
ControlType = packet.Children[0].Value.(string)
|
||
|
} else if packet.Children[0].Data != nil {
|
||
|
ControlType = packet.Children[0].Data.String()
|
||
|
} else {
|
||
|
return nil, fmt.Errorf("not found where to get the control type")
|
||
|
}
|
||
|
|
||
|
// Children[1] could be criticality or value (both are optional)
|
||
|
// duck-type on whether this is a boolean
|
||
|
if _, ok := packet.Children[1].Value.(bool); ok {
|
||
|
packet.Children[1].Description = "Criticality"
|
||
|
Criticality = packet.Children[1].Value.(bool)
|
||
|
} else {
|
||
|
packet.Children[1].Description = "Control Value"
|
||
|
value = packet.Children[1]
|
||
|
}
|
||
|
|
||
|
case 3:
|
||
|
packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
|
||
|
ControlType = packet.Children[0].Value.(string)
|
||
|
|
||
|
packet.Children[1].Description = "Criticality"
|
||
|
Criticality = packet.Children[1].Value.(bool)
|
||
|
|
||
|
packet.Children[2].Description = "Control Value"
|
||
|
value = packet.Children[2]
|
||
|
|
||
|
default:
|
||
|
// more than 3 children is invalid
|
||
|
return nil, fmt.Errorf("more than 3 children is invalid for controls")
|
||
|
}
|
||
|
|
||
|
switch ControlType {
|
||
|
case ControlTypeManageDsaIT:
|
||
|
return NewControlManageDsaIT(Criticality), nil
|
||
|
case ControlTypePaging:
|
||
|
value.Description += " (Paging)"
|
||
|
c := new(ControlPaging)
|
||
|
if value.Value != nil {
|
||
|
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
value.Data.Truncate(0)
|
||
|
value.Value = nil
|
||
|
value.AppendChild(valueChildren)
|
||
|
}
|
||
|
value = value.Children[0]
|
||
|
value.Description = "Search Control Value"
|
||
|
value.Children[0].Description = "Paging Size"
|
||
|
value.Children[1].Description = "Cookie"
|
||
|
c.PagingSize = uint32(value.Children[0].Value.(int64))
|
||
|
c.Cookie = value.Children[1].Data.Bytes()
|
||
|
value.Children[1].Value = c.Cookie
|
||
|
return c, nil
|
||
|
case ControlTypeBeheraPasswordPolicy:
|
||
|
value.Description += " (Password Policy - Behera)"
|
||
|
c := NewControlBeheraPasswordPolicy()
|
||
|
if value.Value != nil {
|
||
|
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
value.Data.Truncate(0)
|
||
|
value.Value = nil
|
||
|
value.AppendChild(valueChildren)
|
||
|
}
|
||
|
|
||
|
sequence := value.Children[0]
|
||
|
|
||
|
for _, child := range sequence.Children {
|
||
|
if child.Tag == 0 {
|
||
|
// Warning
|
||
|
warningPacket := child.Children[0]
|
||
|
val, err := ber.ParseInt64(warningPacket.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
if warningPacket.Tag == 0 {
|
||
|
// timeBeforeExpiration
|
||
|
c.Expire = val
|
||
|
warningPacket.Value = c.Expire
|
||
|
} else if warningPacket.Tag == 1 {
|
||
|
// graceAuthNsRemaining
|
||
|
c.Grace = val
|
||
|
warningPacket.Value = c.Grace
|
||
|
}
|
||
|
} else if child.Tag == 1 {
|
||
|
// Error
|
||
|
bs := child.Data.Bytes()
|
||
|
if len(bs) != 1 || bs[0] > 8 {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", "invalid PasswordPolicyResponse enum value")
|
||
|
}
|
||
|
val := int8(bs[0])
|
||
|
c.Error = val
|
||
|
child.Value = c.Error
|
||
|
c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]
|
||
|
}
|
||
|
}
|
||
|
return c, nil
|
||
|
case ControlTypeVChuPasswordMustChange:
|
||
|
c := &ControlVChuPasswordMustChange{MustChange: true}
|
||
|
return c, nil
|
||
|
case ControlTypeVChuPasswordWarning:
|
||
|
c := &ControlVChuPasswordWarning{Expire: -1}
|
||
|
expireStr := ber.DecodeString(value.Data.Bytes())
|
||
|
|
||
|
expire, err := strconv.ParseInt(expireStr, 10, 64)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to parse value as int: %s", err)
|
||
|
}
|
||
|
c.Expire = expire
|
||
|
value.Value = c.Expire
|
||
|
|
||
|
return c, nil
|
||
|
case ControlTypeMicrosoftNotification:
|
||
|
return NewControlMicrosoftNotification(), nil
|
||
|
case ControlTypeMicrosoftShowDeleted:
|
||
|
return NewControlMicrosoftShowDeleted(), nil
|
||
|
case ControlTypeMicrosoftServerLinkTTL:
|
||
|
return NewControlMicrosoftServerLinkTTL(), nil
|
||
|
case ControlTypeSubtreeDelete:
|
||
|
return NewControlSubtreeDelete(), nil
|
||
|
case ControlTypeServerSideSorting:
|
||
|
return NewControlServerSideSorting(value)
|
||
|
case ControlTypeServerSideSortingResult:
|
||
|
return NewControlServerSideSortingResult(value)
|
||
|
case ControlTypeDirSync:
|
||
|
value.Description += " (DirSync)"
|
||
|
return NewResponseControlDirSync(value)
|
||
|
case ControlTypeSyncState:
|
||
|
value.Description += " (Sync State)"
|
||
|
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
return NewControlSyncState(valueChildren)
|
||
|
case ControlTypeSyncDone:
|
||
|
value.Description += " (Sync Done)"
|
||
|
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
return NewControlSyncDone(valueChildren)
|
||
|
case ControlTypeSyncInfo:
|
||
|
value.Description += " (Sync Info)"
|
||
|
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
return NewControlSyncInfo(valueChildren)
|
||
|
default:
|
||
|
c := new(ControlString)
|
||
|
c.ControlType = ControlType
|
||
|
c.Criticality = Criticality
|
||
|
if value != nil {
|
||
|
c.ControlValue = value.Value.(string)
|
||
|
}
|
||
|
return c, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewControlString returns a generic control
|
||
|
func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
|
||
|
return &ControlString{
|
||
|
ControlType: controlType,
|
||
|
Criticality: criticality,
|
||
|
ControlValue: controlValue,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewControlPaging returns a paging control
|
||
|
func NewControlPaging(pagingSize uint32) *ControlPaging {
|
||
|
return &ControlPaging{PagingSize: pagingSize}
|
||
|
}
|
||
|
|
||
|
// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
|
||
|
func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
|
||
|
return &ControlBeheraPasswordPolicy{
|
||
|
Expire: -1,
|
||
|
Grace: -1,
|
||
|
Error: -1,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ControlSubtreeDelete implements the subtree delete control described in
|
||
|
// https://datatracker.ietf.org/doc/html/draft-armijo-ldap-treedelete-02
|
||
|
type ControlSubtreeDelete struct{}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlSubtreeDelete) GetControlType() string {
|
||
|
return ControlTypeSubtreeDelete
|
||
|
}
|
||
|
|
||
|
// NewControlSubtreeDelete returns a ControlSubtreeDelete control.
|
||
|
func NewControlSubtreeDelete() *ControlSubtreeDelete {
|
||
|
return &ControlSubtreeDelete{}
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlSubtreeDelete) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeSubtreeDelete, "Control Type ("+ControlTypeMap[ControlTypeSubtreeDelete]+")"))
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
func (c *ControlSubtreeDelete) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q)",
|
||
|
ControlTypeMap[ControlTypeSubtreeDelete],
|
||
|
ControlTypeSubtreeDelete)
|
||
|
}
|
||
|
|
||
|
func encodeControls(controls []Control) *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
|
||
|
for _, control := range controls {
|
||
|
packet.AppendChild(control.Encode())
|
||
|
}
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// ControlDirSync implements the control described in https://msdn.microsoft.com/en-us/library/aa366978(v=vs.85).aspx
|
||
|
type ControlDirSync struct {
|
||
|
Criticality bool
|
||
|
Flags int64
|
||
|
MaxAttrCount int64
|
||
|
Cookie []byte
|
||
|
}
|
||
|
|
||
|
// @deprecated Use NewRequestControlDirSync instead
|
||
|
func NewControlDirSync(flags int64, maxAttrCount int64, cookie []byte) *ControlDirSync {
|
||
|
return NewRequestControlDirSync(flags, maxAttrCount, cookie)
|
||
|
}
|
||
|
|
||
|
// NewRequestControlDirSync returns a dir sync control
|
||
|
func NewRequestControlDirSync(
|
||
|
flags int64, maxAttrCount int64, cookie []byte,
|
||
|
) *ControlDirSync {
|
||
|
return &ControlDirSync{
|
||
|
Criticality: true,
|
||
|
Flags: flags,
|
||
|
MaxAttrCount: maxAttrCount,
|
||
|
Cookie: cookie,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NewResponseControlDirSync returns a dir sync control
|
||
|
func NewResponseControlDirSync(value *ber.Packet) (*ControlDirSync, error) {
|
||
|
if value.Value != nil {
|
||
|
valueChildren, err := ber.DecodePacketErr(value.Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode data bytes: %s", err)
|
||
|
}
|
||
|
value.Data.Truncate(0)
|
||
|
value.Value = nil
|
||
|
value.AppendChild(valueChildren)
|
||
|
}
|
||
|
child := value.Children[0]
|
||
|
if len(child.Children) != 3 { // also on initial creation, Cookie is an empty string
|
||
|
return nil, fmt.Errorf("invalid number of children in dirSync control")
|
||
|
}
|
||
|
child.Description = "DirSync Control Value"
|
||
|
child.Children[0].Description = "Flags"
|
||
|
child.Children[1].Description = "MaxAttrCount"
|
||
|
child.Children[2].Description = "Cookie"
|
||
|
|
||
|
cookie := child.Children[2].Data.Bytes()
|
||
|
child.Children[2].Value = cookie
|
||
|
return &ControlDirSync{
|
||
|
Criticality: true,
|
||
|
Flags: child.Children[0].Value.(int64),
|
||
|
MaxAttrCount: child.Children[1].Value.(int64),
|
||
|
Cookie: cookie,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlDirSync) GetControlType() string {
|
||
|
return ControlTypeDirSync
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlDirSync) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"ControlType: %s (%q) Criticality: %t ControlValue: Flags: %d MaxAttrCount: %d",
|
||
|
ControlTypeMap[ControlTypeDirSync],
|
||
|
ControlTypeDirSync,
|
||
|
c.Criticality,
|
||
|
c.Flags,
|
||
|
c.MaxAttrCount,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// Encode returns the ber packet representation
|
||
|
func (c *ControlDirSync) Encode() *ber.Packet {
|
||
|
cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Cookie")
|
||
|
if len(c.Cookie) != 0 {
|
||
|
cookie.Value = c.Cookie
|
||
|
cookie.Data.Write(c.Cookie)
|
||
|
}
|
||
|
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeDirSync, "Control Type ("+ControlTypeMap[ControlTypeDirSync]+")"))
|
||
|
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality")) // must be true always
|
||
|
|
||
|
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (DirSync)")
|
||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "DirSync Control Value")
|
||
|
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.Flags), "Flags"))
|
||
|
seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.MaxAttrCount), "MaxAttrCount"))
|
||
|
seq.AppendChild(cookie)
|
||
|
val.AppendChild(seq)
|
||
|
|
||
|
packet.AppendChild(val)
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// SetCookie stores the given cookie in the dirSync control
|
||
|
func (c *ControlDirSync) SetCookie(cookie []byte) {
|
||
|
c.Cookie = cookie
|
||
|
}
|
||
|
|
||
|
// ControlServerSideSorting
|
||
|
|
||
|
type SortKey struct {
|
||
|
Reverse bool
|
||
|
AttributeType string
|
||
|
MatchingRule string
|
||
|
}
|
||
|
|
||
|
type ControlServerSideSorting struct {
|
||
|
SortKeys []*SortKey
|
||
|
}
|
||
|
|
||
|
func (c *ControlServerSideSorting) GetControlType() string {
|
||
|
return ControlTypeServerSideSorting
|
||
|
}
|
||
|
|
||
|
func NewControlServerSideSorting(value *ber.Packet) (*ControlServerSideSorting, error) {
|
||
|
sortKeys := []*SortKey{}
|
||
|
|
||
|
val := value.Children[1].Children
|
||
|
|
||
|
if len(val) != 1 {
|
||
|
return nil, fmt.Errorf("no sequence value in packet")
|
||
|
}
|
||
|
|
||
|
sequences := val[0].Children
|
||
|
|
||
|
for i, sequence := range sequences {
|
||
|
sortKey := &SortKey{}
|
||
|
|
||
|
if len(sequence.Children) < 2 {
|
||
|
return nil, fmt.Errorf("attributeType or matchingRule is missing from sequence %d", i)
|
||
|
}
|
||
|
|
||
|
sortKey.AttributeType = sequence.Children[0].Value.(string)
|
||
|
sortKey.MatchingRule = sequence.Children[1].Value.(string)
|
||
|
|
||
|
if len(sequence.Children) == 3 {
|
||
|
sortKey.Reverse = sequence.Children[2].Value.(bool)
|
||
|
}
|
||
|
|
||
|
sortKeys = append(sortKeys, sortKey)
|
||
|
}
|
||
|
|
||
|
return &ControlServerSideSorting{SortKeys: sortKeys}, nil
|
||
|
}
|
||
|
|
||
|
func NewControlServerSideSortingWithSortKeys(sortKeys []*SortKey) *ControlServerSideSorting {
|
||
|
return &ControlServerSideSorting{SortKeys: sortKeys}
|
||
|
}
|
||
|
|
||
|
func (c *ControlServerSideSorting) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
control := ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.GetControlType(), "Control Type")
|
||
|
|
||
|
value := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value")
|
||
|
seqs := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "SortKeyList")
|
||
|
|
||
|
for _, f := range c.SortKeys {
|
||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "")
|
||
|
|
||
|
seq.AppendChild(
|
||
|
ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, f.AttributeType, "attributeType"),
|
||
|
)
|
||
|
seq.AppendChild(
|
||
|
ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, f.MatchingRule, "orderingRule"),
|
||
|
)
|
||
|
if f.Reverse {
|
||
|
seq.AppendChild(
|
||
|
ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, 1, f.Reverse, "reverseOrder"),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
seqs.AppendChild(seq)
|
||
|
}
|
||
|
|
||
|
value.AppendChild(seqs)
|
||
|
|
||
|
packet.AppendChild(control)
|
||
|
packet.AppendChild(value)
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
func (c *ControlServerSideSorting) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality:%t %+v",
|
||
|
"Server Side Sorting",
|
||
|
c.GetControlType(),
|
||
|
false,
|
||
|
c.SortKeys,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ControlServerSideSortingResponse
|
||
|
|
||
|
const (
|
||
|
ControlServerSideSortingCodeSuccess ControlServerSideSortingCode = 0
|
||
|
ControlServerSideSortingCodeOperationsError ControlServerSideSortingCode = 1
|
||
|
ControlServerSideSortingCodeTimeLimitExceeded ControlServerSideSortingCode = 2
|
||
|
ControlServerSideSortingCodeStrongAuthRequired ControlServerSideSortingCode = 8
|
||
|
ControlServerSideSortingCodeAdminLimitExceeded ControlServerSideSortingCode = 11
|
||
|
ControlServerSideSortingCodeNoSuchAttribute ControlServerSideSortingCode = 16
|
||
|
ControlServerSideSortingCodeInappropriateMatching ControlServerSideSortingCode = 18
|
||
|
ControlServerSideSortingCodeInsufficientAccessRights ControlServerSideSortingCode = 50
|
||
|
ControlServerSideSortingCodeBusy ControlServerSideSortingCode = 51
|
||
|
ControlServerSideSortingCodeUnwillingToPerform ControlServerSideSortingCode = 53
|
||
|
ControlServerSideSortingCodeOther ControlServerSideSortingCode = 80
|
||
|
)
|
||
|
|
||
|
var ControlServerSideSortingCodes = []ControlServerSideSortingCode{
|
||
|
ControlServerSideSortingCodeSuccess,
|
||
|
ControlServerSideSortingCodeOperationsError,
|
||
|
ControlServerSideSortingCodeTimeLimitExceeded,
|
||
|
ControlServerSideSortingCodeStrongAuthRequired,
|
||
|
ControlServerSideSortingCodeAdminLimitExceeded,
|
||
|
ControlServerSideSortingCodeNoSuchAttribute,
|
||
|
ControlServerSideSortingCodeInappropriateMatching,
|
||
|
ControlServerSideSortingCodeInsufficientAccessRights,
|
||
|
ControlServerSideSortingCodeBusy,
|
||
|
ControlServerSideSortingCodeUnwillingToPerform,
|
||
|
ControlServerSideSortingCodeOther,
|
||
|
}
|
||
|
|
||
|
type ControlServerSideSortingCode int64
|
||
|
|
||
|
// Valid test the code contained in the control against the ControlServerSideSortingCodes slice and return an error if the code is unknown.
|
||
|
func (c ControlServerSideSortingCode) Valid() error {
|
||
|
for _, validRet := range ControlServerSideSortingCodes {
|
||
|
if c == validRet {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
return fmt.Errorf("unknown return code : %d", c)
|
||
|
}
|
||
|
|
||
|
func NewControlServerSideSortingResult(pkt *ber.Packet) (*ControlServerSideSortingResult, error) {
|
||
|
control := &ControlServerSideSortingResult{}
|
||
|
|
||
|
if pkt == nil || len(pkt.Children) == 0 {
|
||
|
return nil, fmt.Errorf("bad packet")
|
||
|
}
|
||
|
|
||
|
codeInt, err := ber.ParseInt64(pkt.Children[0].Data.Bytes())
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
code := ControlServerSideSortingCode(codeInt)
|
||
|
if err := code.Valid(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return control, nil
|
||
|
}
|
||
|
|
||
|
type ControlServerSideSortingResult struct {
|
||
|
Criticality bool
|
||
|
|
||
|
Result ControlServerSideSortingCode
|
||
|
|
||
|
// Not populated for now. I can't get openldap to send me this value, so I think this is specific to other directory server
|
||
|
// AttributeType string
|
||
|
}
|
||
|
|
||
|
func (control *ControlServerSideSortingResult) GetControlType() string {
|
||
|
return ControlTypeServerSideSortingResult
|
||
|
}
|
||
|
|
||
|
func (c *ControlServerSideSortingResult) Encode() *ber.Packet {
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "SortResult sequence")
|
||
|
sortResult := ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, int64(c.Result), "SortResult")
|
||
|
packet.AppendChild(sortResult)
|
||
|
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
func (c *ControlServerSideSortingResult) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality:%t ResultCode:%+v",
|
||
|
"Server Side Sorting Result",
|
||
|
c.GetControlType(),
|
||
|
c.Criticality,
|
||
|
c.Result,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// Mode for ControlTypeSyncRequest
|
||
|
type ControlSyncRequestMode int64
|
||
|
|
||
|
const (
|
||
|
SyncRequestModeRefreshOnly ControlSyncRequestMode = 1
|
||
|
SyncRequestModeRefreshAndPersist ControlSyncRequestMode = 3
|
||
|
)
|
||
|
|
||
|
// ControlSyncRequest implements the Sync Request Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncRequest struct {
|
||
|
Criticality bool
|
||
|
Mode ControlSyncRequestMode
|
||
|
Cookie []byte
|
||
|
ReloadHint bool
|
||
|
}
|
||
|
|
||
|
func NewControlSyncRequest(
|
||
|
mode ControlSyncRequestMode, cookie []byte, reloadHint bool,
|
||
|
) *ControlSyncRequest {
|
||
|
return &ControlSyncRequest{
|
||
|
Criticality: true,
|
||
|
Mode: mode,
|
||
|
Cookie: cookie,
|
||
|
ReloadHint: reloadHint,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlSyncRequest) GetControlType() string {
|
||
|
return ControlTypeSyncRequest
|
||
|
}
|
||
|
|
||
|
// Encode encodes the control
|
||
|
func (c *ControlSyncRequest) Encode() *ber.Packet {
|
||
|
_mode := int64(c.Mode)
|
||
|
mode := ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, _mode, "Mode")
|
||
|
var cookie *ber.Packet
|
||
|
if len(c.Cookie) > 0 {
|
||
|
cookie = ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
|
||
|
cookie.Value = c.Cookie
|
||
|
cookie.Data.Write(c.Cookie)
|
||
|
}
|
||
|
reloadHint := ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.ReloadHint, "Reload Hint")
|
||
|
|
||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
|
||
|
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeSyncRequest, "Control Type ("+ControlTypeMap[ControlTypeSyncRequest]+")"))
|
||
|
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
|
||
|
|
||
|
val := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Sync Request)")
|
||
|
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Sync Request Value")
|
||
|
seq.AppendChild(mode)
|
||
|
if cookie != nil {
|
||
|
seq.AppendChild(cookie)
|
||
|
}
|
||
|
seq.AppendChild(reloadHint)
|
||
|
val.AppendChild(seq)
|
||
|
|
||
|
packet.AppendChild(val)
|
||
|
return packet
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncRequest) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t Mode: %d Cookie: %s ReloadHint: %t",
|
||
|
ControlTypeMap[ControlTypeSyncRequest],
|
||
|
ControlTypeSyncRequest,
|
||
|
c.Criticality,
|
||
|
c.Mode,
|
||
|
string(c.Cookie),
|
||
|
c.ReloadHint,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// State for ControlSyncState
|
||
|
type ControlSyncStateState int64
|
||
|
|
||
|
const (
|
||
|
SyncStatePresent ControlSyncStateState = 0
|
||
|
SyncStateAdd ControlSyncStateState = 1
|
||
|
SyncStateModify ControlSyncStateState = 2
|
||
|
SyncStateDelete ControlSyncStateState = 3
|
||
|
)
|
||
|
|
||
|
// ControlSyncState implements the Sync State Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncState struct {
|
||
|
Criticality bool
|
||
|
State ControlSyncStateState
|
||
|
EntryUUID uuid.UUID
|
||
|
Cookie []byte
|
||
|
}
|
||
|
|
||
|
func NewControlSyncState(pkt *ber.Packet) (*ControlSyncState, error) {
|
||
|
var (
|
||
|
state ControlSyncStateState
|
||
|
entryUUID uuid.UUID
|
||
|
cookie []byte
|
||
|
err error
|
||
|
)
|
||
|
switch len(pkt.Children) {
|
||
|
case 0, 1:
|
||
|
return nil, fmt.Errorf("at least two children are required: %d", len(pkt.Children))
|
||
|
case 2:
|
||
|
state = ControlSyncStateState(pkt.Children[0].Value.(int64))
|
||
|
entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode uuid: %w", err)
|
||
|
}
|
||
|
case 3:
|
||
|
state = ControlSyncStateState(pkt.Children[0].Value.(int64))
|
||
|
entryUUID, err = uuid.FromBytes(pkt.Children[1].ByteValue)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode uuid: %w", err)
|
||
|
}
|
||
|
cookie = pkt.Children[2].ByteValue
|
||
|
}
|
||
|
return &ControlSyncState{
|
||
|
Criticality: false,
|
||
|
State: state,
|
||
|
EntryUUID: entryUUID,
|
||
|
Cookie: cookie,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlSyncState) GetControlType() string {
|
||
|
return ControlTypeSyncState
|
||
|
}
|
||
|
|
||
|
// Encode encodes the control
|
||
|
func (c *ControlSyncState) Encode() *ber.Packet {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncState) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t State: %d EntryUUID: %s Cookie: %s",
|
||
|
ControlTypeMap[ControlTypeSyncState],
|
||
|
ControlTypeSyncState,
|
||
|
c.Criticality,
|
||
|
c.State,
|
||
|
c.EntryUUID.String(),
|
||
|
string(c.Cookie),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ControlSyncDone implements the Sync Done Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncDone struct {
|
||
|
Criticality bool
|
||
|
Cookie []byte
|
||
|
RefreshDeletes bool
|
||
|
}
|
||
|
|
||
|
func NewControlSyncDone(pkt *ber.Packet) (*ControlSyncDone, error) {
|
||
|
var (
|
||
|
cookie []byte
|
||
|
refreshDeletes bool
|
||
|
)
|
||
|
switch len(pkt.Children) {
|
||
|
case 0:
|
||
|
// have nothing to do
|
||
|
case 1:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
case 2:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
refreshDeletes = pkt.Children[1].Value.(bool)
|
||
|
}
|
||
|
return &ControlSyncDone{
|
||
|
Criticality: false,
|
||
|
Cookie: cookie,
|
||
|
RefreshDeletes: refreshDeletes,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlSyncDone) GetControlType() string {
|
||
|
return ControlTypeSyncDone
|
||
|
}
|
||
|
|
||
|
// Encode encodes the control
|
||
|
func (c *ControlSyncDone) Encode() *ber.Packet {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncDone) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t Cookie: %s RefreshDeletes: %t",
|
||
|
ControlTypeMap[ControlTypeSyncDone],
|
||
|
ControlTypeSyncDone,
|
||
|
c.Criticality,
|
||
|
string(c.Cookie),
|
||
|
c.RefreshDeletes,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// Tag For ControlSyncInfo
|
||
|
type ControlSyncInfoValue uint64
|
||
|
|
||
|
const (
|
||
|
SyncInfoNewcookie ControlSyncInfoValue = 0
|
||
|
SyncInfoRefreshDelete ControlSyncInfoValue = 1
|
||
|
SyncInfoRefreshPresent ControlSyncInfoValue = 2
|
||
|
SyncInfoSyncIdSet ControlSyncInfoValue = 3
|
||
|
)
|
||
|
|
||
|
// ControlSyncInfoNewCookie implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncInfoNewCookie struct {
|
||
|
Cookie []byte
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncInfoNewCookie) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"NewCookie[Cookie: %s]",
|
||
|
string(c.Cookie),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ControlSyncInfoRefreshDelete implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncInfoRefreshDelete struct {
|
||
|
Cookie []byte
|
||
|
RefreshDone bool
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncInfoRefreshDelete) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"RefreshDelete[Cookie: %s RefreshDone: %t]",
|
||
|
string(c.Cookie),
|
||
|
c.RefreshDone,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ControlSyncInfoRefreshPresent implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncInfoRefreshPresent struct {
|
||
|
Cookie []byte
|
||
|
RefreshDone bool
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncInfoRefreshPresent) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"RefreshPresent[Cookie: %s RefreshDone: %t]",
|
||
|
string(c.Cookie),
|
||
|
c.RefreshDone,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ControlSyncInfoSyncIdSet implements a part of syncInfoValue described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncInfoSyncIdSet struct {
|
||
|
Cookie []byte
|
||
|
RefreshDeletes bool
|
||
|
SyncUUIDs []uuid.UUID
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncInfoSyncIdSet) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"SyncIdSet[Cookie: %s RefreshDeletes: %t SyncUUIDs: %v]",
|
||
|
string(c.Cookie),
|
||
|
c.RefreshDeletes,
|
||
|
c.SyncUUIDs,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// ControlSyncInfo implements the Sync Info Control described in https://www.ietf.org/rfc/rfc4533.txt
|
||
|
type ControlSyncInfo struct {
|
||
|
Criticality bool
|
||
|
Value ControlSyncInfoValue
|
||
|
NewCookie *ControlSyncInfoNewCookie
|
||
|
RefreshDelete *ControlSyncInfoRefreshDelete
|
||
|
RefreshPresent *ControlSyncInfoRefreshPresent
|
||
|
SyncIdSet *ControlSyncInfoSyncIdSet
|
||
|
}
|
||
|
|
||
|
func NewControlSyncInfo(pkt *ber.Packet) (*ControlSyncInfo, error) {
|
||
|
var (
|
||
|
cookie []byte
|
||
|
refreshDone = true
|
||
|
refreshDeletes bool
|
||
|
syncUUIDs []uuid.UUID
|
||
|
)
|
||
|
c := &ControlSyncInfo{Criticality: false}
|
||
|
switch ControlSyncInfoValue(pkt.Identifier.Tag) {
|
||
|
case SyncInfoNewcookie:
|
||
|
c.Value = SyncInfoNewcookie
|
||
|
c.NewCookie = &ControlSyncInfoNewCookie{
|
||
|
Cookie: pkt.ByteValue,
|
||
|
}
|
||
|
case SyncInfoRefreshDelete:
|
||
|
c.Value = SyncInfoRefreshDelete
|
||
|
switch len(pkt.Children) {
|
||
|
case 0:
|
||
|
// have nothing to do
|
||
|
case 1:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
case 2:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
refreshDone = pkt.Children[1].Value.(bool)
|
||
|
}
|
||
|
c.RefreshDelete = &ControlSyncInfoRefreshDelete{
|
||
|
Cookie: cookie,
|
||
|
RefreshDone: refreshDone,
|
||
|
}
|
||
|
case SyncInfoRefreshPresent:
|
||
|
c.Value = SyncInfoRefreshPresent
|
||
|
switch len(pkt.Children) {
|
||
|
case 0:
|
||
|
// have nothing to do
|
||
|
case 1:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
case 2:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
refreshDone = pkt.Children[1].Value.(bool)
|
||
|
}
|
||
|
c.RefreshPresent = &ControlSyncInfoRefreshPresent{
|
||
|
Cookie: cookie,
|
||
|
RefreshDone: refreshDone,
|
||
|
}
|
||
|
case SyncInfoSyncIdSet:
|
||
|
c.Value = SyncInfoSyncIdSet
|
||
|
switch len(pkt.Children) {
|
||
|
case 0:
|
||
|
// have nothing to do
|
||
|
case 1:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
case 2:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
refreshDeletes = pkt.Children[1].Value.(bool)
|
||
|
case 3:
|
||
|
cookie = pkt.Children[0].ByteValue
|
||
|
refreshDeletes = pkt.Children[1].Value.(bool)
|
||
|
syncUUIDs = make([]uuid.UUID, 0, len(pkt.Children[2].Children))
|
||
|
for _, child := range pkt.Children[2].Children {
|
||
|
u, err := uuid.FromBytes(child.ByteValue)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("failed to decode uuid: %w", err)
|
||
|
}
|
||
|
syncUUIDs = append(syncUUIDs, u)
|
||
|
}
|
||
|
}
|
||
|
c.SyncIdSet = &ControlSyncInfoSyncIdSet{
|
||
|
Cookie: cookie,
|
||
|
RefreshDeletes: refreshDeletes,
|
||
|
SyncUUIDs: syncUUIDs,
|
||
|
}
|
||
|
default:
|
||
|
return nil, fmt.Errorf("unknown sync info value: %d", pkt.Identifier.Tag)
|
||
|
}
|
||
|
return c, nil
|
||
|
}
|
||
|
|
||
|
// GetControlType returns the OID
|
||
|
func (c *ControlSyncInfo) GetControlType() string {
|
||
|
return ControlTypeSyncInfo
|
||
|
}
|
||
|
|
||
|
// Encode encodes the control
|
||
|
func (c *ControlSyncInfo) Encode() *ber.Packet {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// String returns a human-readable description
|
||
|
func (c *ControlSyncInfo) String() string {
|
||
|
return fmt.Sprintf(
|
||
|
"Control Type: %s (%q) Criticality: %t Value: %d %s %s %s %s",
|
||
|
ControlTypeMap[ControlTypeSyncInfo],
|
||
|
ControlTypeSyncInfo,
|
||
|
c.Criticality,
|
||
|
c.Value,
|
||
|
c.NewCookie,
|
||
|
c.RefreshDelete,
|
||
|
c.RefreshPresent,
|
||
|
c.SyncIdSet,
|
||
|
)
|
||
|
}
|