Skip to content

DSML gateway: NPE on abandonRequest leaks the LDAP connection, and NPE when Content-Type is absent #809

Description

@vharseko

Two unhandled NullPointerExceptions in DSMLServlet.doPost(), found while triaging an unrelated report against the same file. Both escape doPost() and reach the container as a 500; the first one additionally leaks the LDAP connection to the directory server.

File: opendj-dsml-servlet/src/main/java/org/opends/dsml/protocol/DSMLServlet.java

1. abandonRequest causes an NPE and leaks the LDAP connection

performLDAPRequest() deliberately returns null for an abandon request:

} else if (request instanceof AbandonRequest) {
  // Process the abandon request.
  ...
  ao.doOperation(objFactory, ar, controls);
  return null;
}

The caller guards the null only when adding to the response list, then dereferences it unconditionally:

JAXBElement<?> result = performLDAPRequest(connection, objFactory, proxyAuthzControl, request);
if ( result != null ) {
  batchResponses.add(result);
}
// evaluate response to check if an error occurred
Object o = result.getValue();   // NPE when the request was an abandonRequest

abandonRequest is a valid child of batchRequest in DSMLv2.xsd, so <abandonRequest abandonID="1"/> inside a batch reaches this line whenever DSMLAbandonOperation.doOperation succeeds (it only throws for a non-numeric abandonID, which would be turned into an error response instead).

The block that opens the connection is not wrapped in try/finally:

if ( connected ) {
  for (DsmlMessage request : list) { ... }   // NPE thrown here
}
// close connection to LDAP server
if ( connection != null ) {
  connection.close(nextMessageID);           // never reached
}

so connection.close(...) is skipped. org.opends.server.tools.LDAPConnection has no finalizer and closes its socket only in close(), so the socket to the directory server stays open. Every such request leaks one connection, which is trivially repeatable and ends in connection-handler exhaustion on the server.

Reproduce: POST a SOAP batch containing a single <abandonRequest abandonID="1"/> to /DSMLServlet. Expected: a batch response with no entry for the abandon (per DSMLv2). Actual: HTTP 500, and one leaked LDAP connection per request.

Fix: continue when result is null, and move the connection cleanup into a finally.

2. NPE when the request has no Content-Type header

messageFactory is assigned only inside the header loop, when a content-type header is present and matches SOAP 1.1 or 1.2. A POST without a Content-Type header is legal HTTP, and leaves messageFactory at null:

SOAPMessage message = messageFactory.createMessage(mimeHeaders, is);   // NPE

Only SOAPException is caught there, so the NPE escapes doPost() → 500.

The same field is dereferenced again on the response path, which produces a second, quieter symptom: when an error response was already queued (for example invalid credentials) the parsing block is skipped, and

sendResponse(doc, messageFactory, messageContentType, res);

throws the NPE inside a catch (Exception e) { e.printStackTrace(); }. The client then receives an empty HTTP 200 instead of the credentials error.

Fix: treat a missing or unsupported Content-Type as a malformed request and return a proper malformedRequest batch response (a default message factory for the response path, or an early rejection), rather than dereferencing null.

Both are long-standing and predate the Open Identity Platform fork.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugjavaPull requests that update java code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions