ldap_parser/
parser.rs

1// DEFINITIONS
2// IMPLICIT TAGS
3// EXTENSIBILITY IMPLIED
4
5use crate::error::*;
6use crate::filter::*;
7use crate::ldap::*;
8use asn1_rs::nom;
9use asn1_rs::{
10    Class, Enumerated, FromBer, Header, Implicit, OptTaggedParser, ParseResult, Sequence, Tag,
11    TaggedParser, TaggedValue,
12};
13use nom::bytes::streaming::take;
14use nom::combinator::{complete, map, opt, verify};
15use nom::multi::{many0, many1};
16use nom::Err;
17use std::borrow::Cow;
18
19// // maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
20// const MAX_INT: u32 = 2_147_483_647;
21
22// MessageID ::= INTEGER (0 ..  maxInt)
23impl<'a> FromBer<'a, LdapError> for MessageID {
24    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
25        map(u32::from_ber, MessageID)(bytes).map_err(Err::convert)
26    }
27}
28
29// LDAPString ::= OCTET STRING -- UTF-8 encoded,
30//                             -- [ISO10646] characters
31impl<'a> FromBer<'a, LdapError> for LdapString<'a> {
32    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
33        let (i, b) = parse_ldap_octet_string_as_slice(bytes)?;
34        // convert to UTF-8
35        let s = std::str::from_utf8(b).or(Err(Err::Error(LdapError::InvalidString)))?;
36        Ok((i, LdapString(Cow::Borrowed(s))))
37    }
38}
39
40#[inline]
41pub(crate) fn parse_ldap_octet_string_as_slice(i: &[u8]) -> Result<&[u8]> {
42    <&[u8]>::from_ber(i).map_err(Err::convert)
43}
44
45#[inline]
46fn parse_ldap_int_as_u32(i: &[u8]) -> Result<u32> {
47    <u32>::from_ber(i).map_err(Err::convert)
48}
49
50#[inline]
51fn parse_ldap_enum_as_u32(i: &[u8]) -> Result<u32> {
52    let (i, obj) = Enumerated::from_ber(i).map_err(Err::convert)?;
53    Ok((i, obj.0))
54}
55
56// LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
57//                       -- [RFC4514]
58impl<'a> FromBer<'a, LdapError> for LdapDN<'a> {
59    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
60        // read bytes
61        let (i, b) = <&[u8]>::from_ber(bytes).map_err(Err::convert)?;
62        // convert to UTF-8
63        let s = std::str::from_utf8(b).or(Err(Err::Error(LdapError::InvalidDN)))?;
64        Ok((i, LdapDN(Cow::Borrowed(s))))
65    }
66}
67
68// RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
69//                               -- [RFC4514]
70impl<'a> FromBer<'a, LdapError> for RelativeLdapDN<'a> {
71    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
72        // read bytes
73        let (i, b) = <&[u8]>::from_ber(bytes).map_err(Err::convert)?;
74        // convert to UTF-8
75        let s = std::str::from_utf8(b).or(Err(Err::Error(LdapError::InvalidDN)))?;
76        Ok((i, RelativeLdapDN(Cow::Borrowed(s))))
77    }
78}
79
80// LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
81//                          -- [RFC4512]
82impl<'a> FromBer<'a, LdapError> for LdapOID<'a> {
83    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
84        // read bytes
85        let (i, b) = <&[u8]>::from_ber(bytes).map_err(Err::convert)?;
86        // convert to UTF-8
87        let s = std::str::from_utf8(b).or(Err(Err::Error(LdapError::InvalidDN)))?;
88        Ok((i, LdapOID(Cow::Borrowed(s))))
89    }
90}
91
92// URI ::= LDAPString     -- limited to characters permitted in
93//                                -- URIs
94#[inline]
95fn parse_ldap_uri(i: &[u8]) -> Result<LdapString> {
96    LdapString::from_ber(i)
97}
98
99//
100//
101//
102//
103//
104// ----------------------- LDAP OBJECTS -----------------------
105//
106//
107//
108//
109//
110//
111
112// LDAPResult ::= SEQUENCE {
113//      resultCode         ENUMERATED {
114//           success                      (0),
115//           operationsError              (1),
116//           protocolError                (2),
117//           timeLimitExceeded            (3),
118//           sizeLimitExceeded            (4),
119//           compareFalse                 (5),
120//           compareTrue                  (6),
121//           authMethodNotSupported       (7),
122//           strongerAuthRequired         (8),
123//                -- 9 reserved --
124//           referral                     (10),
125//           adminLimitExceeded           (11),
126//           unavailableCriticalExtension (12),
127//           confidentialityRequired      (13),
128//           saslBindInProgress           (14),
129//           noSuchAttribute              (16),
130//           undefinedAttributeType       (17),
131//           inappropriateMatching        (18),
132//           constraintViolation          (19),
133//           attributeOrValueExists       (20),
134//           invalidAttributeSyntax       (21),
135//                -- 22-31 unused --
136//           noSuchObject                 (32),
137//           aliasProblem                 (33),
138//           invalidDNSyntax              (34),
139//                -- 35 reserved for undefined isLeaf --
140//           aliasDereferencingProblem    (36),
141//                -- 37-47 unused --
142//           inappropriateAuthentication  (48),
143//           invalidCredentials           (49),
144//           insufficientAccessRights     (50),
145//           busy                         (51),
146//           unavailable                  (52),
147//           unwillingToPerform           (53),
148//           loopDetect                   (54),
149//                -- 55-63 unused --
150//           namingViolation              (64),
151//           objectClassViolation         (65),
152//           notAllowedOnNonLeaf          (66),
153//           notAllowedOnRDN              (67),
154//           entryAlreadyExists           (68),
155//           objectClassModsProhibited    (69),
156//                -- 70 reserved for CLDAP --
157//           affectsMultipleDSAs          (71),
158//                -- 72-79 unused --
159//           other                        (80),
160//           ...  },
161//      matchedDN          LDAPDN,
162//      diagnosticMessage  LDAPString,
163//      referral           [3] Referral OPTIONAL }
164fn parse_ldap_result_content(i: &[u8]) -> Result<LdapResult> {
165    let (i, result_code) = map(parse_ldap_enum_as_u32, ResultCode)(i)?;
166    let (i, matched_dn) = LdapDN::from_ber(i)?;
167    let (i, diagnostic_message) = LdapString::from_ber(i)?;
168    // TODO: referral
169    let result = LdapResult {
170        result_code,
171        matched_dn,
172        diagnostic_message,
173    };
174    Ok((i, result))
175}
176
177// LDAPMessage ::= SEQUENCE {
178//      messageID       MessageID,
179//      protocolOp      CHOICE {
180//           bindRequest           BindRequest,
181//           bindResponse          BindResponse,
182//           unbindRequest         UnbindRequest,
183//           searchRequest         SearchRequest,
184//           searchResEntry        SearchResultEntry,
185//           searchResDone         SearchResultDone,
186//           searchResRef          SearchResultReference,
187//           modifyRequest         ModifyRequest,
188//           modifyResponse        ModifyResponse,
189//           addRequest            AddRequest,
190//           addResponse           AddResponse,
191//           delRequest            DelRequest,
192//           delResponse           DelResponse,
193//           modDNRequest          ModifyDNRequest,
194//           modDNResponse         ModifyDNResponse,
195//           compareRequest        CompareRequest,
196//           compareResponse       CompareResponse,
197//           abandonRequest        AbandonRequest,
198//           extendedReq           ExtendedRequest,
199//           extendedResp          ExtendedResponse,
200//           ...,
201//           intermediateResponse  IntermediateResponse },
202//      controls       [0] Controls OPTIONAL }
203/// Parse a single LDAP message and return a structure borrowing fields from the input buffer
204///
205/// ```rust
206/// use ldap_parser::FromBer;
207/// use ldap_parser::ldap::{LdapMessage, MessageID, ProtocolOp, ProtocolOpTag};
208///
209/// static DATA: &[u8] = include_bytes!("../assets/message-search-request-01.bin");
210///
211/// # fn main() {
212/// let res = LdapMessage::from_ber(DATA);
213/// match res {
214///     Ok((rem, msg)) => {
215///         assert!(rem.is_empty());
216///         //
217///         assert_eq!(msg.message_id, MessageID(4));
218///         assert_eq!(msg.protocol_op.tag(), ProtocolOpTag::SearchRequest);
219///         match msg.protocol_op {
220///             ProtocolOp::SearchRequest(req) => {
221///                 assert_eq!(req.base_object.0, "dc=rccad,dc=net");
222///             },
223///             _ => panic!("Unexpected message type"),
224///         }
225///     },
226///     _ => panic!("LDAP parsing failed: {:?}", res),
227/// }
228/// # }
229/// ```
230impl<'a> FromBer<'a, LdapError> for LdapMessage<'a> {
231    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
232        Sequence::from_ber_and_then(bytes, |i| {
233            let (i, message_id) = MessageID::from_ber(i)?;
234            // read header of next element and look tag value
235            let (_, header) = Header::from_ber(i).map_err(Err::convert)?;
236            let (i, protocol_op) = match header.tag().0 {
237                0 => map(BindRequest::from_ber, ProtocolOp::BindRequest)(i),
238                1 => map(BindResponse::from_ber, ProtocolOp::BindResponse)(i),
239                2 => parse_ldap_unbind_request(i),
240                3 => map(SearchRequest::from_ber, ProtocolOp::SearchRequest)(i),
241                4 => map(SearchResultEntry::from_ber, ProtocolOp::SearchResultEntry)(i),
242                5 => map(parse_ldap_search_result_done, ProtocolOp::SearchResultDone)(i),
243                6 => map(ModifyRequest::from_ber, ProtocolOp::ModifyRequest)(i),
244                7 => map(parse_ldap_modify_response, ProtocolOp::ModifyResponse)(i),
245                8 => map(AddRequest::from_ber, ProtocolOp::AddRequest)(i),
246                9 => map(parse_ldap_add_response, ProtocolOp::AddResponse)(i),
247                10 => map(parse_ldap_del_request, ProtocolOp::DelRequest)(i),
248                11 => map(parse_ldap_del_response, ProtocolOp::DelResponse)(i),
249                12 => map(ModDnRequest::from_ber, ProtocolOp::ModDnRequest)(i),
250                13 => map(parse_ldap_moddn_response, ProtocolOp::ModDnResponse)(i),
251                14 => map(CompareRequest::from_ber, ProtocolOp::CompareRequest)(i),
252                15 => map(parse_ldap_compare_response, ProtocolOp::CompareResponse)(i),
253                16 => map(parse_ldap_abandon_request, ProtocolOp::AbandonRequest)(i),
254                19 => map(
255                    parse_ldap_search_result_ref,
256                    ProtocolOp::SearchResultReference,
257                )(i),
258                23 => map(ExtendedRequest::from_ber, ProtocolOp::ExtendedRequest)(i),
259                24 => map(ExtendedResponse::from_ber, ProtocolOp::ExtendedResponse)(i),
260                25 => map(
261                    IntermediateResponse::from_ber,
262                    ProtocolOp::IntermediateResponse,
263                )(i),
264                _ => {
265                    // print_hex_dump(i, 32);
266                    // panic!("Protocol op {} not yet implemented", header.tag.0);
267                    Err(Err::Error(LdapError::InvalidMessageType))
268                }
269            }?;
270            let (i, controls) = OptTaggedParser::new(Class::ContextSpecific, Tag(0))
271                .parse_ber(i, |_, i| many0(complete(Control::from_ber))(i))?;
272            let msg = LdapMessage {
273                message_id,
274                protocol_op,
275                controls,
276            };
277            Ok((i, msg))
278        })
279    }
280}
281
282#[deprecated(
283    since = "0.3.0",
284    note = "Parsing functions are deprecated. Users should instead use the FromBer trait"
285)]
286#[inline]
287pub fn parse_ldap_message(i: &[u8]) -> Result<LdapMessage> {
288    LdapMessage::from_ber(i)
289}
290
291/// Parse a list of LDAP messages and return a structure borrowing fields from the input buffer
292// Note: we don't use the trait because Vec<_>::from_ber forces the Error type
293pub fn parse_ldap_messages(i: &[u8]) -> Result<Vec<LdapMessage>> {
294    // println!("parse_ldap_message: len={}", i.len());
295    // print_hex_dump(i, 32);
296    many1(complete(LdapMessage::from_ber))(i)
297}
298
299// BindRequest ::= [APPLICATION 0] SEQUENCE {
300//      version                 INTEGER (1 ..  127),
301//      name                    LDAPDN,
302//      authentication          AuthenticationChoice }
303impl<'a> FromBer<'a, LdapError> for BindRequest<'a> {
304    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
305        TaggedParser::from_ber_and_then(Class::Application, 0, bytes, |i| {
306            // Sequence::from_ber_and_then(bytes, |i| {
307            let (i, version) = verify(u8::from_ber, |&n| n < 128)(i).map_err(Err::convert)?;
308            let (i, name) = LdapDN::from_ber(i)?;
309            let (i, authentication) = AuthenticationChoice::from_ber(i)?;
310            let req = BindRequest {
311                version,
312                name,
313                authentication,
314            };
315            Ok((i, req))
316            // })
317        })
318    }
319}
320
321// BindResponse ::= [APPLICATION 1] SEQUENCE {
322//      COMPONENTS OF LDAPResult,
323//      serverSaslCreds    [7] OCTET STRING OPTIONAL }
324impl<'a> FromBer<'a, LdapError> for BindResponse<'a> {
325    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
326        TaggedParser::from_ber_and_then(Class::Application, 1, bytes, |i| {
327            let (i, result) = parse_ldap_result_content(i)?;
328            let (i, server_sasl_creds) = OptTaggedParser::new(Class::ContextSpecific, Tag(7))
329                .parse_ber(i, |_, data| Ok((&b""[..], Cow::Borrowed(data))))?;
330
331            // opt(complete(parse_ber_tagged_implicit_g(7, |content, _, _| {
332            // Ok((&b""[..], Cow::Borrowed(content)))
333            // })))(i)?;
334            let req = BindResponse {
335                result,
336                server_sasl_creds,
337            };
338            Ok((i, req))
339        })
340    }
341}
342
343// UnbindRequest ::= [APPLICATION 2] NULL
344fn parse_ldap_unbind_request(bytes: &[u8]) -> Result<ProtocolOp> {
345    TaggedParser::from_ber_and_then(Class::Application, 2, bytes, |i| {
346        // accept empty input, otherwise expect NULL
347        if !i.is_empty() {
348            let (_, _) = <()>::from_ber(i).map_err(Err::convert)?;
349        }
350        Ok((i, ProtocolOp::UnbindRequest))
351    })
352}
353
354// SearchRequest ::= [APPLICATION 3] SEQUENCE {
355//      baseObject      LDAPDN,
356//      scope           ENUMERATED {
357//           baseObject              (0),
358//           singleLevel             (1),
359//           wholeSubtree            (2),
360//           ...  },
361//      derefAliases    ENUMERATED {
362//           neverDerefAliases       (0),
363//           derefInSearching        (1),
364//           derefFindingBaseObj     (2),
365//           derefAlways             (3) },
366//      sizeLimit       INTEGER (0 ..  maxInt),
367//      timeLimit       INTEGER (0 ..  maxInt),
368//      typesOnly       BOOLEAN,
369//      filter          Filter,
370//      attributes      AttributeSelection }
371impl<'a> FromBer<'a, LdapError> for SearchRequest<'a> {
372    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
373        TaggedParser::from_ber_and_then(Class::Application, 3, bytes, |i| {
374            let (i, base_object) = LdapDN::from_ber(i)?;
375            let (i, scope) = map(parse_ldap_enum_as_u32, SearchScope)(i)?;
376            let (i, deref_aliases) = map(parse_ldap_enum_as_u32, DerefAliases)(i)?;
377            let (i, size_limit) = parse_ldap_int_as_u32(i)?;
378            let (i, time_limit) = parse_ldap_int_as_u32(i)?;
379            let (i, types_only) = <bool>::from_ber(i).map_err(Err::convert)?;
380            let (i, filter) = Filter::from_ber(i)?;
381            let (i, attributes) = parse_attribute_selection(i)?;
382            let req = SearchRequest {
383                base_object,
384                scope,
385                deref_aliases,
386                size_limit,
387                time_limit,
388                types_only,
389                filter,
390                attributes,
391            };
392            Ok((i, req))
393        })
394    }
395}
396
397// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
398//     objectName      LDAPDN,
399//     attributes      PartialAttributeList }
400impl<'a> FromBer<'a, LdapError> for SearchResultEntry<'a> {
401    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
402        TaggedParser::from_ber_and_then(Class::Application, 4, bytes, |i| {
403            let (i, object_name) = LdapDN::from_ber(i)?;
404            let (i, attributes) = parse_partial_attribute_list(i)?;
405            let res = SearchResultEntry {
406                object_name,
407                attributes,
408            };
409            Ok((i, res))
410        })
411    }
412}
413
414// SearchResultDone ::= [APPLICATION 5] LDAPResult
415fn parse_ldap_search_result_done(bytes: &[u8]) -> Result<LdapResult> {
416    TaggedParser::from_ber_and_then(Class::Application, 5, bytes, parse_ldap_result_content)
417}
418
419// ModifyRequest ::= [APPLICATION 6] SEQUENCE {
420//     object          LDAPDN,
421//     changes         SEQUENCE OF change SEQUENCE {
422//          operation       ENUMERATED {
423//               add     (0),
424//               delete  (1),
425//               replace (2),
426//               ...  },
427//          modification    PartialAttribute } }
428impl<'a> FromBer<'a, LdapError> for ModifyRequest<'a> {
429    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
430        TaggedParser::from_ber_and_then(Class::Application, 6, bytes, |i| {
431            let (i, object) = LdapDN::from_ber(i)?;
432            let (i, changes) = Sequence::from_ber_and_then(i, many1(complete(Change::from_ber)))?;
433            let res = ModifyRequest { object, changes };
434            Ok((i, res))
435        })
436    }
437}
438
439// ModifyResponse ::= [APPLICATION 7] LDAPResult
440fn parse_ldap_modify_response(bytes: &[u8]) -> Result<ModifyResponse> {
441    TaggedParser::from_ber_and_then(Class::Application, 7, bytes, |i| {
442        let (i, result) = parse_ldap_result_content(i)?;
443        let res = ModifyResponse { result };
444        Ok((i, res))
445    })
446}
447
448// AddRequest ::= [APPLICATION 8] SEQUENCE {
449//     entry           LDAPDN,
450//     attributes      AttributeList }
451impl<'a> FromBer<'a, LdapError> for AddRequest<'a> {
452    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
453        TaggedParser::from_ber_and_then(Class::Application, 8, bytes, |i| {
454            let (i, entry) = LdapDN::from_ber(i)?;
455            let (i, attributes) = parse_attribute_list(i)?;
456            let res = AddRequest { entry, attributes };
457            Ok((i, res))
458        })
459    }
460}
461
462// AddResponse ::= [APPLICATION 9] LDAPResult
463fn parse_ldap_add_response(bytes: &[u8]) -> Result<LdapResult> {
464    TaggedParser::from_ber_and_then(Class::Application, 9, bytes, parse_ldap_result_content)
465}
466
467// DelRequest ::= [APPLICATION 10] LDAPDN
468fn parse_ldap_del_request(bytes: &[u8]) -> Result<LdapDN> {
469    TaggedParser::from_ber_and_then(Class::Application, 10, bytes, |i| {
470        let s = std::str::from_utf8(i).or(Err(Err::Error(LdapError::InvalidDN)))?;
471        let oid = LdapDN(Cow::Borrowed(s));
472        Ok((&b""[..], oid))
473    })
474}
475
476// DelResponse ::= [APPLICATION 11] LDAPResult
477fn parse_ldap_del_response(bytes: &[u8]) -> Result<LdapResult> {
478    TaggedParser::from_ber_and_then(Class::Application, 11, bytes, parse_ldap_result_content)
479}
480
481// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
482//     entry           LDAPDN,
483//     newrdn          RelativeLDAPDN,
484//     deleteoldrdn    BOOLEAN,
485//     newSuperior     [0] LDAPDN OPTIONAL }
486impl<'a> FromBer<'a, LdapError> for ModDnRequest<'a> {
487    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
488        TaggedParser::from_ber_and_then(Class::Application, 12, bytes, |i| {
489            let (i, entry) = LdapDN::from_ber(i)?;
490            let (i, newrdn) = RelativeLdapDN::from_ber(i)?;
491            let (i, deleteoldrdn) = <bool>::from_ber(i).map_err(Err::convert)?;
492            let (i, newsuperior) =
493                OptTaggedParser::new(Class::ContextSpecific, Tag(0)).parse_ber(i, |_, i| {
494                    let s = std::str::from_utf8(i).or(Err(Err::Error(LdapError::InvalidDN)))?;
495                    let oid = LdapDN(Cow::Borrowed(s));
496                    Ok((&b""[..], oid))
497                })?;
498            let res = ModDnRequest {
499                entry,
500                newrdn,
501                deleteoldrdn,
502                newsuperior,
503            };
504            Ok((i, res))
505        })
506    }
507}
508
509// ModifyDNResponse ::= [APPLICATION 13] LDAPResult
510fn parse_ldap_moddn_response(bytes: &[u8]) -> Result<LdapResult> {
511    TaggedParser::from_ber_and_then(Class::Application, 13, bytes, parse_ldap_result_content)
512}
513
514// CompareRequest ::= [APPLICATION 14] SEQUENCE {
515//     entry           LDAPDN,
516//     ava             AttributeValueAssertion }
517impl<'a> FromBer<'a, LdapError> for CompareRequest<'a> {
518    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
519        TaggedParser::from_ber_and_then(Class::Application, 14, bytes, |i| {
520            let (i, entry) = LdapDN::from_ber(i)?;
521            let (i, ava) = AttributeValueAssertion::from_ber(i)?;
522            let res = CompareRequest { entry, ava };
523            Ok((i, res))
524        })
525    }
526}
527
528// CompareResponse ::= [APPLICATION 15] LDAPResult
529fn parse_ldap_compare_response(bytes: &[u8]) -> Result<LdapResult> {
530    TaggedParser::from_ber_and_then(Class::Application, 15, bytes, parse_ldap_result_content)
531}
532
533// AbandonRequest ::= [APPLICATION 16] MessageID
534fn parse_ldap_abandon_request(bytes: &[u8]) -> Result<MessageID> {
535    let (rem, id) = TaggedValue::<u32, _, Implicit, { Class::APPLICATION }, 16>::from_ber(bytes)
536        .map_err(Err::convert)?;
537    Ok((rem, MessageID(id.into_inner())))
538}
539
540// SearchResultReference ::= [APPLICATION 19] SEQUENCE
541//                                   SIZE (1..MAX) OF uri URI
542fn parse_ldap_search_result_ref(bytes: &[u8]) -> Result<Vec<LdapString>> {
543    TaggedParser::from_ber_and_then(
544        Class::Application,
545        19,
546        bytes,
547        many1(complete(parse_ldap_uri)),
548    )
549}
550
551// ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
552//     requestName      [0] LDAPOID,
553//     requestValue     [1] OCTET STRING OPTIONAL }
554impl<'a> FromBer<'a, LdapError> for ExtendedRequest<'a> {
555    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
556        TaggedParser::from_ber_and_then(Class::Application, 23, bytes, |i| {
557            let (i, request_name) =
558                TaggedParser::from_ber_and_then(Class::ContextSpecific, 0, i, |i| {
559                    let s = std::str::from_utf8(i).or(Err(Err::Error(LdapError::InvalidDN)))?;
560                    let oid = LdapOID(Cow::Borrowed(s));
561                    Ok((&b""[..], oid))
562                })?;
563            let (i, request_value) = OptTaggedParser::new(Class::ContextSpecific, Tag(1))
564                .parse_ber(i, |_, data| Ok((&b""[..], Cow::Borrowed(data))))?;
565            let req = ExtendedRequest {
566                request_name,
567                request_value,
568            };
569            Ok((i, req))
570        })
571    }
572}
573
574// ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
575//     COMPONENTS OF LDAPResult,
576//     responseName     [10] LDAPOID OPTIONAL,
577//     responseValue    [11] OCTET STRING OPTIONAL }
578impl<'a> FromBer<'a, LdapError> for ExtendedResponse<'a> {
579    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
580        TaggedParser::from_ber_and_then(Class::Application, 24, bytes, |i| {
581            let (i, result) = parse_ldap_result_content(i)?;
582            let (i, response_name) = OptTaggedParser::new(Class::ContextSpecific, Tag(10))
583                .parse_ber(i, |_, i| {
584                    let s = std::str::from_utf8(i).or(Err(Err::Error(LdapError::InvalidDN)))?;
585                    let oid = LdapOID(Cow::Borrowed(s));
586                    Ok((&b""[..], oid))
587                })?;
588            let (i, response_value) = OptTaggedParser::new(Class::ContextSpecific, Tag(11))
589                .parse_ber(i, |_, data| Ok((&b""[..], Cow::Borrowed(data))))?;
590            let resp = ExtendedResponse {
591                result,
592                response_name,
593                response_value,
594            };
595            Ok((i, resp))
596        })
597    }
598}
599
600// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
601//      responseName     [0] LDAPOID OPTIONAL,
602//      responseValue    [1] OCTET STRING OPTIONAL }
603impl<'a> FromBer<'a, LdapError> for IntermediateResponse<'a> {
604    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
605        TaggedParser::from_ber_and_then(Class::Application, 25, bytes, |i| {
606            let (i, response_name) = OptTaggedParser::new(Class::ContextSpecific, Tag(0))
607                .parse_ber(i, |_, i| {
608                    let s = std::str::from_utf8(i).or(Err(Err::Error(LdapError::InvalidDN)))?;
609                    let oid = LdapOID(Cow::Borrowed(s));
610                    Ok((&b""[..], oid))
611                })?;
612            let (i, response_value) = OptTaggedParser::new(Class::ContextSpecific, Tag(1))
613                .parse_ber(i, |_, data| Ok((&b""[..], Cow::Borrowed(data))))?;
614            let resp = IntermediateResponse {
615                response_name,
616                response_value,
617            };
618            Ok((i, resp))
619        })
620    }
621}
622
623// AuthenticationChoice ::= CHOICE {
624//      simple                  [0] OCTET STRING,
625//                              -- 1 and 2 reserved
626//      sasl                    [3] SaslCredentials,
627//      ...  }
628impl<'a> FromBer<'a, LdapError> for AuthenticationChoice<'a> {
629    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
630        let (rem, header) = Header::from_ber(bytes).map_err(Err::convert)?;
631        match header.tag().0 {
632            0 => {
633                // assume len is primitive, and just take bytes
634                let sz = header
635                    .length()
636                    .definite()
637                    .map_err(|e| Err::Error(LdapError::Ber(e)))?;
638                let (i, b) = take(sz)(rem)?;
639                // // other solution: read content as octetstring and get slice
640                // let (i, b) = map_res(
641                //     |d| {
642                //         ber_read_element_content_as(
643                //             d,
644                //             BerTag::OctetString,
645                //             header.len,
646                //             header.is_constructed(),
647                //             1,
648                //         )
649                //     },
650                //     |o| o.as_slice(),
651                // )(rem)
652                // .map_err(Err::convert)?;
653                Ok((i, AuthenticationChoice::Simple(Cow::Borrowed(b))))
654            }
655            3 => map(parse_sasl_credentials, AuthenticationChoice::Sasl)(rem),
656            _ => Err(Err::Error(LdapError::InvalidAuthenticationType)),
657        }
658    }
659}
660
661// SaslCredentials ::= SEQUENCE {
662//      mechanism               LDAPString,
663//      credentials             OCTET STRING OPTIONAL }
664fn parse_sasl_credentials(i: &[u8]) -> Result<SaslCredentials> {
665    let (i, mechanism) = LdapString::from_ber(i)?;
666    let (i, credentials) = opt(complete(map(
667        parse_ldap_octet_string_as_slice,
668        Cow::Borrowed,
669    )))(i)?;
670    let credentials = SaslCredentials {
671        mechanism,
672        credentials,
673    };
674    Ok((i, credentials))
675}
676
677// AttributeSelection ::= SEQUENCE OF selector LDAPString
678//      -- The LDAPString is constrained to
679//      -- <attributeSelector> in Section 4.5.1.8
680fn parse_attribute_selection(bytes: &[u8]) -> Result<Vec<LdapString>> {
681    Sequence::from_ber_and_then(bytes, many0(complete(LdapString::from_ber)))
682}
683
684// PartialAttributeList ::= SEQUENCE OF partialAttribute PartialAttribute
685fn parse_partial_attribute_list(bytes: &[u8]) -> Result<Vec<PartialAttribute>> {
686    Sequence::from_ber_and_then(bytes, many0(complete(PartialAttribute::from_ber)))
687}
688
689// AttributeList ::= SEQUENCE OF attribute Attribute
690fn parse_attribute_list(bytes: &[u8]) -> Result<Vec<Attribute>> {
691    Sequence::from_ber_and_then(bytes, many0(complete(Attribute::from_ber)))
692}
693
694// change SEQUENCE {
695//          operation       ENUMERATED {
696//               add     (0),
697//               delete  (1),
698//               replace (2),
699//               ...  },
700//          modification    PartialAttribute }
701impl<'a> FromBer<'a, LdapError> for Change<'a> {
702    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
703        Sequence::from_ber_and_then(bytes, |i| {
704            let (i, operation) = map(parse_ldap_enum_as_u32, Operation)(i)?;
705            let (i, modification) = PartialAttribute::from_ber(i)?;
706            let change = Change {
707                operation,
708                modification,
709            };
710            Ok((i, change))
711        })
712    }
713}
714
715// Control ::= SEQUENCE {
716//     controlType             LDAPOID,
717//     criticality             BOOLEAN DEFAULT FALSE,
718//     controlValue            OCTET STRING OPTIONAL }
719impl<'a> FromBer<'a, LdapError> for Control<'a> {
720    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, LdapError> {
721        Sequence::from_ber_and_then(bytes, |i| {
722            let (i, control_type) = LdapOID::from_ber(i)?;
723            let (i, maybe_critical) = <Option<bool>>::from_ber(i).map_err(Err::convert)?;
724            // opt(complete(bool::from_ber))(i).map_err(Err::convert)?;
725            let criticality = maybe_critical.unwrap_or(false);
726            let (i, control_value) = opt(complete(map(
727                parse_ldap_octet_string_as_slice,
728                Cow::Borrowed,
729            )))(i)?;
730            let control = Control {
731                control_type,
732                criticality,
733                control_value,
734            };
735            Ok((i, control))
736        })
737    }
738}
739
740//
741//
742//
743//
744//
745// ----------------------- TESTS -----------------------
746//
747//
748//
749//
750//
751//
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756    use asn1_rs::oid;
757    use hex_literal::hex;
758
759    #[test]
760    fn test_parse_bind_request() {
761        const DATA: &[u8] = include_bytes!("../assets/bind_request.bin");
762
763        let (rem, req) = BindRequest::from_ber(DATA).expect("parsing failed");
764        //
765        // dbg!(&req);
766        //
767        assert!(rem.is_empty());
768        assert_eq!(&req.name.0, "xxxxxxxxxxx@xx.xxx.xxxxx.net");
769        assert_eq!(
770            req.authentication,
771            AuthenticationChoice::Simple(Cow::Borrowed(b"passwor8d1"))
772        );
773    }
774
775    #[test]
776    fn test_parse_bind_request_sasl() {
777        const DATA: &[u8] = include_bytes!("../assets/bind_request_sasl.bin");
778
779        let (rem, req) = BindRequest::from_ber(DATA).expect("parsing failed");
780        //
781        // dbg!(&req);
782        //
783        assert!(rem.is_empty());
784        assert_eq!(&req.name.0, "");
785        if let AuthenticationChoice::Sasl(sasl_credentials) = &req.authentication {
786            assert_eq!(&sasl_credentials.mechanism.0, "GSS-SPNEGO");
787        } else {
788            panic!("wrong authentication type");
789        }
790    }
791
792    #[test]
793    fn test_parse_bind_response_minimal() {
794        const DATA: &[u8] = &hex!("61 84 00 00 00 07 0a 01 00 04 00 04 00");
795        let (rem, resp) = BindResponse::from_ber(DATA).expect("parsing failed");
796        //
797        // dbg!(&resp);
798        //
799        assert!(rem.is_empty());
800        assert_eq!(resp.result.result_code, ResultCode::Success);
801    }
802
803    #[test]
804    fn test_parse_bind_response_sasl() {
805        const DATA: &[u8] = include_bytes!("../assets/bind_response_sasl.bin");
806        let (rem, resp) = BindResponse::from_ber(DATA).expect("parsing failed");
807        //
808        // dbg!(&resp);
809        //
810        assert!(rem.is_empty());
811        assert_eq!(resp.result.result_code, ResultCode::Success);
812        assert!(resp.server_sasl_creds.is_some());
813    }
814
815    #[test]
816    fn test_parse_unbind_request() {
817        const DATA: &[u8] = &hex!("42 00");
818
819        let (rem, req) = parse_ldap_unbind_request(DATA).expect("parsing failed");
820        //
821        // dbg!(&req);
822        //
823        assert!(rem.is_empty());
824        assert_eq!(req, ProtocolOp::UnbindRequest);
825    }
826
827    #[test]
828    fn test_parse_search_request() {
829        const DATA: &[u8] = include_bytes!("../assets/search_request.bin");
830        let (rem, resp) = SearchRequest::from_ber(DATA).expect("parsing failed");
831        //
832        // dbg!(&resp);
833        //
834        assert!(rem.is_empty());
835        assert_eq!(&resp.base_object.0, "DC=xx,DC=xxx,DC=xxxxx,DC=net");
836        assert_eq!(resp.scope, SearchScope::WholeSubtree);
837        assert_eq!(resp.attributes.len(), 1);
838    }
839
840    #[test]
841    fn test_parse_search_result_entry() {
842        const DATA: &[u8] = include_bytes!("../assets/search_result_entry.bin");
843        let (rem, resp) = SearchResultEntry::from_ber(DATA).expect("parsing failed");
844        //
845        // dbg!(&resp);
846        //
847        assert!(rem.is_empty());
848        assert_eq!(resp.attributes.len(), 1);
849    }
850
851    #[test]
852    fn test_parse_search_result_done() {
853        const DATA: &[u8] = include_bytes!("../assets/search_result_done.bin");
854        let (rem, resp) = parse_ldap_search_result_done(DATA).expect("parsing failed");
855        //
856        // dbg!(&resp);
857        //
858        assert!(rem.is_empty());
859        assert_eq!(resp.result_code, ResultCode::Success);
860    }
861
862    #[test]
863    fn test_parse_search_result_ref() {
864        const DATA: &[u8] = include_bytes!("../assets/search_result_ref.bin");
865        let (rem, v) = parse_ldap_search_result_ref(DATA).expect("parsing failed");
866        //
867        // dbg!(&v);
868        //
869        assert!(rem.is_empty());
870        assert_eq!(v.len(), 1);
871        assert_eq!(
872            &v[0].0,
873            "ldap://DomainDnsZones.rccad.net/DC=DomainDnsZones,DC=rccad,DC=net"
874        );
875    }
876
877    #[test]
878    fn test_parse_extended_req() {
879        const DATA: &[u8] = include_bytes!("../assets/extended-req.bin");
880        let (rem, req) = ExtendedRequest::from_ber(DATA).expect("parsing failed");
881        //
882        // dbg!(&req);
883        //
884        assert!(rem.is_empty());
885        assert_eq!(
886            req.request_name.0,
887            oid!(1.3.6 .1 .4 .1 .1466 .20037).to_string()
888        );
889        assert!(req.request_value.is_none());
890    }
891
892    #[test]
893    fn test_parse_extended_response() {
894        const DATA: &[u8] = &hex!("78 07 0a 01 00 04 00 04 00");
895        let (rem, resp) = ExtendedResponse::from_ber(DATA).expect("parsing failed");
896        //
897        // dbg!(&resp);
898        //
899        assert!(rem.is_empty());
900        assert_eq!(resp.result.result_code, ResultCode::Success);
901    }
902
903    #[test]
904    fn test_parse_modify_request() {
905        const DATA: &[u8] = include_bytes!("../assets/modify-request.bin");
906        let (rem, req) = ModifyRequest::from_ber(DATA).expect("parsing failed");
907        //
908        // dbg!(&req);
909        //
910        assert!(rem.is_empty());
911        assert_eq!(&req.object.0, "cn=username1,ou=users,dc=xxx,dc=internet");
912        assert_eq!(req.changes.len(), 1);
913        assert_eq!(req.changes[0].modification.attr_type.0, "description");
914    }
915
916    #[test]
917    fn test_parse_modify_response() {
918        const DATA: &[u8] = include_bytes!("../assets/modify-response.bin");
919        let (rem, resp) = parse_ldap_modify_response(DATA).expect("parsing failed");
920        //
921        // dbg!(&resp);
922        //
923        assert!(rem.is_empty());
924        assert_eq!(resp.result.result_code, ResultCode::Success);
925    }
926
927    #[test]
928    fn test_parse_add_request() {
929        const DATA: &[u8] = include_bytes!("../assets/add-request.bin");
930        let (rem, req) = AddRequest::from_ber(DATA).expect("parsing failed");
931        //
932        // dbg!(&req);
933        //
934        assert!(rem.is_empty());
935        assert_eq!(&req.entry.0, "cn=username1,ou=users,dc=xxx,dc=internet");
936        assert_eq!(req.attributes.len(), 4);
937    }
938
939    #[test]
940    fn test_parse_add_response() {
941        const DATA: &[u8] = include_bytes!("../assets/add-response.bin");
942        let (rem, resp) = parse_ldap_add_response(DATA).expect("parsing failed");
943        //
944        // dbg!(&resp);
945        //
946        assert!(rem.is_empty());
947        assert_eq!(resp.result_code, ResultCode::Success);
948    }
949
950    #[test]
951    fn test_parse_del_request() {
952        const DATA: &[u8] = include_bytes!("../assets/del-request.bin");
953        let (rem, req) = parse_ldap_del_request(DATA).expect("parsing failed");
954        //
955        // dbg!(&req);
956        //
957        assert!(rem.is_empty());
958        assert_eq!(&req.0, "cn=username2,ou=users2,dc=xxx,dc=internet");
959    }
960
961    #[test]
962    fn test_parse_del_response() {
963        const DATA: &[u8] = include_bytes!("../assets/del-response.bin");
964        let (rem, resp) = parse_ldap_del_response(DATA).expect("parsing failed");
965        //
966        // dbg!(&resp);
967        //
968        assert!(rem.is_empty());
969        assert_eq!(resp.result_code, ResultCode::Success);
970    }
971
972    #[test]
973    fn test_parse_moddn_request() {
974        const DATA: &[u8] = include_bytes!("../assets/moddn-request.bin");
975        let (rem, req) = ModDnRequest::from_ber(DATA).expect("parsing failed");
976        //
977        // dbg!(&req);
978        //
979        assert!(rem.is_empty());
980        assert_eq!(&req.entry.0, "cn=username1,ou=users,dc=xxx,dc=internet");
981        assert_eq!(&req.newrdn.0, "cn=username2");
982        assert!(req.deleteoldrdn);
983        assert_eq!(&req.newsuperior.unwrap().0, "ou=users,dc=xxx,dc=internet");
984    }
985
986    #[test]
987    fn test_parse_moddn_response() {
988        const DATA: &[u8] = include_bytes!("../assets/moddn-response.bin");
989        let (rem, resp) = parse_ldap_moddn_response(DATA).expect("parsing failed");
990        //
991        // dbg!(&resp);
992        //
993        assert!(rem.is_empty());
994        assert_eq!(resp.result_code, ResultCode::Success);
995    }
996
997    #[test]
998    fn test_parse_compare_request() {
999        const DATA: &[u8] = include_bytes!("../assets/compare-request.bin");
1000        let (rem, req) = CompareRequest::from_ber(DATA).expect("parsing failed");
1001        //
1002        // dbg!(&req);
1003        //
1004        assert!(rem.is_empty());
1005        assert_eq!(&req.entry.0, "cn=username2,ou=users2,dc=xxx,dc=internet");
1006        assert_eq!(&req.ava.attribute_desc.0, "cn");
1007    }
1008
1009    #[test]
1010    fn test_parse_compare_response() {
1011        const DATA: &[u8] = include_bytes!("../assets/compare-response.bin");
1012        let (rem, resp) = parse_ldap_compare_response(DATA).expect("parsing failed");
1013        //
1014        // dbg!(&resp);
1015        //
1016        assert!(rem.is_empty());
1017        assert_eq!(resp.result_code, ResultCode::CompareTrue);
1018    }
1019
1020    #[test]
1021    fn test_parse_abandon_request() {
1022        const DATA: &[u8] = &[0x30, 0x06, 0x02, 0x01, 0x06, 0x50, 0x01, 0x05];
1023
1024        let (rem, msg) = LdapMessage::from_ber(DATA).expect("parsing failed");
1025        assert!(rem.is_empty());
1026        assert_eq!(msg.message_id, MessageID(6));
1027        assert!(matches!(
1028            msg.protocol_op,
1029            ProtocolOp::AbandonRequest(MessageID(5))
1030        ))
1031    }
1032}