mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
420 lines
13 KiB
420 lines
13 KiB
// 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 ldap |
|
|
|
import ( |
|
"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 descriptions |
|
var 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 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")) |
|
} |
|
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} |
|
} |
|
|
|
// 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 { |
|
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 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, |
|
} |
|
} |
|
|
|
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 |
|
}
|
|
|