// 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.
// This package provides LDAP client functions.
package ldap
import (
"github.com/mmitton/asn1-ber"
"fmt"
"io/ioutil"
"os"
)
// LDAP Application Codes
const (
ApplicationBindRequest = 0
ApplicationBindResponse = 1
ApplicationUnbindRequest = 2
ApplicationSearchRequest = 3
ApplicationSearchResultEntry = 4
ApplicationSearchResultDone = 5
ApplicationModifyRequest = 6
ApplicationModifyResponse = 7
ApplicationAddRequest = 8
ApplicationAddResponse = 9
ApplicationDelRequest = 10
ApplicationDelResponse = 11
ApplicationModifyDNRequest = 12
ApplicationModifyDNResponse = 13
ApplicationCompareRequest = 14
ApplicationCompareResponse = 15
ApplicationAbandonRequest = 16
ApplicationSearchResultReference = 19
ApplicationExtendedRequest = 23
ApplicationExtendedResponse = 24
)
var ApplicationMap = map[ uint8 ] string {
ApplicationBindRequest : "Bind Request",
ApplicationBindResponse : "Bind Response",
ApplicationUnbindRequest : "Unbind Request",
ApplicationSearchRequest : "Search Request",
ApplicationSearchResultEntry : "Search Result Entry",
ApplicationSearchResultDone : "Search Result Done",
ApplicationModifyRequest : "Modify Request",
ApplicationModifyResponse : "Modify Response",
ApplicationAddRequest : "Add Request",
ApplicationAddResponse : "Add Response",
ApplicationDelRequest : "Del Request",
ApplicationDelResponse : "Del Response",
ApplicationModifyDNRequest : "Modify DN Request",
ApplicationModifyDNResponse : "Modify DN Response",
ApplicationCompareRequest : "Compare Request",
ApplicationCompareResponse : "Compare Response",
ApplicationAbandonRequest : "Abandon Request",
ApplicationSearchResultReference : "Search Result Reference",
ApplicationExtendedRequest : "Extended Request",
ApplicationExtendedResponse : "Extended Response",
}
// LDAP Result Codes
const (
LDAPResultSuccess = 0
LDAPResultOperationsError = 1
LDAPResultProtocolError = 2
LDAPResultTimeLimitExceeded = 3
LDAPResultSizeLimitExceeded = 4
LDAPResultCompareFalse = 5
LDAPResultCompareTrue = 6
LDAPResultAuthMethodNotSupported = 7
LDAPResultStrongAuthRequired = 8
LDAPResultReferral = 10
LDAPResultAdminLimitExceeded = 11
LDAPResultUnavailableCriticalExtension = 12
LDAPResultConfidentialityRequired = 13
LDAPResultSaslBindInProgress = 14
LDAPResultNoSuchAttribute = 16
LDAPResultUndefinedAttributeType = 17
LDAPResultInappropriateMatching = 18
LDAPResultConstraintViolation = 19
LDAPResultAttributeOrValueExists = 20
LDAPResultInvalidAttributeSyntax = 21
LDAPResultNoSuchObject = 32
LDAPResultAliasProblem = 33
LDAPResultInvalidDNSyntax = 34
LDAPResultAliasDereferencingProblem = 36
LDAPResultInappropriateAuthentication = 48
LDAPResultInvalidCredentials = 49
LDAPResultInsufficientAccessRights = 50
LDAPResultBusy = 51
LDAPResultUnavailable = 52
LDAPResultUnwillingToPerform = 53
LDAPResultLoopDetect = 54
LDAPResultNamingViolation = 64
LDAPResultObjectClassViolation = 65
LDAPResultNotAllowedOnNonLeaf = 66
LDAPResultNotAllowedOnRDN = 67
LDAPResultEntryAlreadyExists = 68
LDAPResultObjectClassModsProhibited = 69
LDAPResultAffectsMultipleDSAs = 71
LDAPResultOther = 80
ErrorNetwork = 200
ErrorFilterCompile = 201
ErrorFilterDecompile = 202
ErrorDebugging = 203
)
var LDAPResultCodeMap = map[uint8] string {
LDAPResultSuccess : "Success",
LDAPResultOperationsError : "Operations Error",
LDAPResultProtocolError : "Protocol Error",
LDAPResultTimeLimitExceeded : "Time Limit Exceeded",
LDAPResultSizeLimitExceeded : "Size Limit Exceeded",
LDAPResultCompareFalse : "Compare False",
LDAPResultCompareTrue : "Compare True",
LDAPResultAuthMethodNotSupported : "Auth Method Not Supported",
LDAPResultStrongAuthRequired : "Strong Auth Required",
LDAPResultReferral : "Referral",
LDAPResultAdminLimitExceeded : "Admin Limit Exceeded",
LDAPResultUnavailableCriticalExtension : "Unavailable Critical Extension",
LDAPResultConfidentialityRequired : "Confidentiality Required",
LDAPResultSaslBindInProgress : "Sasl Bind In Progress",
LDAPResultNoSuchAttribute : "No Such Attribute",
LDAPResultUndefinedAttributeType : "Undefined Attribute Type",
LDAPResultInappropriateMatching : "Inappropriate Matching",
LDAPResultConstraintViolation : "Constraint Violation",
LDAPResultAttributeOrValueExists : "Attribute Or Value Exists",
LDAPResultInvalidAttributeSyntax : "Invalid Attribute Syntax",
LDAPResultNoSuchObject : "No Such Object",
LDAPResultAliasProblem : "Alias Problem",
LDAPResultInvalidDNSyntax : "Invalid DN Syntax",
LDAPResultAliasDereferencingProblem : "Alias Dereferencing Problem",
LDAPResultInappropriateAuthentication : "Inappropriate Authentication",
LDAPResultInvalidCredentials : "Invalid Credentials",
LDAPResultInsufficientAccessRights : "Insufficient Access Rights",
LDAPResultBusy : "Busy",
LDAPResultUnavailable : "Unavailable",
LDAPResultUnwillingToPerform : "Unwilling To Perform",
LDAPResultLoopDetect : "Loop Detect",
LDAPResultNamingViolation : "Naming Violation",
LDAPResultObjectClassViolation : "Object Class Violation",
LDAPResultNotAllowedOnNonLeaf : "Not Allowed On Non Leaf",
LDAPResultNotAllowedOnRDN : "Not Allowed On RDN",
LDAPResultEntryAlreadyExists : "Entry Already Exists",
LDAPResultObjectClassModsProhibited : "Object Class Mods Prohibited",
LDAPResultAffectsMultipleDSAs : "Affects Multiple DSAs",
LDAPResultOther : "Other",
}
// Adds descriptions to an LDAP Response packet for debugging
func addLDAPDescriptions( packet *ber.Packet ) (err *Error) {
defer func() {
if r := recover(); r != nil {
err = NewError( ErrorDebugging, os.NewError( "Cannot process packet to add descriptions" ) )
}
}()
packet.Description = "LDAP Response"
packet.Children[ 0 ].Description = "Message ID";
application := packet.Children[ 1 ].Tag
packet.Children[ 1 ].Description = ApplicationMap[ application ]
switch application {
case ApplicationBindRequest:
addRequestDescriptions( packet )
case ApplicationBindResponse:
addDefaultLDAPResponseDescriptions( packet )
case ApplicationUnbindRequest:
addRequestDescriptions( packet )
case ApplicationSearchRequest:
addRequestDescriptions( packet )
case ApplicationSearchResultEntry:
packet.Children[ 1 ].Children[ 0 ].Description = "Object Name"
packet.Children[ 1 ].Children[ 1 ].Description = "Attributes"
for _, child := range packet.Children[ 1 ].Children[ 1 ].Children {
child.Description = "Attribute"
child.Children[ 0 ].Description = "Attribute Name"
child.Children[ 1 ].Description = "Attribute Values"
for _, grandchild := range child.Children[ 1 ].Children {
grandchild.Description = "Attribute Value"
}
}
if len( packet.Children ) == 3 {
addControlDescriptions( packet.Children[ 2 ] )
}
case ApplicationSearchResultDone:
addDefaultLDAPResponseDescriptions( packet )
case ApplicationModifyRequest:
addRequestDescriptions( packet )
case ApplicationModifyResponse:
case ApplicationAddRequest:
addRequestDescriptions( packet )
case ApplicationAddResponse:
case ApplicationDelRequest:
addRequestDescriptions( packet )
case ApplicationDelResponse:
case ApplicationModifyDNRequest:
addRequestDescriptions( packet )
case ApplicationModifyDNResponse:
case ApplicationCompareRequest:
addRequestDescriptions( packet )
case ApplicationCompareResponse:
case ApplicationAbandonRequest:
addRequestDescriptions( packet )
case ApplicationSearchResultReference:
case ApplicationExtendedRequest:
addRequestDescriptions( packet )
case ApplicationExtendedResponse:
}
return nil
}
func addControlDescriptions( packet *ber.Packet ) {
packet.Description = "Controls"
for _, child := range packet.Children {
child.Description = "Control"
child.Children[ 0 ].Description = "Control Type (" + ControlTypeMap[ child.Children[ 0 ].Value.(string) ] + ")"
value := child.Children[ 1 ]
if len( child.Children ) == 3 {
child.Children[ 1 ].Description = "Criticality"
value = child.Children[ 2 ]
}
value.Description = "Control Value"
switch child.Children[ 0 ].Value.(string) {
case ControlTypePaging:
value.Description += " (Paging)"
if value.Value != nil {
value_children := ber.DecodePacket( value.Data.Bytes() )
value.Data.Truncate( 0 )
value.Value = nil
value_children.Children[ 1 ].Value = value_children.Children[ 1 ].Data.Bytes()
value.AppendChild( value_children )
}
value.Children[ 0 ].Description = "Real Search Control Value"
value.Children[ 0 ].Children[ 0 ].Description = "Paging Size"
value.Children[ 0 ].Children[ 1 ].Description = "Cookie"
}
}
}
func addRequestDescriptions( packet *ber.Packet ) {
packet.Description = "LDAP Request"
packet.Children[ 0 ].Description = "Message ID"
packet.Children[ 1 ].Description = ApplicationMap[ packet.Children[ 1 ].Tag ];
if len( packet.Children ) == 3 {
addControlDescriptions( packet.Children[ 2 ] )
}
}
func addDefaultLDAPResponseDescriptions( packet *ber.Packet ) {
resultCode := packet.Children[ 1 ].Children[ 0 ].Value.(uint64)
packet.Children[ 1 ].Children[ 0 ].Description = "Result Code (" + LDAPResultCodeMap[ uint8(resultCode) ] + ")";
packet.Children[ 1 ].Children[ 1 ].Description = "Matched DN";
packet.Children[ 1 ].Children[ 2 ].Description = "Error Message";
if len( packet.Children[ 1 ].Children ) > 3 {
packet.Children[ 1 ].Children[ 3 ].Description = "Referral";
}
if len( packet.Children ) == 3 {
addControlDescriptions( packet.Children[ 2 ] )
}
}
func DebugBinaryFile( FileName string ) *Error {
file, err := ioutil.ReadFile( FileName )
if err != nil {
return NewError( ErrorDebugging, err )
}
ber.PrintBytes( file, "" )
packet := ber.DecodePacket( file )
addLDAPDescriptions( packet )
ber.PrintPacket( packet )
return nil
}
type Error struct {
Err os.Error
ResultCode uint8
}
func (e *Error) String() string {
return fmt.Sprintf( "LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[ e.ResultCode ], e.Err.String() )
}
func NewError( ResultCode uint8, Err os.Error ) (* Error) {
return &Error{ ResultCode: ResultCode, Err: Err }
}
func getLDAPResultCode( p *ber.Packet ) ( code uint8, description string ) {
if len( p.Children ) >= 2 {
response := p.Children[ 1 ]
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len( response.Children ) == 3 {
code = uint8(response.Children[ 0 ].Value.(uint64))
description = response.Children[ 2 ].Value.(string)
return
}
}
code = ErrorNetwork
description = "Invalid packet format"
return
}