Menu

Paged search problem with Oracle OID LDAP Server

2016-11-28
2016-12-15
  • Sergey Klyushin

    Sergey Klyushin - 2016-11-28

    Hello,
    We recently switched from JNDI to UnboundID LDAP SDK for Java and we got paged search problem with Oracle OID LDAP Server (Oracle Internet Directory 11g R1) that we didn’t have while using JNDI.
    Looks like Oracle OID LDAP server does not encode properly response to paged search request.

    Looking at the trace with OID LDAP Server, WireShark clearly displays error in OID encoding: "BER Error: This filed lies beyond the end of the known sequence definition. BER Error: Unknown field in Sequence"

    Transmission Control Protocol, Src Port: interserver (3060), Dst Port: 50559 (50559), Seq: 1, Ack: 118, Len: 630
    Lightweight Directory Access Protocol
    LDAPMessage searchResEntry(2) "ou=People,dc=evs,dc=local" [5 results]
    messageID: 2
    protocolOp: searchResEntry (4)
    searchResEntry
    [Response To: 1]
    [Time: 0.005425000 seconds]
    Lightweight Directory Access Protocol
    LDAPMessage searchResEntry(2) "uid=tedwards,ou=People,dc=evs,dc=local" [5 results]
    messageID: 2
    protocolOp: searchResEntry (4)
    searchResEntry
    [Response To: 1]
    [Time: 0.005425000 seconds]
    Lightweight Directory Access Protocol
    LDAPMessage searchResEntry(2) "uid=miketest,ou=People,dc=evs,dc=local" [5 results]
    messageID: 2
    protocolOp: searchResEntry (4)
    searchResEntry
    [Response To: 1]
    [Time: 0.005425000 seconds]
    Lightweight Directory Access Protocol
    LDAPMessage searchResEntry(2) "uid=blackler,ou=People,dc=evs,dc=local" [5 results]
    messageID: 2
    protocolOp: searchResEntry (4)
    searchResEntry
    [Response To: 1]
    [Time: 0.005425000 seconds]
    Lightweight Directory Access Protocol
    LDAPMessage searchResEntry(2) "uid=sclarke,ou=People,dc=evs,dc=local" [5 results]
    messageID: 2
    protocolOp: searchResEntry (4)
    searchResEntry
    [Response To: 1]
    [Time: 0.005425000 seconds]
    Lightweight Directory Access Protocol
    LDAPMessage searchResDone(2) success [5 results]
    messageID: 2
    protocolOp: searchResDone (5)
    searchResDone
    resultCode: success (0)
    matchedDN:
    errorMessage:
    BER Error: This field lies beyond the end of the known sequence definition.
    [Expert Info (Warning/Malformed): BER Error: Unknown field in Sequence]
    [BER Error: Unknown field in Sequence]
    [Severity level: Warning]
    [Group: Malformed]
    [Response To: 1]
    [Time: 0.005425000 seconds]

    As a result, UnboundID LDAP library does NOT return cookie for next page request (to be more precise – doesn’t return PagedSearch Control).
    At the same time UnboundID LDAP SDK does NOT return error, so it looks like all search entries are returned in the first reply.

    Looking in binaries reply, I can see that Oracle OID LDAP server does return PagedSearch Control and cookie for next request.
    JNDI decodes reply ignoring encoding error, while UnboundID doesn’t.

    Do you have any suggestions or work around this problem?
    Do you know, if Oracle OID LDAP fixed paged search encoding problem in some hot fixes or updates?

    Thanks in advance,
    Sergey

     
  • Neil Wilson

    Neil Wilson - 2016-11-28

    I took a look at the issue, and the problem is exacty the same as the other issue. The Oracle server is incorrectly encoding the response. The LDAP SDK isn't doing anything wrong, so there's nothing to be resolved on the LDAP SDK side. It's completely up to Oracle to fix the problem in their server.

    Since I went ahead and analyzed the packet traces before I saw this update that pointed you at another comment on the same issue, I've included my analysis of that below, which I was going to use as my response:

    I’ve taken a look at the two packet captures, and I can confirm that the response returned by the OID server is malformed. The short explanation is that OID is incorrectly placing the response control inside the search result done protocol op element of the LDAP message instead of the portion of the LDAP message where controls are supposed to go. This is a result of a single-byte error in the response, where the server incorrectly uses 31 in the 28th byte of the encoded message where 07 would have been the correct value.

    A longer explanation is provided below if you’re curious, or if you want to reach out to Oracle with additional information about the problem in the hopes that they will fix their broken server, but ultimately, the problem here is not in the LDAP SDK.

    All LDAP packets are encoded in LDAP messages, using the ASN.1 Basic Encoding Rules (BER). An LDAP message is a sequence with two or three elements:

    • The LDAP message ID, which is used to correlate requests and responses
    • The protocol op, which contains the body of the LDAP request or response
    • An optional set of controls to accompany the request or response

    The raw bytes that comprise the OID server’s response are as follows:

    30 84 00 00 00 47 02 01 02 65 84 00 00 00 3e 0a
    01 00 04 00 04 00 a0 84 00 00 00 31 30 84 00 00
    00 2b 04 16 31 2e 32 2e 38 34 30 2e 31 31 33 35
    35 36 2e 31 2e 34 2e 33 31 39 01 01 00 04 0e 30
    84 00 00 00 08 02 01 00 04 03 63 66 66
    

    And here is a decoded representation:

    30 84 00 00 00 47 — Begin a 71-byte LDAP message
       02 01 02 — LDAP message ID 2
       65 84 00 00 00 3e — Begin a 62-byte search result done LDAP protocol
             op.  This is where the problem lies, because that last
             byte should actually be 07 (to indicate that the protocol
             op only contains 7 bytes) instead of 3e.
          0a 01 00 — Result code 0 (success)
          04 00 — Empty matched DN
          04 00 — Empty diagnostic message
          a0 84 00 00 00 31 — Begin a 49-byte element with a BER type that
                is inappropriate for this location in the LDAP message.
                However, because the only valid thing that can go
                in this portion of a search result done protocol op
                is an optional set of referral URLs, and because
                this element can actually be decoded in the way that
                the LDAP SDK expects to be able to parse a set of
                referral URLs (even though the “URL” in this case is
                very weird-looking), that’s what the LDAP SDK
                 interprets it as.
          30 84 00 00 00 2b — The LDAP SDK interprets this as indicating
                 the start of a 43-byte referral URL, and the remaining
                 43 bytes as the content of that URL.
             04 16 31 2e 32 2e 38 34 30 2e 31 31 33 35 35 36 ↵
             2e 31 2e 34 2e 33 31 39 01 01 00 04 0e 30 84 00 ↵
             00 00 08 02 01 00 04 03 63 66 66
    

    So basically, there is a single-byte error in the response that causes the server to incorrectly place the control in the LDAP search result entry protocol op instead of in the portion of the result that is supposed to hold the controls. And because of pure chance, that mistake just happens to result in something that the LDAP SDK is able to interpret as what it considers to be a valid response, since the LDAP SDK doesn’t check the BER type of this element because there is only one thing that it is allowed to be and because it can be interpreted as that.

    If the LDAP SDK had been super-strict about checking the BER type of every element inside the response (which it does in some cases), then it would have detected the invalid data and thrown an LDAPException with a DECODING_ERROR result, making the problem more visible. However, I think that the LDAP SDK’s behavior in this case is acceptable, since it has a reasonable expectation that the server knows how to correctly encode the response, and since it can actually decode the errant element as the only valid thing that can go in that position of a search result done message.

    I don’t see any need to make any change to the LDAP SDK as a result of this. The OID server is demonstrably in the wrong, and Oracle should fix their product. The fact that JNDI is able to handle this is somewhat puzzling, but it suggests that Oracle is aware of the problem and has worked around it in their client implementation rather than fixing the problem on the server.

     
  • Sergey Klyushin

    Sergey Klyushin - 2016-11-28

    Hello Neil
    Thank you for your prompt response.
    Sergey

     
  • Sergey Klyushin

    Sergey Klyushin - 2016-12-15

    Hello Neil
    I am still trying to convince Oracle that bug is in OID. Personally, I am convinced, but it's really hard to pass Oracle support -:)
    Could you help me, please, with paged control syntax for UnboundID ldapsearch?
    I see that it should be in the format
    -J, --control {oid}[:{criticality}[:{stringValue}|::{base64Value}]]
    Information about a control to include in search requests.

    I know the first part,but I can't find the value
    -J 1.2.840.113556.1.4.319:true:[:{stringValue}|::{base64Value}]

    What should be in [:{stringValue}|::{base64Value}]?

    Thanks in advance,
    Sergey

     
  • Neil Wilson

    Neil Wilson - 2016-12-15

    The value of the simple paged results control is encoded with the ASN.1 Basic Encoding Rules (BER), so it's definitely not obvious how to create it unless you're familiar with BER. However, you can create it with the following program:

    import com.unboundid.ldap.sdk.controls.SimplePagedResultsControl;
    import com.unboundid.util.Base64;
    import com.unboundid.util.StaticUtils;
    
    public class SimplePagedResultsControlTest
    {
      public static void main(final String... args)
             throws Exception
      {
        final SimplePagedResultsControl c =
             new SimplePagedResultsControl(100, null);
        System.out.println("Control OID:  " + c.getOID());
        System.out.println("Control string representation:  " + c);
        System.out.println();
    
        final byte[] valueBytes = c.getValue().getValue();
        System.out.println("Control value bytes:");
        System.out.println(StaticUtils.toHexPlusASCII(valueBytes, 0));
        System.out.println();
    
        System.out.println("Base64-encoded control value bytes:  " +
             Base64.encode(valueBytes));
        System.out.println();
      }
    }
    

    The final base64-encoded value that this generates is "MAUCAWQEAA==", and then you can use that with ldapsearch like:

    $ tools/ldapsearch --hostname ds.example.com \
         --port 389 \
         --bindDN "uid=jdoe,ou=People,dc=example,dc=com" \
         --bindPassword password \
         --baseDN "dc=example,dc=com" \
         --scope sub \
         --control 1.2.840.113556.1.4.319:true::MAUCAWQEAA== \
         "(objectClass=person)"
    

    This will generate the request for the first page of results. Unfortunately, it's not as easy to continue with the second page with the ldapsearch command-line tool because you have to get the response back, extract the cookie from it, and then figure out how to include that in a new encoded representation of the simple paged results control.

    I'm actually currently in the middle of updating the searchrate tool to include suport for specific controls (including the simple paged results control). I'll also look at updating the ldapsearch tool to provide improved support for specific controls like this in addition to the generic control argument that it already offers.

     
  • Sergey Klyushin

    Sergey Klyushin - 2016-12-15

    Hello Neil
    The example works perfect! I will update Oracle SR.
    FYI
    Sun ONE / iPlanet Directory Server and OpenDJ also have the same "problem" with OID.
    OpenDJ returns error on reply, while iPlanet same as UnboudID returns just first page, but not error.

    I have to do some "trick" with UnboundID SDK to detect that there is error: I check if paged control is returned by server and I consider that if control is not returned, it means error, even in case when there are no more entries on the server. Otherwise I can't distinguish between encoding error and that there are no more entries on the server. I hope that this is correct approach and won't break me with other servers. What would you say? Should server return paged control in reply with no / null coockie or server may not return paged control at all, if there are no more entries?

    BTW, I already convinced Oracle to open "Bug 25178637 : PAGED SEARCH PROBLEM WITH INTERNET DIRECTORY LDAP SERVER" https://support.oracle.com/epmos/faces/BugDisplay?_afrLoop=486353422389894&id=25178637&_afrWindowMode=0&_adf.ctrl-state=vx8r85opw_4

    However, Oracle opened it as "Enhancement" to certify UnboundID.

    I am trying to change it to bug/defect and your help is really appreciated.

    Thanks,
    Sergey

     
  • Neil Wilson

    Neil Wilson - 2016-12-15

    If the request includes a simple paged results request control that is marked critical and the result code is SUCCESS, then it should be safe to assume that a valid response should include a simple paged results response control. Even when there are no more pages of results, there should still be a response control; it just wouldn't have a cookie.

    If the simple paged results request control is not marked critical, then it would be valid behavior for the server to ignore it if it can't process it for some reason, and if the server ignores the request control then there wouldn't be a corresponding response control.

    Similarly, if a problem occurs during processing that causes the server to return a non-success response, then it would also be valid for the server to not include a simple paged results response control in that response.

     

Log in to post a comment.