github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/ldap.v2/control.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ldap
     6  
     7  import (
     8  	"fmt"
     9  	"strconv"
    10  
    11  	"gopkg.in/asn1-ber.v1"
    12  )
    13  
    14  const (
    15  	// ControlTypePaging - https://www.ietf.org/rfc/rfc2696.txt
    16  	ControlTypePaging = "1.2.840.113556.1.4.319"
    17  	// ControlTypeBeheraPasswordPolicy - https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
    18  	ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
    19  	// ControlTypeVChuPasswordMustChange - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
    20  	ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
    21  	// ControlTypeVChuPasswordWarning - https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
    22  	ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
    23  	// ControlTypeManageDsaIT - https://tools.ietf.org/html/rfc3296
    24  	ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
    25  )
    26  
    27  // ControlTypeMap maps controls to text descriptions
    28  var ControlTypeMap = map[string]string{
    29  	ControlTypePaging:               "Paging",
    30  	ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
    31  	ControlTypeManageDsaIT:          "Manage DSA IT",
    32  }
    33  
    34  // Control defines an interface controls provide to encode and describe themselves
    35  type Control interface {
    36  	// GetControlType returns the OID
    37  	GetControlType() string
    38  	// Encode returns the ber packet representation
    39  	Encode() *ber.Packet
    40  	// String returns a human-readable description
    41  	String() string
    42  }
    43  
    44  // ControlString implements the Control interface for simple controls
    45  type ControlString struct {
    46  	ControlType  string
    47  	Criticality  bool
    48  	ControlValue string
    49  }
    50  
    51  // GetControlType returns the OID
    52  func (c *ControlString) GetControlType() string {
    53  	return c.ControlType
    54  }
    55  
    56  // Encode returns the ber packet representation
    57  func (c *ControlString) Encode() *ber.Packet {
    58  	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
    59  	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.ControlType, "Control Type ("+ControlTypeMap[c.ControlType]+")"))
    60  	if c.Criticality {
    61  		packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
    62  	}
    63  	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(c.ControlValue), "Control Value"))
    64  	return packet
    65  }
    66  
    67  // String returns a human-readable description
    68  func (c *ControlString) String() string {
    69  	return fmt.Sprintf("Control Type: %s (%q)  Criticality: %t  Control Value: %s", ControlTypeMap[c.ControlType], c.ControlType, c.Criticality, c.ControlValue)
    70  }
    71  
    72  // ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
    73  type ControlPaging struct {
    74  	// PagingSize indicates the page size
    75  	PagingSize uint32
    76  	// Cookie is an opaque value returned by the server to track a paging cursor
    77  	Cookie []byte
    78  }
    79  
    80  // GetControlType returns the OID
    81  func (c *ControlPaging) GetControlType() string {
    82  	return ControlTypePaging
    83  }
    84  
    85  // Encode returns the ber packet representation
    86  func (c *ControlPaging) Encode() *ber.Packet {
    87  	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
    88  	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypePaging, "Control Type ("+ControlTypeMap[ControlTypePaging]+")"))
    89  
    90  	p2 := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value (Paging)")
    91  	seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Search Control Value")
    92  	seq.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, int64(c.PagingSize), "Paging Size"))
    93  	cookie := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Cookie")
    94  	cookie.Value = c.Cookie
    95  	cookie.Data.Write(c.Cookie)
    96  	seq.AppendChild(cookie)
    97  	p2.AppendChild(seq)
    98  
    99  	packet.AppendChild(p2)
   100  	return packet
   101  }
   102  
   103  // String returns a human-readable description
   104  func (c *ControlPaging) String() string {
   105  	return fmt.Sprintf(
   106  		"Control Type: %s (%q)  Criticality: %t  PagingSize: %d  Cookie: %q",
   107  		ControlTypeMap[ControlTypePaging],
   108  		ControlTypePaging,
   109  		false,
   110  		c.PagingSize,
   111  		c.Cookie)
   112  }
   113  
   114  // SetCookie stores the given cookie in the paging control
   115  func (c *ControlPaging) SetCookie(cookie []byte) {
   116  	c.Cookie = cookie
   117  }
   118  
   119  // ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
   120  type ControlBeheraPasswordPolicy struct {
   121  	// Expire contains the number of seconds before a password will expire
   122  	Expire int64
   123  	// Grace indicates the remaining number of times a user will be allowed to authenticate with an expired password
   124  	Grace int64
   125  	// Error indicates the error code
   126  	Error int8
   127  	// ErrorString is a human readable error
   128  	ErrorString string
   129  }
   130  
   131  // GetControlType returns the OID
   132  func (c *ControlBeheraPasswordPolicy) GetControlType() string {
   133  	return ControlTypeBeheraPasswordPolicy
   134  }
   135  
   136  // Encode returns the ber packet representation
   137  func (c *ControlBeheraPasswordPolicy) Encode() *ber.Packet {
   138  	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
   139  	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeBeheraPasswordPolicy, "Control Type ("+ControlTypeMap[ControlTypeBeheraPasswordPolicy]+")"))
   140  
   141  	return packet
   142  }
   143  
   144  // String returns a human-readable description
   145  func (c *ControlBeheraPasswordPolicy) String() string {
   146  	return fmt.Sprintf(
   147  		"Control Type: %s (%q)  Criticality: %t  Expire: %d  Grace: %d  Error: %d, ErrorString: %s",
   148  		ControlTypeMap[ControlTypeBeheraPasswordPolicy],
   149  		ControlTypeBeheraPasswordPolicy,
   150  		false,
   151  		c.Expire,
   152  		c.Grace,
   153  		c.Error,
   154  		c.ErrorString)
   155  }
   156  
   157  // ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
   158  type ControlVChuPasswordMustChange struct {
   159  	// MustChange indicates if the password is required to be changed
   160  	MustChange bool
   161  }
   162  
   163  // GetControlType returns the OID
   164  func (c *ControlVChuPasswordMustChange) GetControlType() string {
   165  	return ControlTypeVChuPasswordMustChange
   166  }
   167  
   168  // Encode returns the ber packet representation
   169  func (c *ControlVChuPasswordMustChange) Encode() *ber.Packet {
   170  	return nil
   171  }
   172  
   173  // String returns a human-readable description
   174  func (c *ControlVChuPasswordMustChange) String() string {
   175  	return fmt.Sprintf(
   176  		"Control Type: %s (%q)  Criticality: %t  MustChange: %v",
   177  		ControlTypeMap[ControlTypeVChuPasswordMustChange],
   178  		ControlTypeVChuPasswordMustChange,
   179  		false,
   180  		c.MustChange)
   181  }
   182  
   183  // ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
   184  type ControlVChuPasswordWarning struct {
   185  	// Expire indicates the time in seconds until the password expires
   186  	Expire int64
   187  }
   188  
   189  // GetControlType returns the OID
   190  func (c *ControlVChuPasswordWarning) GetControlType() string {
   191  	return ControlTypeVChuPasswordWarning
   192  }
   193  
   194  // Encode returns the ber packet representation
   195  func (c *ControlVChuPasswordWarning) Encode() *ber.Packet {
   196  	return nil
   197  }
   198  
   199  // String returns a human-readable description
   200  func (c *ControlVChuPasswordWarning) String() string {
   201  	return fmt.Sprintf(
   202  		"Control Type: %s (%q)  Criticality: %t  Expire: %b",
   203  		ControlTypeMap[ControlTypeVChuPasswordWarning],
   204  		ControlTypeVChuPasswordWarning,
   205  		false,
   206  		c.Expire)
   207  }
   208  
   209  // ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
   210  type ControlManageDsaIT struct {
   211  	// Criticality indicates if this control is required
   212  	Criticality bool
   213  }
   214  
   215  // GetControlType returns the OID
   216  func (c *ControlManageDsaIT) GetControlType() string {
   217  	return ControlTypeManageDsaIT
   218  }
   219  
   220  // Encode returns the ber packet representation
   221  func (c *ControlManageDsaIT) Encode() *ber.Packet {
   222  	//FIXME
   223  	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
   224  	packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
   225  	if c.Criticality {
   226  		packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
   227  	}
   228  	return packet
   229  }
   230  
   231  // String returns a human-readable description
   232  func (c *ControlManageDsaIT) String() string {
   233  	return fmt.Sprintf(
   234  		"Control Type: %s (%q)  Criticality: %t",
   235  		ControlTypeMap[ControlTypeManageDsaIT],
   236  		ControlTypeManageDsaIT,
   237  		c.Criticality)
   238  }
   239  
   240  // NewControlManageDsaIT returns a ControlManageDsaIT control
   241  func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
   242  	return &ControlManageDsaIT{Criticality: Criticality}
   243  }
   244  
   245  // FindControl returns the first control of the given type in the list, or nil
   246  func FindControl(controls []Control, controlType string) Control {
   247  	for _, c := range controls {
   248  		if c.GetControlType() == controlType {
   249  			return c
   250  		}
   251  	}
   252  	return nil
   253  }
   254  
   255  // DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
   256  func DecodeControl(packet *ber.Packet) Control {
   257  	var (
   258  		ControlType = ""
   259  		Criticality = false
   260  		value       *ber.Packet
   261  	)
   262  
   263  	switch len(packet.Children) {
   264  	case 0:
   265  		// at least one child is required for control type
   266  		return nil
   267  
   268  	case 1:
   269  		// just type, no criticality or value
   270  		packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
   271  		ControlType = packet.Children[0].Value.(string)
   272  
   273  	case 2:
   274  		packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
   275  		ControlType = packet.Children[0].Value.(string)
   276  
   277  		// Children[1] could be criticality or value (both are optional)
   278  		// duck-type on whether this is a boolean
   279  		if _, ok := packet.Children[1].Value.(bool); ok {
   280  			packet.Children[1].Description = "Criticality"
   281  			Criticality = packet.Children[1].Value.(bool)
   282  		} else {
   283  			packet.Children[1].Description = "Control Value"
   284  			value = packet.Children[1]
   285  		}
   286  
   287  	case 3:
   288  		packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")"
   289  		ControlType = packet.Children[0].Value.(string)
   290  
   291  		packet.Children[1].Description = "Criticality"
   292  		Criticality = packet.Children[1].Value.(bool)
   293  
   294  		packet.Children[2].Description = "Control Value"
   295  		value = packet.Children[2]
   296  
   297  	default:
   298  		// more than 3 children is invalid
   299  		return nil
   300  	}
   301  
   302  	switch ControlType {
   303  	case ControlTypeManageDsaIT:
   304  		return NewControlManageDsaIT(Criticality)
   305  	case ControlTypePaging:
   306  		value.Description += " (Paging)"
   307  		c := new(ControlPaging)
   308  		if value.Value != nil {
   309  			valueChildren := ber.DecodePacket(value.Data.Bytes())
   310  			value.Data.Truncate(0)
   311  			value.Value = nil
   312  			value.AppendChild(valueChildren)
   313  		}
   314  		value = value.Children[0]
   315  		value.Description = "Search Control Value"
   316  		value.Children[0].Description = "Paging Size"
   317  		value.Children[1].Description = "Cookie"
   318  		c.PagingSize = uint32(value.Children[0].Value.(int64))
   319  		c.Cookie = value.Children[1].Data.Bytes()
   320  		value.Children[1].Value = c.Cookie
   321  		return c
   322  	case ControlTypeBeheraPasswordPolicy:
   323  		value.Description += " (Password Policy - Behera)"
   324  		c := NewControlBeheraPasswordPolicy()
   325  		if value.Value != nil {
   326  			valueChildren := ber.DecodePacket(value.Data.Bytes())
   327  			value.Data.Truncate(0)
   328  			value.Value = nil
   329  			value.AppendChild(valueChildren)
   330  		}
   331  
   332  		sequence := value.Children[0]
   333  
   334  		for _, child := range sequence.Children {
   335  			if child.Tag == 0 {
   336  				//Warning
   337  				warningPacket := child.Children[0]
   338  				packet := ber.DecodePacket(warningPacket.Data.Bytes())
   339  				val, ok := packet.Value.(int64)
   340  				if ok {
   341  					if warningPacket.Tag == 0 {
   342  						//timeBeforeExpiration
   343  						c.Expire = val
   344  						warningPacket.Value = c.Expire
   345  					} else if warningPacket.Tag == 1 {
   346  						//graceAuthNsRemaining
   347  						c.Grace = val
   348  						warningPacket.Value = c.Grace
   349  					}
   350  				}
   351  			} else if child.Tag == 1 {
   352  				// Error
   353  				packet := ber.DecodePacket(child.Data.Bytes())
   354  				val, ok := packet.Value.(int8)
   355  				if !ok {
   356  					// what to do?
   357  					val = -1
   358  				}
   359  				c.Error = val
   360  				child.Value = c.Error
   361  				c.ErrorString = BeheraPasswordPolicyErrorMap[c.Error]
   362  			}
   363  		}
   364  		return c
   365  	case ControlTypeVChuPasswordMustChange:
   366  		c := &ControlVChuPasswordMustChange{MustChange: true}
   367  		return c
   368  	case ControlTypeVChuPasswordWarning:
   369  		c := &ControlVChuPasswordWarning{Expire: -1}
   370  		expireStr := ber.DecodeString(value.Data.Bytes())
   371  
   372  		expire, err := strconv.ParseInt(expireStr, 10, 64)
   373  		if err != nil {
   374  			return nil
   375  		}
   376  		c.Expire = expire
   377  		value.Value = c.Expire
   378  
   379  		return c
   380  	default:
   381  		c := new(ControlString)
   382  		c.ControlType = ControlType
   383  		c.Criticality = Criticality
   384  		if value != nil {
   385  			c.ControlValue = value.Value.(string)
   386  		}
   387  		return c
   388  	}
   389  }
   390  
   391  // NewControlString returns a generic control
   392  func NewControlString(controlType string, criticality bool, controlValue string) *ControlString {
   393  	return &ControlString{
   394  		ControlType:  controlType,
   395  		Criticality:  criticality,
   396  		ControlValue: controlValue,
   397  	}
   398  }
   399  
   400  // NewControlPaging returns a paging control
   401  func NewControlPaging(pagingSize uint32) *ControlPaging {
   402  	return &ControlPaging{PagingSize: pagingSize}
   403  }
   404  
   405  // NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
   406  func NewControlBeheraPasswordPolicy() *ControlBeheraPasswordPolicy {
   407  	return &ControlBeheraPasswordPolicy{
   408  		Expire: -1,
   409  		Grace:  -1,
   410  		Error:  -1,
   411  	}
   412  }
   413  
   414  func encodeControls(controls []Control) *ber.Packet {
   415  	packet := ber.Encode(ber.ClassContext, ber.TypeConstructed, 0, nil, "Controls")
   416  	for _, control := range controls {
   417  		packet.AppendChild(control.Encode())
   418  	}
   419  	return packet
   420  }