LDAP ASN.1 codec
LDAP ASN.1 Grammar
The LDAP ASN.1 Grammar is described in RFC 2251. Here is the grammar :
Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18} -- Copyright (C) The Internet Society (2006). This version of -- this ASN.1 module is part of RFC 4511; see the RFC itself -- for full legal notices. DEFINITIONS IMPLICIT TAGS EXTENSIBILITY IMPLIED ::= BEGIN LDAPMessage ::= SEQUENCE { messageID MessageID, protocolOp CHOICE { bindRequest BindRequest, bindResponse BindResponse, unbindRequest UnbindRequest, searchRequest SearchRequest, searchResEntry SearchResultEntry, searchResDone SearchResultDone, searchResRef SearchResultReference, modifyRequest ModifyRequest, modifyResponse ModifyResponse, addRequest AddRequest, addResponse AddResponse, delRequest DelRequest, delResponse DelResponse, modDNRequest ModifyDNRequest, modDNResponse ModifyDNResponse, compareRequest CompareRequest, compareResponse CompareResponse, abandonRequest AbandonRequest, extendedReq ExtendedRequest, extendedResp ExtendedResponse, ..., intermediateResponse IntermediateResponse }, controls [0] Controls OPTIONAL } MessageID ::= INTEGER (0 .. maxInt) maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- LDAPString ::= OCTET STRING -- UTF-8 encoded, -- [ISO10646] characters LDAPOID ::= OCTET STRING -- Constrained to <numericoid> -- [RFC4512] LDAPDN ::= LDAPString -- Constrained to <distinguishedName> -- [RFC4514] RelativeLDAPDN ::= LDAPString -- Constrained to <name-component> -- [RFC4514] AttributeDescription ::= LDAPString -- Constrained to <attributedescription> -- [RFC4512] AttributeValue ::= OCTET STRING AttributeValueAssertion ::= SEQUENCE { attributeDesc AttributeDescription, assertionValue AssertionValue } AssertionValue ::= OCTET STRING PartialAttribute ::= SEQUENCE { type AttributeDescription, vals SET OF value AttributeValue } Attribute ::= PartialAttribute(WITH COMPONENTS { ..., vals (SIZE(1..MAX))}) MatchingRuleId ::= LDAPString LDAPResult ::= SEQUENCE { resultCode ENUMERATED { success (0), operationsError (1), protocolError (2), timeLimitExceeded (3), sizeLimitExceeded (4), compareFalse (5), compareTrue (6), authMethodNotSupported (7), strongerAuthRequired (8), -- 9 reserved -- referral (10), adminLimitExceeded (11), unavailableCriticalExtension (12), confidentialityRequired (13), saslBindInProgress (14), noSuchAttribute (16), undefinedAttributeType (17), inappropriateMatching (18), constraintViolation (19), attributeOrValueExists (20), invalidAttributeSyntax (21), -- 22-31 unused -- noSuchObject (32), aliasProblem (33), invalidDNSyntax (34), -- 35 reserved for undefined isLeaf -- aliasDereferencingProblem (36), -- 37-47 unused -- inappropriateAuthentication (48), invalidCredentials (49), insufficientAccessRights (50), busy (51), unavailable (52), unwillingToPerform (53), loopDetect (54), -- 55-63 unused -- namingViolation (64), objectClassViolation (65), notAllowedOnNonLeaf (66), notAllowedOnRDN (67), entryAlreadyExists (68), objectClassModsProhibited (69), -- 70 reserved for CLDAP -- affectsMultipleDSAs (71), -- 72-79 unused -- other (80), ... }, matchedDN LDAPDN, diagnosticMessage LDAPString, referral [3] Referral OPTIONAL } Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI URI ::= LDAPString -- limited to characters permitted in -- URIs Controls ::= SEQUENCE OF control Control Control ::= SEQUENCE { controlType LDAPOID, criticality BOOLEAN DEFAULT FALSE, controlValue OCTET STRING OPTIONAL } BindRequest ::= [APPLICATION 0] SEQUENCE { version INTEGER (1 .. 127), name LDAPDN, authentication AuthenticationChoice } AuthenticationChoice ::= CHOICE { simple [0] OCTET STRING, -- 1 and 2 reserved sasl [3] SaslCredentials, ... } SaslCredentials ::= SEQUENCE { mechanism LDAPString, credentials OCTET STRING OPTIONAL } BindResponse ::= [APPLICATION 1] SEQUENCE { COMPONENTS OF LDAPResult, serverSaslCreds [7] OCTET STRING OPTIONAL } UnbindRequest ::= [APPLICATION 2] NULL SearchRequest ::= [APPLICATION 3] SEQUENCE { baseObject LDAPDN, scope ENUMERATED { baseObject (0), singleLevel (1), wholeSubtree (2), ... }, derefAliases ENUMERATED { neverDerefAliases (0), derefInSearching (1), derefFindingBaseObj (2), derefAlways (3) }, sizeLimit INTEGER (0 .. maxInt), timeLimit INTEGER (0 .. maxInt), typesOnly BOOLEAN, filter Filter, attributes AttributeSelection } AttributeSelection ::= SEQUENCE OF selector LDAPString -- The LDAPString is constrained to -- <attributeSelector> in Section 4.5.1.8 Filter ::= CHOICE { and [0] SET SIZE (1..MAX) OF filter Filter, or [1] SET SIZE (1..MAX) OF filter Filter, not [2] Filter, equalityMatch [3] AttributeValueAssertion, substrings [4] SubstringFilter, greaterOrEqual [5] AttributeValueAssertion, lessOrEqual [6] AttributeValueAssertion, present [7] AttributeDescription, approxMatch [8] AttributeValueAssertion, extensibleMatch [9] MatchingRuleAssertion, ... } SubstringFilter ::= SEQUENCE { type AttributeDescription, substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { initial [0] AssertionValue, -- can occur at most once any [1] AssertionValue, final [2] AssertionValue } -- can occur at most once } MatchingRuleAssertion ::= SEQUENCE { matchingRule [1] MatchingRuleId OPTIONAL, type [2] AttributeDescription OPTIONAL, matchValue [3] AssertionValue, dnAttributes [4] BOOLEAN DEFAULT FALSE } SearchResultEntry ::= [APPLICATION 4] SEQUENCE { objectName LDAPDN, attributes PartialAttributeList } PartialAttributeList ::= SEQUENCE OF partialAttribute PartialAttribute SearchResultReference ::= [APPLICATION 19] SEQUENCE SIZE (1..MAX) OF uri URI SearchResultDone ::= [APPLICATION 5] LDAPResult ModifyRequest ::= [APPLICATION 6] SEQUENCE { object LDAPDN, changes SEQUENCE OF change SEQUENCE { operation ENUMERATED { add (0), delete (1), replace (2), ... }, modification PartialAttribute } } ModifyResponse ::= [APPLICATION 7] LDAPResult AddRequest ::= [APPLICATION 8] SEQUENCE { entry LDAPDN, attributes AttributeList } AttributeList ::= SEQUENCE OF attribute Attribute AddResponse ::= [APPLICATION 9] LDAPResult DelRequest ::= [APPLICATION 10] LDAPDN DelResponse ::= [APPLICATION 11] LDAPResult ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { entry LDAPDN, newrdn RelativeLDAPDN, deleteoldrdn BOOLEAN, newSuperior [0] LDAPDN OPTIONAL } ModifyDNResponse ::= [APPLICATION 13] LDAPResult CompareRequest ::= [APPLICATION 14] SEQUENCE { entry LDAPDN, ava AttributeValueAssertion } CompareResponse ::= [APPLICATION 15] LDAPResult AbandonRequest ::= [APPLICATION 16] MessageID ExtendedRequest ::= [APPLICATION 23] SEQUENCE { requestName [0] LDAPOID, requestValue [1] OCTET STRING OPTIONAL } ExtendedResponse ::= [APPLICATION 24] SEQUENCE { COMPONENTS OF LDAPResult, responseName [10] LDAPOID OPTIONAL, responseValue [11] OCTET STRING OPTIONAL } IntermediateResponse ::= [APPLICATION 25] SEQUENCE { responseName [0] LDAPOID OPTIONAL, responseValue [1] OCTET STRING OPTIONAL } END
As one can see, it's much more complicated than the SPNEGO grammar ! The used encoding is a subset of BER, where the TRUE value is always encoded as 0xFF and where all length are fully definite.
Decoding an LDAP message
LdapMessage
Each LDAP message LdapMessage starts with a first automate :
ProtocolOp
The next part (protocolOp) will contains all the different type of possible messages. :
Each message is described in the next chapters.
Controls
It can be followed by an optionnal control part, which automaton state is shown below :
Those two states automaton will be implemented as a Grammar instance, which will be processed for each LdapMessage we will receive. When we will know which kind of Message we have to deal with, a new Grammar will be loaded and processed (a BindRequest grammar, or a BindResponse...). If the specific message is correctly decoded, we may have to load the Controls grammar. So the engine switch from grammar to grammar depending on which part it is decoding. This is implemented by a Grammar stack which is stored in the decoder Container.
LdapResult
Many LdapMessage while return a result. A specific sub-diagram is dedicated to this LdapResult :
We have a specific Grammar to process this LdapResult.
BindRequest
The BindRequest Ldap Message state diagram is shown below :
The decoding of a BindRequest message is simple, as we just need to build an engine that walks through the state automaton, checking at each state that the next transition is valid, and execute the associated action. In the real world, it's a little bit more complicated, because states are not those that we have in the pictures. We have to split each 'state' to sub-states : one sub-state for the Type, one sub-state for the Length and another one for the Value, if necessary.
Those sub-states are just usefull to stop the decoding if anything goes wrong (for instance, if an inner PDU Length is above its encapsulating PDU).
But basically, it's the way the decoder works.
It is also able to stop in the middle of a PDU and start again when the missing data arrive (of course, there must be an action from the caller !).
BindResponse
The Ldap BindResponse message is also made up of four elements: a LdapMessage, the BindResponse, a LdapResult element and optionnaly a final Controls. Here is the inner BindResponse element :
UnBindRequest
This is the simplest automate! It is fully described in the protocolOP schema.
AbandonRequest
This LDAP message is sent by a user when he want the current operation to abort (if a search request take too long, for instance).
This request just send the Message id which has to be stopped.
SearchRequest
The heart of LDAP. Search request are quite complicated, as LDAP is mainly used to search information. We will use four different automatons to express this part of the LDAP grammar :
Filter
We can use filters to select elements :
SubstringFilter
MatchingRuleAssertion
Search responses
We may have three different kind of responses to a ldap Search. The first two are entries or references, and the last one is returned when all teh entries or references have been sent.
SearchResultEntry
SearchResultReference
SearchResultDone
Add
We can add an entry. Here is the message to send, and the response you get :
AddRequest
AddResponse
Del
We can delete an entry. Here is the message to send, and the response you get :
DelRequest
DelResponse
Modify
We can modify an entry. Here is the message to send, and the response you get :
ModifyRequest
ModifyResponse
Modify DN
We can modify a DN. Here is the message to send, and the response you get :
ModifyDNRequest
ModifyDNResponse
Compare message
We can compare elements. Here are the request and the response :
CompareRequest
CompareResponse
Extended message
Otherwise, as LDAP accepts extension, it does through those two messages :
ExtendedRequest
ExtendedResponse
IntermediateResponse
TODO Add diagram