Coverage Report

Created: 2025-07-11 07:47

/src/PcapPlusPlus/Packet++/header/LdapLayer.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include "Layer.h"
4
#include "Asn1Codec.h"
5
#include <ostream>
6
#include <string>
7
#include <functional>
8
9
/// @file
10
11
/// @namespace pcpp
12
/// @brief The main namespace for the PcapPlusPlus lib
13
namespace pcpp
14
{
15
  /// @class LdapOperationType
16
  /// @brief An enum wrapper class for LDAP operation types
17
  class LdapOperationType
18
  {
19
  public:
20
    /// Define enum types and the corresponding int values
21
    enum Value : uint8_t
22
    {
23
      /// Bind Request
24
      BindRequest = 0,
25
      /// Bind Response
26
      BindResponse = 1,
27
      /// Unbind Request
28
      UnbindRequest = 2,
29
      /// Search Request
30
      SearchRequest = 3,
31
      /// Search Result Entry
32
      SearchResultEntry = 4,
33
      /// Search Result Done
34
      SearchResultDone = 5,
35
      /// Modify Request
36
      ModifyRequest = 6,
37
      /// Modify Response
38
      ModifyResponse = 7,
39
      /// Add Request
40
      AddRequest = 8,
41
      /// Add Response
42
      AddResponse = 9,
43
      /// Delete Request
44
      DeleteRequest = 10,
45
      /// Delete Response
46
      DeleteResponse = 11,
47
      /// Modify DN (Distinguished Name) Request
48
      ModifyDNRequest = 12,
49
      /// Modify DN (Distinguished Name) Response
50
      ModifyDNResponse = 13,
51
      /// Compare Request
52
      CompareRequest = 14,
53
      /// Compare Response
54
      CompareResponse = 15,
55
      /// Abandon Request
56
      AbandonRequest = 16,
57
      /// Search Result Reference
58
      SearchResultReference = 19,
59
      /// Extended Request
60
      ExtendedRequest = 23,
61
      /// Extended Response
62
      ExtendedResponse = 24,
63
      /// Intermediate Response
64
      IntermediateResponse = 25,
65
      /// Unknown operation type
66
      Unknown = 255
67
    };
68
69
    LdapOperationType() = default;
70
71
    // cppcheck-suppress noExplicitConstructor
72
    /// Construct LdapOperationType from Value enum
73
    /// @param[in] value the operation type enum value
74
956
    constexpr LdapOperationType(Value value) : m_Value(value)
75
956
    {}
76
77
    /// @return A string representation of the operation type
78
    std::string toString() const;
79
80
    /// A static method that creates LdapOperationType from an integer value
81
    /// @param[in] value The operation type integer value
82
    /// @return The operation type that corresponds to the integer value. If the integer value
83
    /// doesn't corresponds to any operation type, LdapOperationType::Unknown is returned
84
    static LdapOperationType fromUintValue(uint8_t value);
85
86
    // Allow switch and comparisons.
87
    constexpr operator Value() const
88
20.6k
    {
89
20.6k
      return m_Value;
90
20.6k
    }
91
92
    // Prevent usage: if(LdapOperationType)
93
    explicit operator bool() const = delete;
94
95
  private:
96
    Value m_Value = LdapOperationType::Unknown;
97
  };
98
99
  /// @class LdapResultCode
100
  /// @brief An enum wrapper class for LDAP result codes
101
  class LdapResultCode
102
  {
103
  public:
104
    /// Define enum types and the corresponding int values
105
    enum Value : uint8_t
106
    {
107
      /// Indicates that the associated operation completed successfully
108
      Success = 0,
109
      /// Indicates that there was a problem with the client’s use of the LDAP protocol
110
      OperationsError = 1,
111
      /// Indicates that there was a problem with the client’s use of the LDAP protocol
112
      ProtocolError = 2,
113
      /// Indicates that the associated operation failed because it hadn’t completed by the time
114
      /// a maximum processing time limit had been reached
115
      TimeLimitExceeded = 3,
116
      /// Indicates that the associated search operation failed because the server has determined
117
      /// that the number of entries that would be returned in response to the search would exceed
118
      /// the upper bound for that operation
119
      SizeLimitExceeded = 4,
120
      /// Indicates that the associated compare request targeted an entry that exists and that contains
121
      /// the targeted attribute, but does not have any value that matches the provided assertion value
122
      CompareFalse = 5,
123
      /// Indicates that the associated compare request targeted an entry that exists and that contains
124
      /// the targeted attribute with a value that matches the provided assertion value
125
      CompareTrue = 6,
126
      /// Indicates that the associated bind operation failed because the client attempted to authenticate
127
      /// with a mechanism that the server does not support or that it does not allow the client to use
128
      AuthMethodNotSupported = 7,
129
      /// Indicates that the server requires the client to authenticate with a stronger form of authentication
130
      StrongerAuthRequired = 8,
131
      /// Indicates that the request cannot be processed exactly as issued, but that it might succeed
132
      /// if re-issued to a different server, or is updated to target a different location in the DIT
133
      Referral = 10,
134
      /// Indicates that some administrative limit within the server was exceeded while processing the request
135
      AdminLimitExceeded = 11,
136
      /// Indicates that the request includes a control with a criticality of true,
137
      /// but that control could not be honored for some reason
138
      UnavailableCriticalExtension = 12,
139
      /// Indicates that the server is only willing to process the requested operation if it is received
140
      /// over a secure connection that does not allow an eavesdropper to decipher or alter the contents
141
      /// of the request or response
142
      ConfidentialityRequired = 13,
143
      /// Indicates that the server has completed a portion of the processing for the provided SASL
144
      /// bind request, but that it needs additional information from the client to complete the authentication
145
      SaslBindInProgress = 14,
146
      /// Indicates that the request targeted an attribute that does not exist in the specified entry
147
      NoSuchAttribute = 16,
148
      /// Indicates that the request attempted to provide one or more values for an attribute type
149
      /// that is not defined in the server schema
150
      UndefinedAttributeType = 17,
151
      /// Indicates that the search request tried to perform some type of matching that is not
152
      /// supported for the target attribute type
153
      InappropriateMatching = 18,
154
      /// Indicates that the requested operation would have resulted in an entry that violates
155
      /// some constraint defined within the server
156
      ConstraintViolation = 19,
157
      /// Indicates that the requested operation would have resulted in an attribute in which
158
      /// the same value appeared more than once
159
      AttributeOrValueExists = 20,
160
      /// Indicates that the requested add or modify operation would have resulted in an entry
161
      /// that had at least one attribute value that does not conform to the constraints of the
162
      /// associated attribute syntax
163
      InvalidAttributeSyntax = 21,
164
      /// Indicates that the requested operation targeted an entry that does not exist within the DIT
165
      NoSuchObject = 32,
166
      /// Indicates that a problem occurred while attempting to dereference an alias during search processing
167
      AliasProblem = 33,
168
      /// Indicates that the request included a malformed entry DN
169
      InvalidDNSyntax = 34,
170
      /// Indicates that the server encountered an alias while processing the request and that there
171
      /// was some problem related to that alias
172
      AliasDereferencingProblem = 36,
173
      /// Indicates that the client attempted to bind in an inappropriate manner that is inappropriate
174
      /// for the target account
175
      InappropriateAuthentication = 48,
176
      /// Indicates that the client attempted to bind with a set of credentials that cannot
177
      /// be used to authenticate
178
      InvalidCredentials = 49,
179
      /// Indicates that the client requested an operation for which it does not have the necessary
180
      /// access control permissions
181
      InsufficientAccessRights = 50,
182
      /// Indicates that the requested operation cannot be processed because the server is currently too busy
183
      Busy = 51,
184
      /// Indicates that the server is currently not available to process the requested operation
185
      Unavailable = 52,
186
      /// Indicates that the server is not willing to process the requested operation for some reason
187
      UnwillingToPerform = 53,
188
      /// Indicates that the server detected some kind of circular reference in the course
189
      /// of processing an operation
190
      LoopDetect = 54,
191
      /// Indicates that the requested add or modify DN operation would have resulted in an entry
192
      /// that violates some naming constraint within the server
193
      NamingViolation = 64,
194
      /// Indicates that the requested operation would have resulted in an entry that has
195
      /// an inappropriate set of object classes, or whose attributes violate the constraints
196
      /// associated with its set of object classes
197
      ObjectClassViolation = 65,
198
      /// Indicates that the requested operation is only supported for leaf entries,
199
      /// but the targeted entry has one or more subordinates
200
      NotAllowedOnNonLeaf = 66,
201
      /// Indicates that the requested modify operation would have resulted in an entry that
202
      /// does not include all of the attributes used in its RDN
203
      NotAllowedOnRDN = 67,
204
      /// Indicates that the requested operation would have resulted in an entry with the same
205
      /// DN as an entry that already exists in the server
206
      EntryAlreadyExists = 68,
207
      /// Indicates that the requested modify operation would have altered the target entry’s
208
      /// set of object classes in a way that is not supported
209
      ObjectClassModsProhibited = 69,
210
      /// Indicates that the requested operation would have required manipulating information
211
      /// in multiple servers in a way that is not supported
212
      AffectsMultipleDSAs = 71,
213
      /// Used when a problem occurs for which none of the other result codes is more appropriate
214
      Other = 80,
215
      /// Unknown result code
216
      Unknown = 255
217
    };
218
219
    LdapResultCode() = default;
220
221
    // cppcheck-suppress noExplicitConstructor
222
    /// Construct LdapResultCode from Value enum
223
    /// @param[in] value the result code enum value
224
0
    constexpr LdapResultCode(Value value) : m_Value(value)
225
0
    {}
226
227
    /// @return A string representation of the result code
228
    std::string toString() const;
229
230
    /// A static method that creates LdapResultCode from an integer value
231
    /// @param[in] value The result code integer value
232
    /// @return The result code that corresponds to the integer value. If the integer value
233
    /// doesn't corresponds to any operation type, LdapResultCode::Unknown is returned
234
    static LdapResultCode fromUintValue(uint8_t value);
235
236
    // Allow switch and comparisons
237
    constexpr operator Value() const
238
0
    {
239
0
      return m_Value;
240
0
    }
241
242
    // Prevent usage: if(LdapResultCode)
243
    explicit operator bool() const = delete;
244
245
  private:
246
    Value m_Value = LdapResultCode::Unknown;
247
  };
248
249
  /// @struct LdapControl
250
  /// A struct that represents an LDAP Control
251
  struct LdapControl
252
  {
253
    /// LDAP control type
254
    std::string controlType;
255
    /// LDAP control value
256
    std::string controlValue;
257
258
    /// Equality operator overload for this struct
259
    /// @param[in] other The value to compare with
260
    /// @return True if both values are equal, false otherwise
261
    bool operator==(const LdapControl& other) const
262
0
    {
263
0
      return controlType == other.controlType && controlValue == other.controlValue;
264
0
    }
265
  };
266
267
  /// @struct LdapAttribute
268
  /// A struct that represents an LDAP attribute
269
  struct LdapAttribute
270
  {
271
    /// Attribute description
272
    std::string type;
273
    /// A list of attribute values (zero or more)
274
    std::vector<std::string> values;
275
276
    /// Equality operator overload for this struct
277
    /// @param[in] other The value to compare with
278
    /// @return True if both values are equal, false otherwise
279
    bool operator==(const LdapAttribute& other) const
280
0
    {
281
0
      return type == other.type && values == other.values;
282
0
    }
283
  };
284
285
  /// @class LdapLayer
286
  /// Represents an LDAP message
287
  class LdapLayer : public Layer
288
  {
289
  public:
290
    /// A constructor to create a new LDAP message
291
    /// @param[in] messageId The LDAP message ID
292
    /// @param[in] operationType The LDAP operation type
293
    /// @param[in] messageRecords A vector of ASN.1 records that comprise the LDAP message
294
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
295
    /// will be created without LDAP controls
296
    LdapLayer(uint16_t messageId, LdapOperationType operationType, const std::vector<Asn1Record*>& messageRecords,
297
              const std::vector<LdapControl>& controls = std::vector<LdapControl>());
298
299
20.3k
    ~LdapLayer() override = default;
300
301
    /// @return The root ASN.1 record of the LDAP message. All of the message data will be under this record.
302
    /// If the Root ASN.1 record is malformed, an exception is thrown
303
    Asn1SequenceRecord* getRootAsn1Record() const;
304
305
    /// @return The ASN.1 record of the specific LDAP operation in this LDAP message. Each operation has a specific
306
    /// structure. If the Operation ASN.1 record is malformed, an exception is thrown
307
    Asn1ConstructedRecord* getLdapOperationAsn1Record() const;
308
309
    /// @return The LDAP message ID. If the ASN.1 record is malformed, an exception is thrown
310
    uint16_t getMessageID() const;
311
312
    /// @return A vector of LDAP controls in this message. If the message contains no controls then an empty
313
    /// vector is returned. If the Controls ASN.1 record is malformed, an exception is thrown
314
    std::vector<LdapControl> getControls() const;
315
316
    /// @return The LDAP operation of this message. If the Operation ASN.1 record is malformed, an exception is
317
    /// thrown
318
    virtual LdapOperationType getLdapOperationType() const;
319
320
    /// Most getter methods in this class throw an exception if the corresponding ASN.1 record is invalid.
321
    /// This is a wrapper method that allows calling these getters without adding a `try...catch` clause.
322
    /// It accepts the getter method and an out variable. It tries to call the getter and if no exception
323
    /// is thrown, the out variable will contain the result.
324
    ///
325
    /// Here is an example:
326
    /// @code
327
    /// uint16_t messageId;
328
    /// ldapLayer->tryGet(&pcpp::LdapLayer::getMessageID, messageId));
329
    /// @endcode
330
    ///
331
    /// We call getMessageID(), if no exception is thrown the variable messageId will hold the result
332
    ///
333
    /// @tparam Method The class method type
334
    /// @tparam ResultType The expected result type (for example: uint8_t, std::string, etc.)
335
    /// @param[in] method The class method to call
336
    /// @param[out] result An outvariable to contain the result if no exception is thrown
337
    /// @return True if no exception was thrown or false otherwise
338
    template <typename Method, typename ResultType> bool tryGet(Method method, ResultType& result)
339
    {
340
      return internalTryGet(this, method, result);
341
    }
342
343
    /// A static method that checks whether a source or dest port match those associated with the LDAP protocol
344
    /// @param[in] port The port number to check
345
    /// @return True if this is an LDAP port, false otherwise
346
    static bool isLdapPort(uint16_t port)
347
95.2k
    {
348
95.2k
      return port == 389;
349
95.2k
    }
350
351
    /// A static message to parse an LDAP message from raw data
352
    /// @param[in] data A pointer to the raw data
353
    /// @param[in] dataLen Size of the data in bytes
354
    /// @param[in] prevLayer A pointer to the previous layer
355
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
356
    /// @return An instance of LdapLayer if this is indeed an LDAP message, nullptr otherwise
357
    static LdapLayer* parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
358
359
    // implement abstract methods
360
361
    /// Tries to identify more LDAP messages in this packet if exist
362
    void parseNextLayer() override;
363
364
    /// @return The size of the LDAP message
365
    size_t getHeaderLen() const override
366
23.8k
    {
367
23.8k
      return m_Asn1Record->getTotalLength();
368
23.8k
    }
369
370
    void computeCalculateFields() override
371
3.46k
    {}
372
373
    OsiModelLayer getOsiModelLayer() const override
374
3.46k
    {
375
3.46k
      return OsiModelApplicationLayer;
376
3.46k
    }
377
378
    std::string toString() const override;
379
380
  protected:
381
    std::unique_ptr<Asn1Record> m_Asn1Record;
382
383
    LdapLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
384
              Packet* packet);
385
0
    LdapLayer() = default;
386
    void init(uint16_t messageId, LdapOperationType operationType, const std::vector<Asn1Record*>& messageRecords,
387
              const std::vector<LdapControl>& controls);
388
    virtual std::string getExtendedInfoString() const
389
3.06k
    {
390
3.06k
      return "";
391
3.06k
    }
392
393
    static constexpr int messageIdIndex = 0;
394
    static constexpr int operationTypeIndex = 1;
395
    static constexpr int controlsIndex = 2;
396
397
    static constexpr int controlTypeIndex = 0;
398
    static constexpr int controlValueIndex = 1;
399
400
    template <typename LdapClass, typename Method, typename ResultType>
401
    bool internalTryGet(LdapClass* thisPtr, Method method, ResultType& result)
402
    {
403
      try
404
      {
405
        result = std::mem_fn(method)(thisPtr);
406
        return true;
407
      }
408
      catch (...)
409
      {
410
        return false;
411
      }
412
    }
413
  };
414
415
  /// @class LdapResponseLayer
416
  /// An abstract class for representing an LDAP response message. It's the parent class
417
  /// for all response message layers
418
  class LdapResponseLayer : public LdapLayer
419
  {
420
  public:
421
    /// @return LDAP result code
422
    LdapResultCode getResultCode() const;
423
424
    /// @return An optional distinguished name (DN) that may be included in the response to a request
425
    /// targeting an entry that does not exist
426
    std::string getMatchedDN() const;
427
428
    /// @return An optional string that can provide additional information about the processing that
429
    /// was performed
430
    std::string getDiagnosticMessage() const;
431
432
    /// @return An optional list of one or more URIs that the client may use to re-try the operation
433
    /// somewhere else. If referral doesn't exist on the message, and empty vector is returned
434
    std::vector<std::string> getReferral() const;
435
436
  protected:
437
    static constexpr int resultCodeIndex = 0;
438
    static constexpr int matchedDNIndex = 1;
439
    static constexpr int diagnotsticsMessageIndex = 2;
440
    static constexpr int referralIndex = 3;
441
442
    static constexpr uint8_t referralTagType = 3;
443
444
0
    LdapResponseLayer() = default;
445
    LdapResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
446
                      Packet* packet)
447
6.49k
        : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
448
6.49k
    {}
449
450
    LdapResponseLayer(uint16_t messageId, LdapOperationType operationType, LdapResultCode resultCode,
451
                      const std::string& matchedDN, const std::string& diagnosticMessage,
452
                      const std::vector<std::string>& referral = std::vector<std::string>(),
453
                      const std::vector<LdapControl>& controls = std::vector<LdapControl>());
454
455
    void init(uint16_t messageId, LdapOperationType operationType, LdapResultCode resultCode,
456
              const std::string& matchedDN, const std::string& diagnosticMessage,
457
              const std::vector<std::string>& referral = std::vector<std::string>(),
458
              const std::vector<Asn1Record*>& additionalRecords = std::vector<Asn1Record*>(),
459
              const std::vector<LdapControl>& controls = std::vector<LdapControl>());
460
461
    std::string getExtendedInfoString() const override;
462
  };
463
464
  /// @class LdapBindRequestLayer
465
  /// Represents LDAP bind request operation
466
  class LdapBindRequestLayer : public LdapLayer
467
  {
468
  public:
469
    /// An enum to represent the bind request authentication type
470
    enum class AuthenticationType : uint8_t
471
    {
472
      /// Simple authentication
473
      Simple = 0,
474
      /// SASL authentication
475
      Sasl = 3,
476
      /// Unknown / not application authentication type
477
      NotApplicable = 255
478
    };
479
480
    /// @struct SaslAuthentication
481
    /// A struct to represent SASL authentication
482
    struct SaslAuthentication
483
    {
484
      /// The SASL mechanism
485
      std::string mechanism;
486
      /// Encoded SASL credentials
487
      std::vector<uint8_t> credentials;
488
489
      /// Equality operator overload for this struct
490
      /// @param[in] other The value to compare with
491
      /// @return True if both values are equal, false otherwise
492
      bool operator==(const SaslAuthentication& other) const
493
0
      {
494
0
        return mechanism == other.mechanism && credentials == other.credentials;
495
0
      }
496
497
      /// Inequality operator overload for this struct
498
      /// @param[in] other The value to compare with
499
      /// @return False if both values are equal, true otherwise
500
      bool operator!=(const SaslAuthentication& other) const
501
0
      {
502
0
        return !operator==(other);
503
0
      }
504
    };
505
506
    /// A constructor to create a new LDAP bind request message with simple authentication
507
    /// @param[in] messageId The LDAP message ID
508
    /// @param[in] version The LDAP protocol version that the client wants to use
509
    /// @param[in] name The DN of the user to authenticate
510
    /// @param[in] simpleAuthentication Simple authentication to use in this message
511
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
512
    /// will be created without LDAP controls
513
    LdapBindRequestLayer(uint16_t messageId, uint8_t version, const std::string& name,
514
                         const std::string& simpleAuthentication,
515
                         const std::vector<LdapControl>& controls = std::vector<LdapControl>());
516
517
    /// A constructor to create a new LDAP bind request message with SASL authentication
518
    /// @param[in] messageId The LDAP message ID
519
    /// @param[in] version The LDAP protocol version that the client wants to use
520
    /// @param[in] name The DN of the user to authenticate
521
    /// @param[in] saslAuthentication SASL authentication to use in this message
522
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
523
    /// will be created without LDAP controls
524
    LdapBindRequestLayer(uint16_t messageId, uint8_t version, const std::string& name,
525
                         const SaslAuthentication& saslAuthentication,
526
                         const std::vector<LdapControl>& controls = std::vector<LdapControl>());
527
528
    /// @return The LDAP protocol version that the client wants to use
529
    uint32_t getVersion() const;
530
531
    /// @return The DN of the user to authenticate
532
    std::string getName() const;
533
534
    /// @return The authentication type included in this message
535
    AuthenticationType getAuthenticationType() const;
536
537
    /// @return The simple authentication included in this message
538
    /// @throws std::invalid_argument if the message doesn't include simple authentication
539
    std::string getSimpleAuthentication() const;
540
541
    /// @return The SASL authentication included in this message
542
    /// @throws std::invalid_argument if the message doesn't include SASL authentication
543
    SaslAuthentication getSaslAuthentication() const;
544
545
    template <typename Method, typename ResultType> bool tryGet(Method method, ResultType& result)
546
    {
547
      return internalTryGet(this, method, result);
548
    }
549
550
  protected:
551
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
552
553
    LdapBindRequestLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
554
                         Packet* packet)
555
2.27k
        : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
556
2.27k
    {}
557
558
    std::string getExtendedInfoString() const override;
559
560
  private:
561
    static constexpr int versionIndex = 0;
562
    static constexpr int nameIndex = 1;
563
    static constexpr int credentialIndex = 2;
564
565
    static constexpr int saslMechanismIndex = 0;
566
    static constexpr int saslCredentialsIndex = 1;
567
  };
568
569
  /// @class LdapBindResponseLayer
570
  /// Represents LDAP bind response operation
571
  class LdapBindResponseLayer : public LdapResponseLayer
572
  {
573
  public:
574
    /// A constructor to create a new LDAP bind response message
575
    /// @param[in] messageId The LDAP message ID
576
    /// @param[in] resultCode The LDAP result code
577
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
578
    /// pass an empty string
579
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
580
    /// pass an empty string
581
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
582
    /// parameter. If not provided then referral won't be added to the message
583
    /// @param[in] serverSaslCredentials Encoded server SASL credentials for use in subsequent processing
584
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
585
    /// will be created without LDAP controls
586
    LdapBindResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
587
                          const std::string& diagnosticMessage,
588
                          const std::vector<std::string>& referral = std::vector<std::string>(),
589
                          const std::vector<uint8_t>& serverSaslCredentials = std::vector<uint8_t>(),
590
                          const std::vector<LdapControl>& controls = std::vector<LdapControl>());
591
592
    /// @return Encoded server SASL credentials for use in subsequent processing
593
    std::vector<uint8_t> getServerSaslCredentials() const;
594
595
  protected:
596
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
597
598
    static constexpr int serverSaslCredentialsTagType = 7;
599
600
    LdapBindResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
601
                          Packet* packet)
602
1.18k
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
603
1.18k
    {}
604
  };
605
606
  /// @class LdapUnbindRequestLayer
607
  /// Represents LDAP unbind operation
608
  class LdapUnbindRequestLayer : public LdapLayer
609
  {
610
  public:
611
    /// A constructor to create a new LDAP unbind message
612
    /// @param[in] messageId The LDAP message ID
613
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
614
    /// will be created without LDAP controls
615
    explicit LdapUnbindRequestLayer(uint16_t messageId,
616
                                    const std::vector<LdapControl>& controls = std::vector<LdapControl>());
617
618
    // Unbind request has no operation record
619
    Asn1ConstructedRecord* getLdapOperationAsn1Record() const = delete;
620
621
    LdapOperationType getLdapOperationType() const override
622
530
    {
623
530
      return LdapOperationType::UnbindRequest;
624
530
    }
625
626
    template <typename Method, typename ResultType> bool tryGet(Method method, ResultType& result)
627
    {
628
      return internalTryGet(this, method, result);
629
    }
630
631
  protected:
632
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
633
634
    LdapUnbindRequestLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
635
                           Packet* packet)
636
1.32k
        : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
637
1.32k
    {}
638
  };
639
640
  /// @class LdapSearchRequestLayer
641
  /// Represents LDAP search request operation
642
  class LdapSearchRequestLayer : public LdapLayer
643
  {
644
  public:
645
    /// @class SearchRequestScope
646
    /// An enum wrapper class for LDAP search request scope
647
    class SearchRequestScope
648
    {
649
    public:
650
      /// Define enum types and the corresponding int values
651
      enum Value : uint8_t
652
      {
653
        /// The search operation should only be performed against the entry specified as the search base DN
654
        BaseObject = 0,
655
        /// The search operation should only be performed against entries that are immediate subordinates
656
        /// of the entry specified as the search base DN
657
        SingleLevel = 1,
658
        /// The search operation should be performed against the entry specified as the search base
659
        /// and all of its subordinates to any depth
660
        WholeSubtree = 2,
661
        /// The search operation should be performed against any subordinate entries (to any depth) below the
662
        /// entry specified by the base DN should be considered, but the base entry itself
663
        /// should not be considered
664
        subordinateSubtree = 3,
665
        /// Unknown or unsupported scope
666
        Unknown = 255
667
      };
668
669
      SearchRequestScope() = default;
670
671
      // cppcheck-suppress noExplicitConstructor
672
      /// Construct SearchRequestScope from Value enum
673
      /// @param[in] value the scope enum value
674
640
      constexpr SearchRequestScope(Value value) : m_Value(value)
675
640
      {}
676
677
      /// @return A string representation of the scope value
678
      std::string toString() const;
679
680
      /// A static method that creates SearchRequestScope from an integer value
681
      /// @param[in] value The scope integer value
682
      /// @return The scope that corresponds to the integer value. If the integer value
683
      /// doesn't corresponds to any enum value, SearchRequestScope::Unknown is returned
684
      static SearchRequestScope fromUintValue(uint8_t value);
685
686
      // Allow switch and comparisons.
687
      constexpr operator Value() const
688
0
      {
689
0
        return m_Value;
690
0
      }
691
692
      // Prevent usage: if(LdapOperationType)
693
      explicit operator bool() const = delete;
694
695
    private:
696
      Value m_Value = SearchRequestScope::Unknown;
697
    };
698
699
    /// @class DerefAliases
700
    /// An enum wrapper class for LDAP search request dereferencing aliases
701
    class DerefAliases
702
    {
703
    public:
704
      /// Define enum types and the corresponding int values
705
      enum Value : uint8_t
706
      {
707
        /// Never dereferences aliases
708
        NeverDerefAliases = 0,
709
        /// Dereferences aliases only after name resolution
710
        DerefInSearching = 1,
711
        /// Dereferences aliases only during name resolution
712
        DerefFindingBaseObj = 2,
713
        /// Always dereference aliases
714
        DerefAlways = 3,
715
        /// Unknown value
716
        Unknown = 255
717
      };
718
719
      DerefAliases() = default;
720
721
      // cppcheck-suppress noExplicitConstructor
722
      /// Construct DerefAliases from Value enum
723
      /// @param[in] value the dereference alias enum value
724
0
      constexpr DerefAliases(Value value) : m_Value(value)
725
0
      {}
726
727
      /// @return A string representation of the dereference alias value
728
      std::string toString() const;
729
730
      /// A static method that creates DerefAliases from an integer value
731
      /// @param[in] value The dereference alias integer value
732
      /// @return The dereference alias that corresponds to the integer value. If the integer value
733
      /// doesn't corresponds to any enum value, DerefAliases::Unknown is returned
734
      static DerefAliases fromUintValue(uint8_t value);
735
736
      // Allow switch and comparisons.
737
      constexpr operator Value() const
738
0
      {
739
0
        return m_Value;
740
0
      }
741
742
      // Prevent usage: if(LdapOperationType)
743
      explicit operator bool() const = delete;
744
745
    private:
746
      Value m_Value = DerefAliases::Unknown;
747
    };
748
749
    /// A constructor to create a new LDAP search request message
750
    /// @param[in] messageId The LDAP message ID
751
    /// @param[in] baseObject The base object for the LDAP search request entry
752
    /// @param[in] scope The portion of the target subtree that should be considered
753
    /// @param[in] derefAliases The alias dereferencing behavior, which indicates how the server should treat
754
    /// any aliases encountered while processing the search
755
    /// @param[in] sizeLimit The maximum number of entries that should be returned from the search
756
    /// @param[in] timeLimit The time limit for the search in seconds
757
    /// @param[in] typesOnly If this is given a value of true, then it indicates that entries that match the
758
    /// search criteria should be returned containing only the attribute descriptions for the attributes
759
    /// contained in that entry but should not include the values for those attributes.
760
    /// If this is given a value of false, then it indicates that the attribute values should be included
761
    /// in the entries that are returned
762
    /// @param[in] filterRecord The filter for the search. Please note that parsing for the search filter
763
    /// doesn't exist yet. Therefore, the expected input value should be a plain ASN.1 record
764
    /// @param[in] attributes A set of attributes to request for inclusion in entries that match the search
765
    /// criteria and are returned to the client
766
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
767
    /// will be created without LDAP controls
768
    LdapSearchRequestLayer(uint16_t messageId, const std::string& baseObject, SearchRequestScope scope,
769
                           DerefAliases derefAliases, uint8_t sizeLimit, uint8_t timeLimit, bool typesOnly,
770
                           Asn1Record* filterRecord, const std::vector<std::string>& attributes,
771
                           const std::vector<LdapControl>& controls = std::vector<LdapControl>());
772
773
    /// @return The base object for the LDAP search request entry
774
    std::string getBaseObject() const;
775
776
    /// @return The portion of the target subtree that should be considered
777
    SearchRequestScope getScope() const;
778
779
    /// @return The alias dereferencing behavior
780
    DerefAliases getDerefAlias() const;
781
782
    /// @return The maximum number of entries that should be returned from the search
783
    uint8_t getSizeLimit() const;
784
785
    /// @return The time limit for the search in seconds
786
    uint8_t getTimeLimit() const;
787
788
    /// @return If this flag is true, then it indicates that entries that match the search criteria should be
789
    /// returned containing only the attribute descriptions for the attributes contained in that entry but
790
    /// should not include the values for those attributes. If this flag is false, then it indicates that the
791
    /// attribute values should be included in the entries that are returned
792
    bool getTypesOnly() const;
793
794
    /// @return The filter for the search. Please note that parsing for the search filter doesn't exist yet.
795
    /// Therefore, the return value is a plain ASN.1 record
796
    Asn1Record* getFilter() const;
797
798
    /// @return A list of search request attributes
799
    std::vector<std::string> getAttributes() const;
800
801
    template <typename Method, typename ResultType> bool tryGet(Method method, ResultType& result)
802
    {
803
      return internalTryGet(this, method, result);
804
    }
805
806
  protected:
807
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
808
809
    static constexpr int baseObjectIndex = 0;
810
    static constexpr int scopeIndex = 1;
811
    static constexpr int derefAliasIndex = 2;
812
    static constexpr int sizeLimitIndex = 3;
813
    static constexpr int timeLimitIndex = 4;
814
    static constexpr int typesOnlyIndex = 5;
815
    static constexpr int filterIndex = 6;
816
    static constexpr int attributesIndex = 7;
817
818
    LdapSearchRequestLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
819
                           Packet* packet)
820
1.60k
        : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
821
1.60k
    {}
822
823
    std::string getExtendedInfoString() const override;
824
  };
825
826
  /// @class LdapSearchResultEntryLayer
827
  /// Represents LDAP search result entry message
828
  class LdapSearchResultEntryLayer : public LdapLayer
829
  {
830
  public:
831
    /// A constructor to create a new LDAP search result entry message
832
    /// @param[in] messageId The LDAP message ID
833
    /// @param[in] objectName The entry's DN
834
    /// @param[in] attributes The entry's attributes
835
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
836
    /// will be created without LDAP controls
837
    LdapSearchResultEntryLayer(uint16_t messageId, const std::string& objectName,
838
                               const std::vector<LdapAttribute>& attributes,
839
                               const std::vector<LdapControl>& controls = std::vector<LdapControl>());
840
841
    /// @return The entry's DN
842
    std::string getObjectName() const;
843
844
    /// @return The entry's attributes
845
    std::vector<LdapAttribute> getAttributes() const;
846
847
    template <typename Method, typename ResultType> bool tryGet(Method method, ResultType& result)
848
    {
849
      return internalTryGet(this, method, result);
850
    }
851
852
  protected:
853
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
854
855
    static constexpr int objectNameIndex = 0;
856
    static constexpr int attributesIndex = 1;
857
    static constexpr int attributeTypeIndex = 0;
858
    static constexpr int attributeValueIndex = 1;
859
860
    LdapSearchResultEntryLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen,
861
                               Layer* prevLayer, Packet* packet)
862
493
        : LdapLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
863
493
    {}
864
  };
865
866
  /// @class LdapSearchResultDoneLayer
867
  /// Represents LDAP search result done message
868
  class LdapSearchResultDoneLayer : public LdapResponseLayer
869
  {
870
  public:
871
    /// A constructor to create a new LDAP search result done message
872
    /// @param[in] messageId The LDAP message ID
873
    /// @param[in] resultCode The LDAP result code
874
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
875
    /// pass an empty string
876
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
877
    /// pass an empty string
878
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
879
    /// parameter. If not provided then referral won't be added to the message
880
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
881
    /// will be created without LDAP controls
882
    LdapSearchResultDoneLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
883
                              const std::string& diagnosticMessage,
884
                              const std::vector<std::string>& referral = std::vector<std::string>(),
885
                              const std::vector<LdapControl>& controls = std::vector<LdapControl>())
886
        : LdapResponseLayer(messageId, LdapOperationType::SearchResultDone, resultCode, matchedDN,
887
                            diagnosticMessage, referral, controls)
888
0
    {}
889
890
  protected:
891
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
892
893
    LdapSearchResultDoneLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen,
894
                              Layer* prevLayer, Packet* packet)
895
2.33k
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
896
2.33k
    {}
897
  };
898
899
  /// @class LdapModifyResponseLayer
900
  /// Represents LDAP modify response message
901
  class LdapModifyResponseLayer : public LdapResponseLayer
902
  {
903
  public:
904
    /// A constructor to create a new LDAP modify response message
905
    /// @param[in] messageId The LDAP message ID
906
    /// @param[in] resultCode The LDAP result code
907
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
908
    /// pass an empty string
909
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
910
    /// pass an empty string
911
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
912
    /// parameter. If not provided then referral won't be added to the message
913
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
914
    /// will be created without LDAP controls
915
    LdapModifyResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
916
                            const std::string& diagnosticMessage,
917
                            const std::vector<std::string>& referral = std::vector<std::string>(),
918
                            const std::vector<LdapControl>& controls = std::vector<LdapControl>())
919
        : LdapResponseLayer(messageId, LdapOperationType::ModifyResponse, resultCode, matchedDN, diagnosticMessage,
920
                            referral, controls)
921
0
    {}
922
923
  protected:
924
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
925
926
    LdapModifyResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
927
                            Packet* packet)
928
830
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
929
830
    {}
930
  };
931
932
  /// @class LdapAddResponseLayer
933
  /// Represents LDAP add response message
934
  class LdapAddResponseLayer : public LdapResponseLayer
935
  {
936
  public:
937
    /// A constructor to create a new LDAP add response message
938
    /// @param[in] messageId The LDAP message ID
939
    /// @param[in] resultCode The LDAP result code
940
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
941
    /// pass an empty string
942
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
943
    /// pass an empty string
944
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
945
    /// parameter. If not provided then referral won't be added to the message
946
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
947
    /// will be created without LDAP controls
948
    LdapAddResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
949
                         const std::string& diagnosticMessage,
950
                         const std::vector<std::string>& referral = std::vector<std::string>(),
951
                         const std::vector<LdapControl>& controls = std::vector<LdapControl>())
952
        : LdapResponseLayer(messageId, LdapOperationType::AddResponse, resultCode, matchedDN, diagnosticMessage,
953
                            referral, controls)
954
0
    {}
955
956
  protected:
957
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
958
959
    LdapAddResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
960
                         Packet* packet)
961
155
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
962
155
    {}
963
  };
964
965
  /// @class LdapDeleteResponseLayer
966
  /// Represents LDAP delete response message
967
  class LdapDeleteResponseLayer : public LdapResponseLayer
968
  {
969
  public:
970
    /// A constructor to create a new LDAP delete response message
971
    /// @param[in] messageId The LDAP message ID
972
    /// @param[in] resultCode The LDAP result code
973
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
974
    /// pass an empty string
975
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
976
    /// pass an empty string
977
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
978
    /// parameter. If not provided then referral won't be added to the message
979
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
980
    /// will be created without LDAP controls
981
    LdapDeleteResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
982
                            const std::string& diagnosticMessage,
983
                            const std::vector<std::string>& referral = std::vector<std::string>(),
984
                            const std::vector<LdapControl>& controls = std::vector<LdapControl>())
985
        : LdapResponseLayer(messageId, LdapOperationType::DeleteResponse, resultCode, matchedDN, diagnosticMessage,
986
                            referral, controls)
987
0
    {}
988
989
  protected:
990
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
991
992
    LdapDeleteResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen, Layer* prevLayer,
993
                            Packet* packet)
994
335
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
995
335
    {}
996
  };
997
998
  /// @class LdapModifyDNResponseLayer
999
  /// Represents LDAP modify DN response message
1000
  class LdapModifyDNResponseLayer : public LdapResponseLayer
1001
  {
1002
  public:
1003
    /// A constructor to create a new LDAP modify DN response message
1004
    /// @param[in] messageId The LDAP message ID
1005
    /// @param[in] resultCode The LDAP result code
1006
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
1007
    /// pass an empty string
1008
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
1009
    /// pass an empty string
1010
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
1011
    /// parameter. If not provided then referral won't be added to the message
1012
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
1013
    /// will be created without LDAP controls
1014
    LdapModifyDNResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
1015
                              const std::string& diagnosticMessage,
1016
                              const std::vector<std::string>& referral = std::vector<std::string>(),
1017
                              const std::vector<LdapControl>& controls = std::vector<LdapControl>())
1018
        : LdapResponseLayer(messageId, LdapOperationType::ModifyDNResponse, resultCode, matchedDN,
1019
                            diagnosticMessage, referral, controls)
1020
0
    {}
1021
1022
  protected:
1023
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
1024
1025
    LdapModifyDNResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen,
1026
                              Layer* prevLayer, Packet* packet)
1027
715
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
1028
715
    {}
1029
  };
1030
1031
  /// @class LdapCompareResponseLayer
1032
  /// Represents LDAP compare response message
1033
  class LdapCompareResponseLayer : public LdapResponseLayer
1034
  {
1035
  public:
1036
    /// A constructor to create a new LDAP compare response message
1037
    /// @param[in] messageId The LDAP message ID
1038
    /// @param[in] resultCode The LDAP result code
1039
    /// @param[in] matchedDN The distinguished name (DN) to set on the message. If not applicable
1040
    /// pass an empty string
1041
    /// @param[in] diagnosticMessage The additional information to set on the message. If not applicable
1042
    /// pass an empty string
1043
    /// @param[in] referral A list of URIs to re-try the operation somewhere else. This is an optional
1044
    /// parameter. If not provided then referral won't be added to the message
1045
    /// @param[in] controls A vector of LDAP controls. This is an optional parameter, if not provided the message
1046
    /// will be created without LDAP controls
1047
    LdapCompareResponseLayer(uint16_t messageId, LdapResultCode resultCode, const std::string& matchedDN,
1048
                             const std::string& diagnosticMessage,
1049
                             const std::vector<std::string>& referral = std::vector<std::string>(),
1050
                             const std::vector<LdapControl>& controls = std::vector<LdapControl>())
1051
        : LdapResponseLayer(messageId, LdapOperationType::CompareResponse, resultCode, matchedDN, diagnosticMessage,
1052
                            referral, controls)
1053
0
    {}
1054
1055
  protected:
1056
    friend LdapLayer* LdapLayer::parseLdapMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
1057
1058
    LdapCompareResponseLayer(std::unique_ptr<Asn1Record> asn1Record, uint8_t* data, size_t dataLen,
1059
                             Layer* prevLayer, Packet* packet)
1060
945
        : LdapResponseLayer(std::move(asn1Record), data, dataLen, prevLayer, packet)
1061
945
    {}
1062
  };
1063
1064
  inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapControl& control)
1065
0
  {
1066
0
    os << "{" << control.controlType << ", " << control.controlValue << "}";
1067
0
    return os;
1068
0
  }
1069
1070
  inline std::ostream& operator<<(std::ostream& os, const pcpp::LdapAttribute& attr)
1071
0
  {
1072
0
    os << "{" << attr.type << ", {";
1073
0
1074
0
    std::string separator;
1075
0
    for (const auto& value : attr.values)
1076
0
    {
1077
0
      os << separator << value;
1078
0
      if (separator.empty())
1079
0
      {
1080
0
        separator = ", ";
1081
0
      }
1082
0
    }
1083
0
1084
0
    os << "}}";
1085
0
    return os;
1086
0
  }
1087
1088
  inline std::ostream& operator<<(std::ostream& os,
1089
                                  const pcpp::LdapBindRequestLayer::SaslAuthentication& saslAuthentication)
1090
0
  {
1091
0
    os << "{" << saslAuthentication.mechanism << ", {";
1092
0
1093
0
    std::string separator;
1094
0
    for (const auto& value : saslAuthentication.credentials)
1095
0
    {
1096
0
      os << separator << "0x" << std::hex << static_cast<int>(value) << std::dec;
1097
0
      if (separator.empty())
1098
0
      {
1099
0
        separator = ", ";
1100
0
      }
1101
0
    }
1102
0
1103
0
    os << "}}";
1104
0
    return os;
1105
0
  }
1106
}  // namespace pcpp