| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 | // Copyright 2011 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package ldapimport (	"fmt"	"strconv"	"gopkg.in/asn1-ber.v1")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")// ControlTypeMap maps controls to text descriptionsvar ControlTypeMap = map[string]string{	ControlTypePaging:               "Paging",	ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",	ControlTypeManageDsaIT:          "Manage DSA IT",}// Control defines an interface controls provide to encode and describe themselvestype 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 controlstype ControlString struct {	ControlType  string	Criticality  bool	ControlValue string}// GetControlType returns the OIDfunc (c *ControlString) GetControlType() string {	return c.ControlType}// Encode returns the ber packet representationfunc (c *ControlString) Encode() *ber.Packet {	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))	if c.Criticality {		packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))	}	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))	return packet}// String returns a human-readable descriptionfunc (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.txttype 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 OIDfunc (c *ControlPaging) GetControlType() string {	return ControlTypePaging}// Encode returns the ber packet representationfunc (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 descriptionfunc (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 controlfunc (c *ControlPaging) SetCookie(cookie []byte) {	c.Cookie = cookie}// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10type 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 OIDfunc (c *ControlBeheraPasswordPolicy) GetControlType() string {	return ControlTypeBeheraPasswordPolicy}// Encode returns the ber packet representationfunc (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 descriptionfunc (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-00type ControlVChuPasswordMustChange struct {	// MustChange indicates if the password is required to be changed	MustChange bool}// GetControlType returns the OIDfunc (c *ControlVChuPasswordMustChange) GetControlType() string {	return ControlTypeVChuPasswordMustChange}// Encode returns the ber packet representationfunc (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {	return nil}// String returns a human-readable descriptionfunc (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-00type ControlVChuPasswordWarning struct {	// Expire indicates the time in seconds until the password expires	Expire int64}// GetControlType returns the OIDfunc (c *ControlVChuPasswordWarning) GetControlType() string {	return ControlTypeVChuPasswordWarning}// Encode returns the ber packet representationfunc (c *ControlVChuPasswordWarning) Encode() *ber.Packet {	return nil}// String returns a human-readable descriptionfunc (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/rfc3296type ControlManageDsaIT struct {	// Criticality indicates if this control is required	Criticality bool}// GetControlType returns the OIDfunc (c *ControlManageDsaIT) GetControlType() string {	return ControlTypeManageDsaIT}// Encode returns the ber packet representationfunc (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 descriptionfunc (c *ControlManageDsaIT) String() string {	return fmt.Sprintf(		"Control Type: %s (%q)  Criticality: %t",		ControlTypeMap[ControlTypeManageDsaIT],		ControlTypeManageDsaIT,		c.Criticality)}// NewControlManageDsaIT returns a ControlManageDsaIT controlfunc NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {	return &ControlManageDsaIT{Criticality: Criticality}}// FindControl returns the first control of the given type in the list, or nilfunc 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 madefunc DecodeControl(packet *ber.Packet) Control {	var (		ControlType = ""		Criticality = false		value       *ber.Packet	)	switch len(packet.Children) {	case 0:		// at least one child is required for control type		return nil	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] + ")"		ControlType = packet.Children[0].Value.(string)		// 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	}	switch ControlType {	case ControlTypeManageDsaIT:		return NewControlManageDsaIT(Criticality)	case ControlTypePaging:		value.Description += " (Paging)"		c := new(ControlPaging)		if value.Value != nil {			valueChildren := ber.DecodePacket(value.Data.Bytes())			value.Data.Truncate(0)			value.Value = nil			value.AppendChild(valueChildren)		}		value = value.Children[0]		value.Description = "Search Control Value"		value.Children[0].Description = "Paging Size"		value.Children[1].Description = "Cookie"		c.PagingSize = uint32(value.Children[0].Value.(int64))		c.Cookie = value.Children[1].Data.Bytes()		value.Children[1].Value = c.Cookie		return c	case ControlTypeBeheraPasswordPolicy:		value.Description += " (Password Policy - Behera)"		c := NewControlBeheraPasswordPolicy()		if value.Value != nil {			valueChildren := ber.DecodePacket(value.Data.Bytes())			value.Data.Truncate(0)			value.Value = nil			value.AppendChild(valueChildren)		}		sequence := value.Children[0]		for _, child := range sequence.Children {			if child.Tag == 0 {				//Warning				child := child.Children[0]				packet := ber.DecodePacket(child.Data.Bytes())				val, ok := packet.Value.(int64)				if ok {					if child.Tag == 0 {						//timeBeforeExpiration						c.Expire = val						child.Value = c.Expire					} else if child.Tag == 1 {						//graceAuthNsRemaining						c.Grace = val						child.Value = c.Grace					}				}			} else if child.Tag == 1 {				// Error				packet := ber.DecodePacket(child.Data.Bytes())				val, ok := packet.Value.(int8)				if !ok {					// what to do?					val = -1				}				c.Error = val				child.Value = c.Error				c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]			}		}		return c	case ControlTypeVChuPasswordMustChange:		c := &ControlVChuPasswordMustChange{MustChange: true}		return c	case ControlTypeVChuPasswordWarning:		c := &ControlVChuPasswordWarning{Expire: -1}		expireStr := ber.DecodeString(value.Data.Bytes())		expire, err := strconv.ParseInt(expireStr, 10, 64)		if err != nil {			return nil		}		c.Expire = expire		value.Value = c.Expire		return c	default:		c := new(ControlString)		c.ControlType = ControlType		c.Criticality = Criticality		if value != nil {			c.ControlValue = value.Value.(string)		}		return c	}}// NewControlString returns a generic controlfunc NewControlString(controlType string, criticality bool, controlValue string) *ControlString {	return &ControlString{		ControlType:  controlType,		Criticality:  criticality,		ControlValue: controlValue,	}}// NewControlPaging returns a paging controlfunc NewControlPaging(pagingSize uint32) *ControlPaging {	return &ControlPaging{PagingSize: pagingSize}}// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicyfunc NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {	return &ControlBeheraPasswordPolicy{		Expire: -1,		Grace:  -1,		Error:  -1,	}}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}
 |