2015-05-07 17:59:38 +00:00
package ldap
import (
"fmt"
2015-06-29 21:50:55 +00:00
"strconv"
2015-05-07 17:59:38 +00:00
2015-06-29 21:50:55 +00:00
"gopkg.in/asn1-ber.v1"
2015-05-07 17:59:38 +00:00
)
const (
2016-07-23 00:11:47 +00:00
// 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
2015-06-29 21:50:55 +00:00
ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
2016-07-23 00:11:47 +00:00
// 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"
2015-05-07 17:59:38 +00:00
)
2016-07-23 00:11:47 +00:00
// ControlTypeMap maps controls to text descriptions
2015-05-07 17:59:38 +00:00
var ControlTypeMap = map [ string ] string {
2015-06-29 21:50:55 +00:00
ControlTypePaging : "Paging" ,
ControlTypeBeheraPasswordPolicy : "Password Policy - Behera Draft" ,
2015-10-07 20:10:00 +00:00
ControlTypeManageDsaIT : "Manage DSA IT" ,
2015-05-07 17:59:38 +00:00
}
2016-07-23 00:11:47 +00:00
// Control defines an interface controls provide to encode and describe themselves
2015-05-07 17:59:38 +00:00
type Control interface {
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-05-07 17:59:38 +00:00
GetControlType ( ) string
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-05-07 17:59:38 +00:00
Encode ( ) * ber . Packet
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-05-07 17:59:38 +00:00
String ( ) string
}
2016-07-23 00:11:47 +00:00
// ControlString implements the Control interface for simple controls
2015-05-07 17:59:38 +00:00
type ControlString struct {
ControlType string
Criticality bool
ControlValue string
}
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-05-07 17:59:38 +00:00
func ( c * ControlString ) GetControlType ( ) string {
return c . ControlType
}
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-05-07 17:59:38 +00:00
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" ) )
}
2015-06-29 21:50:55 +00:00
packet . AppendChild ( ber . NewString ( ber . ClassUniversal , ber . TypePrimitive , ber . TagOctetString , string ( c . ControlValue ) , "Control Value" ) )
2015-05-07 17:59:38 +00:00
return packet
}
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-05-07 17:59:38 +00:00
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 )
}
2016-07-23 00:11:47 +00:00
// ControlPaging implements the paging control described in https://www.ietf.org/rfc/rfc2696.txt
2015-05-07 17:59:38 +00:00
type ControlPaging struct {
2016-07-23 00:11:47 +00:00
// PagingSize indicates the page size
2015-05-07 17:59:38 +00:00
PagingSize uint32
2016-07-23 00:11:47 +00:00
// Cookie is an opaque value returned by the server to track a paging cursor
Cookie [ ] byte
2015-05-07 17:59:38 +00:00
}
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-05-07 17:59:38 +00:00
func ( c * ControlPaging ) GetControlType ( ) string {
return ControlTypePaging
}
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-05-07 17:59:38 +00:00
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" )
2016-09-02 22:05:09 +00:00
seq . AppendChild ( ber . NewInteger ( ber . ClassUniversal , ber . TypePrimitive , ber . TagInteger , int64 ( c . PagingSize ) , "Paging Size" ) )
2015-05-07 17:59:38 +00:00
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
}
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-05-07 17:59:38 +00:00
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 )
}
2016-07-23 00:11:47 +00:00
// SetCookie stores the given cookie in the paging control
2015-05-07 17:59:38 +00:00
func ( c * ControlPaging ) SetCookie ( cookie [ ] byte ) {
c . Cookie = cookie
}
2016-07-23 00:11:47 +00:00
// ControlBeheraPasswordPolicy implements the control described in https://tools.ietf.org/html/draft-behera-ldap-password-policy-10
2015-06-29 21:50:55 +00:00
type ControlBeheraPasswordPolicy struct {
2016-07-23 00:11:47 +00:00
// 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
2015-06-29 21:50:55 +00:00
ErrorString string
}
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-06-29 21:50:55 +00:00
func ( c * ControlBeheraPasswordPolicy ) GetControlType ( ) string {
return ControlTypeBeheraPasswordPolicy
}
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-06-29 21:50:55 +00:00
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
}
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-06-29 21:50:55 +00:00
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 )
}
2016-07-23 00:11:47 +00:00
// ControlVChuPasswordMustChange implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
2015-06-29 21:50:55 +00:00
type ControlVChuPasswordMustChange struct {
2016-07-23 00:11:47 +00:00
// MustChange indicates if the password is required to be changed
2015-06-29 21:50:55 +00:00
MustChange bool
}
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-06-29 21:50:55 +00:00
func ( c * ControlVChuPasswordMustChange ) GetControlType ( ) string {
return ControlTypeVChuPasswordMustChange
}
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-06-29 21:50:55 +00:00
func ( c * ControlVChuPasswordMustChange ) Encode ( ) * ber . Packet {
return nil
}
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-06-29 21:50:55 +00:00
func ( c * ControlVChuPasswordMustChange ) String ( ) string {
return fmt . Sprintf (
2016-07-23 00:11:47 +00:00
"Control Type: %s (%q) Criticality: %t MustChange: %v" ,
2015-06-29 21:50:55 +00:00
ControlTypeMap [ ControlTypeVChuPasswordMustChange ] ,
ControlTypeVChuPasswordMustChange ,
false ,
c . MustChange )
}
2016-07-23 00:11:47 +00:00
// ControlVChuPasswordWarning implements the control described in https://tools.ietf.org/html/draft-vchu-ldap-pwd-policy-00
2015-06-29 21:50:55 +00:00
type ControlVChuPasswordWarning struct {
2016-07-23 00:11:47 +00:00
// Expire indicates the time in seconds until the password expires
2015-06-29 21:50:55 +00:00
Expire int64
}
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-06-29 21:50:55 +00:00
func ( c * ControlVChuPasswordWarning ) GetControlType ( ) string {
return ControlTypeVChuPasswordWarning
}
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-06-29 21:50:55 +00:00
func ( c * ControlVChuPasswordWarning ) Encode ( ) * ber . Packet {
return nil
}
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-06-29 21:50:55 +00:00
func ( c * ControlVChuPasswordWarning ) String ( ) string {
return fmt . Sprintf (
"Control Type: %s (%q) Criticality: %t Expire: %b" ,
ControlTypeMap [ ControlTypeVChuPasswordWarning ] ,
ControlTypeVChuPasswordWarning ,
false ,
c . Expire )
}
2016-07-23 00:11:47 +00:00
// ControlManageDsaIT implements the control described in https://tools.ietf.org/html/rfc3296
2015-10-07 20:10:00 +00:00
type ControlManageDsaIT struct {
2016-07-23 00:11:47 +00:00
// Criticality indicates if this control is required
2015-10-07 20:10:00 +00:00
Criticality bool
}
2016-07-23 00:11:47 +00:00
// GetControlType returns the OID
2015-10-07 20:10:00 +00:00
func ( c * ControlManageDsaIT ) GetControlType ( ) string {
return ControlTypeManageDsaIT
}
2016-07-23 00:11:47 +00:00
// Encode returns the ber packet representation
2015-10-07 20:10:00 +00:00
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
}
2016-07-23 00:11:47 +00:00
// String returns a human-readable description
2015-10-07 20:10:00 +00:00
func ( c * ControlManageDsaIT ) String ( ) string {
return fmt . Sprintf (
"Control Type: %s (%q) Criticality: %t" ,
ControlTypeMap [ ControlTypeManageDsaIT ] ,
ControlTypeManageDsaIT ,
c . Criticality )
}
2016-07-23 00:11:47 +00:00
// NewControlManageDsaIT returns a ControlManageDsaIT control
2015-10-07 20:10:00 +00:00
func NewControlManageDsaIT ( Criticality bool ) * ControlManageDsaIT {
return & ControlManageDsaIT { Criticality : Criticality }
}
2016-07-23 00:11:47 +00:00
// FindControl returns the first control of the given type in the list, or nil
2015-05-07 17:59:38 +00:00
func FindControl ( controls [ ] Control , controlType string ) Control {
for _ , c := range controls {
if c . GetControlType ( ) == controlType {
return c
}
}
return nil
}
2016-07-23 00:11:47 +00:00
// DecodeControl returns a control read from the given packet, or nil if no recognized control can be made
2018-06-15 17:13:57 +00:00
func DecodeControl ( packet * ber . Packet ) ( Control , error ) {
2016-09-02 22:05:09 +00:00
var (
ControlType = ""
Criticality = false
value * ber . Packet
)
switch len ( packet . Children ) {
case 0 :
// at least one child is required for control type
2018-06-15 17:13:57 +00:00
return nil , fmt . Errorf ( "at least one child is required for control type" )
2016-09-02 22:05:09 +00:00
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 )
2015-05-07 17:59:38 +00:00
packet . Children [ 1 ] . Description = "Criticality"
Criticality = packet . Children [ 1 ] . Value . ( bool )
2016-09-02 22:05:09 +00:00
packet . Children [ 2 ] . Description = "Control Value"
value = packet . Children [ 2 ]
default :
// more than 3 children is invalid
2018-06-15 17:13:57 +00:00
return nil , fmt . Errorf ( "more than 3 children is invalid for controls" )
2015-05-07 17:59:38 +00:00
}
switch ControlType {
2016-09-02 22:05:09 +00:00
case ControlTypeManageDsaIT :
2018-06-15 17:13:57 +00:00
return NewControlManageDsaIT ( Criticality ) , nil
2015-05-07 17:59:38 +00:00
case ControlTypePaging :
value . Description += " (Paging)"
c := new ( ControlPaging )
if value . Value != nil {
2018-06-15 17:13:57 +00:00
valueChildren , err := ber . DecodePacketErr ( value . Data . Bytes ( ) )
if err != nil {
return nil , fmt . Errorf ( "failed to decode data bytes: %s" , err )
}
2015-05-07 17:59:38 +00:00
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
2018-06-15 17:13:57 +00:00
return c , nil
2015-06-29 21:50:55 +00:00
case ControlTypeBeheraPasswordPolicy :
value . Description += " (Password Policy - Behera)"
c := NewControlBeheraPasswordPolicy ( )
if value . Value != nil {
2018-06-15 17:13:57 +00:00
valueChildren , err := ber . DecodePacketErr ( value . Data . Bytes ( ) )
if err != nil {
return nil , fmt . Errorf ( "failed to decode data bytes: %s" , err )
}
2015-06-29 21:50:55 +00:00
value . Data . Truncate ( 0 )
value . Value = nil
value . AppendChild ( valueChildren )
}
sequence := value . Children [ 0 ]
for _ , child := range sequence . Children {
if child . Tag == 0 {
//Warning
2016-09-30 13:50:46 +00:00
warningPacket := child . Children [ 0 ]
2018-06-15 17:13:57 +00:00
packet , err := ber . DecodePacketErr ( warningPacket . Data . Bytes ( ) )
if err != nil {
return nil , fmt . Errorf ( "failed to decode data bytes: %s" , err )
}
2015-06-29 21:50:55 +00:00
val , ok := packet . Value . ( int64 )
if ok {
2016-09-30 13:50:46 +00:00
if warningPacket . Tag == 0 {
2015-06-29 21:50:55 +00:00
//timeBeforeExpiration
c . Expire = val
2016-09-30 13:50:46 +00:00
warningPacket . Value = c . Expire
} else if warningPacket . Tag == 1 {
2015-06-29 21:50:55 +00:00
//graceAuthNsRemaining
c . Grace = val
2016-09-30 13:50:46 +00:00
warningPacket . Value = c . Grace
2015-06-29 21:50:55 +00:00
}
}
} else if child . Tag == 1 {
// Error
2018-06-15 17:13:57 +00:00
packet , err := ber . DecodePacketErr ( child . Data . Bytes ( ) )
if err != nil {
return nil , fmt . Errorf ( "failed to decode data bytes: %s" , err )
}
2015-06-29 21:50:55 +00:00
val , ok := packet . Value . ( int8 )
if ! ok {
// what to do?
val = - 1
}
c . Error = val
child . Value = c . Error
c . ErrorString = BeheraPasswordPolicyErrorMap [ c . Error ]
}
}
2018-06-15 17:13:57 +00:00
return c , nil
2015-06-29 21:50:55 +00:00
case ControlTypeVChuPasswordMustChange :
c := & ControlVChuPasswordMustChange { MustChange : true }
2018-06-15 17:13:57 +00:00
return c , nil
2015-06-29 21:50:55 +00:00
case ControlTypeVChuPasswordWarning :
c := & ControlVChuPasswordWarning { Expire : - 1 }
expireStr := ber . DecodeString ( value . Data . Bytes ( ) )
expire , err := strconv . ParseInt ( expireStr , 10 , 64 )
if err != nil {
2018-06-15 17:13:57 +00:00
return nil , fmt . Errorf ( "failed to parse value as int: %s" , err )
2015-06-29 21:50:55 +00:00
}
c . Expire = expire
value . Value = c . Expire
2018-06-15 17:13:57 +00:00
return c , nil
2016-09-02 22:05:09 +00:00
default :
c := new ( ControlString )
c . ControlType = ControlType
c . Criticality = Criticality
if value != nil {
c . ControlValue = value . Value . ( string )
}
2018-06-15 17:13:57 +00:00
return c , nil
2015-05-07 17:59:38 +00:00
}
}
2016-07-23 00:11:47 +00:00
// NewControlString returns a generic control
2015-05-07 17:59:38 +00:00
func NewControlString ( controlType string , criticality bool , controlValue string ) * ControlString {
return & ControlString {
ControlType : controlType ,
Criticality : criticality ,
ControlValue : controlValue ,
}
}
2016-07-23 00:11:47 +00:00
// NewControlPaging returns a paging control
2015-05-07 17:59:38 +00:00
func NewControlPaging ( pagingSize uint32 ) * ControlPaging {
return & ControlPaging { PagingSize : pagingSize }
}
2016-07-23 00:11:47 +00:00
// NewControlBeheraPasswordPolicy returns a ControlBeheraPasswordPolicy
2015-06-29 21:50:55 +00:00
func NewControlBeheraPasswordPolicy ( ) * ControlBeheraPasswordPolicy {
return & ControlBeheraPasswordPolicy {
Expire : - 1 ,
Grace : - 1 ,
Error : - 1 ,
}
}
2015-05-07 17:59:38 +00:00
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
}