Main Content

LDAP injection

Data read from an untrusted source is used in the construction of an LDAP query

Since R2023a

Description

This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

This defect occurs when data read from an untrusted source such as standard input is used in the construction of an LDAP query.

This defect checker detects the flow of data from an untrusted source to a function that constructs an LDAP query. The checker recognizes OpenLDAP search functions such as ldap_search(), ldap_search_ext(), and so on. In all search functions, the checker considers the second parameter (base) and the fourth parameter (search filter) as sensitive to untrusted inputs. Untrusted sources can include strings read from the standard input stdin using the fread() or fgets() function. Note that unlike other Bug Finder checkers, this checker skips functions not called directly or indirectly from the main function (if there is a main).

Note that the defect checker is not available in the Polyspace user interface and is disabled even if you select the value all for the option Find defects (-checkers). For the issue to be detected, the checker must be enabled explicitly using the option -checkers LDAP_INJECTION.

Risk

If you construct the search base or search filters in an LDAP query from user inputs but use the inputs directly in the LDAP query without any validation or sanitization, they are vulnerable to LDAP injection. A malicious user can inject unintended LDAP queries masquerading as input, which can compromise secure user information.

For instance, in this LDAP search filter, the variable username is obtained from user inputs. The search filter is vulnerable to LDAP injection.

(&(name=username)(pass=password))
If the user enters *)(name=*))(|(name=* for username, the search filter becomes:
(&(name=*)(name=*))(|(name=*)(pass=password))
The resulting search filter is trivially true and bypasses the authentication mechanism built into the search filter using the variable password.

Fix

Validate user inputs if you use them in the construction of the search base or search filters in an LDAP query. For more information on how to make the checker aware of your validation functions, see the next section.

Extend Checker

If you validate user inputs using dedicated functions, you can make the LDAP injection checker aware of your validation functions. Suppose that the function ldap_validate_inputs checks user inputs for malicious entries.

int ldap_validate_inputs(
     char *,    /* String to check */
     int);      /* Length of string */
Suppose that the n-th parameter of this function is the user input to validate. For instance, the first argument in the above signature could be the string to validate.

To make the LDAP injection checker aware of this function:

  1. In a file with extension .dl, add:

    .include "models/interfaces/ldap.dl"
    
    Ldap.Basic.sanitizing("ldap_validate_inputs", $OutParameterDeref(n-1)).
    
    If n is 1 (that is, the first parameters of the function is the parameter of interest), then the statement becomes:
    .include "models/interfaces/ldap.dl"
    
    Ldap.Basic.sanitizing("ldap_validate_inputs", $OutParameterDeref(0)).
    

  2. Specify this file using the option -code-behavior-specifications. For instance, if the file is named ldapAdditionalFunctions.dl, use the analysis option:

    -code-behavior-specifications ldapAdditionalFunctions.dl

Examples

expand all

#include <stdio.h>
#include <stddef.h>
#include <sys/time.h>
#include <ldap.h>

int LDAPSearch()
{
    const char* hostName = "fabrikam.com";
    LDAP* ld;
    LDAPControl **serverctrls;
    LDAPControl **clientctrls;
    struct timeval *timeoutp;
    int sizelimit;
    int *msgidp;

    // Creating LDAP search parameters
    ld = ldap_initialize(hostName, LDAP_PORT);

    const char* base = "DC=fabrikam,DC=com";
    const int scope = LDAP_SCOPE_SUBTREE;
    char* attrs[6];
    attrs[0] = "cn";
    attrs[1] = "company";
    attrs[2] = "department";
    attrs[3] = "telephoneNumber";
    attrs[4] = "memberOf";
    attrs[5] = NULL;
    const int attrsonly = 0;

    // Creating LDAP search filter from untrusted source
    char filter[1024] = "";
    char* idToSearch="EMPLOYEE_ID_NO";
    char password[512];

    fread(password, sizeof(password), 1, stdin);

    strcat(filter, "(&(uid=");
    strcat(filter, idToSearch);
    strcat(filter, ")(pass=");
    strcat(filter, password);
    strcat(filter, "))");

    // Performing LDAP search
    ldap_search_ext(ld, base, scope, filter, 
    attrs, attrsonly, serverctrls, clientctrls, timeoutp, sizelimit, msgidp);

    return 0;
}

In this example, the fourth argument to ldap_search_ext(), that is, the variable filter (representing the LDAP search filter), is constructed from the untrusted user input password.

Correction – Validate User Input Before Use in LDAP Query

You can write your own sanitization function to validate or sanitize the input before using the input to construct an LDAP search filter. In this example, assume that the function ldap_validate_inputs() validates the user input and removes elements that do not belong to an allowlist.

#include <stdio.h>
#include <stddef.h>
#include <sys/time.h>
#include <ldap.h>

extern void ldap_validate_inputs(char* s, size_t n);

int LDAPSearch()
{
    const char* hostName = "fabrikam.com";
    LDAP* ld;
    LDAPControl **serverctrls;
    LDAPControl **clientctrls;
    struct timeval *timeoutp;
    int sizelimit;
    int *msgidp;

    // Creating LDAP search parameters
    ld = ldap_initialize(hostName, LDAP_PORT);

    const char* base = "DC=fabrikam,DC=com";
    const int scope = LDAP_SCOPE_SUBTREE;
    char* attrs[6];
    attrs[0] = "cn";
    attrs[1] = "company";
    attrs[2] = "department";
    attrs[3] = "telephoneNumber";
    attrs[4] = "memberOf";
    attrs[5] = NULL;
    const int attrsonly = 0;

    // Creating LDAP search filter from untrusted source
    char filter[1024] = "";
    char* idToSearch="EMPLOYEE_ID_NO";
    char password[512];

    fread(password, sizeof(password), 1, stdin);

    strcat(filter, "(&(uid=");
    strcat(filter, idToSearch);
    strcat(filter, ")(pass=");
    strcat(filter, password);
    strcat(filter, "))");

    // Sanitize filter
    ldap_validate_inputs(filter, sizeof(filter));

    // Performing LDAP search
    ldap_search_ext(ld, base, scope, filter, 
    attrs, attrsonly, serverctrls, clientctrls, timeoutp, sizelimit, msgidp);

    return 0;
}

To make the Polyspace analysis aware of the nature of your sanitization function:

  1. Specify this code in a file with extension .dl (for instance, sanitizeFunctions.dl):

    .include "models/interfaces/ldap.dl"
    
    Ldap.Basic.sanitizing("ldap_validate_inputs", $OutParameterDeref(0)).
    

  2. Specify this option with the analysis:

    -code-behavior-specifications sanitizeFunctions.dl

    See also -code-behavior-specifications.

Result Information

Group: Security
Language: C | C++
Default: Off
Command-Line Syntax: LDAP_INJECTION
Impact: High

Version History

Introduced in R2023a