Main Content

CWE Rule 90

Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection')

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).

Rule Definition

The software constructs all or part of an LDAP query using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended LDAP query when it is sent to a downstream component.

Polyspace Implementation

This rule checker checks for the issue LDAP Injection.

Examples

expand all

Issue

LDAP Injection 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. Functions include OpenLDAP search functions such as ldap_search(), ldap_search_ext(), and so on. In all search functions, the second parameter (base) and the fourth parameter (search filter) are considered as sensitive to untrusted inputs. Untrusted sources can include read operations on the standard input stdin using the fread() or fgets() function.

Risk

The search base or search filters in an LDAP query are often constructed from user inputs. However, if these inputs are directly used in the construction of an LDAP query without any validation or sanitization, they are vulnerable to LDAP injection. A malicious user can inject unintended LDAP queries masquerading as input, resulting in secure user information being compromised.

For instance, an LDAP search filter, such as the following, where username is obtained from user inputs is vulnerable to LDAP injection:

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

Fix

Validate user inputs if they are used in the construction of the search base or search filters in an LDAP query.

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 that is validated. For instance, the first argument in the above signature could be the string to be validated.

To make the LDAP injection checker aware of this function

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

    .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

Example – LDAP Filter Created from Untrusted User Input
#include <stdio.h>
#include <stddef.h>
#include <sys/time.h>
#include <ldap.h>

int LDAPSearch()
{
    const char* hostName = "fabrikam.com";
    LDAP* ld;

    // Creating LDAP search parameters
    ld = ldap_open(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[512];
    char password[512];

    fread(idToSearch, sizeof(idToSearch), 1, stdin);
    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(ld, base, scope, filter, attrs, attrsonly); //Noncompliant 

    return 0;
}

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

Correction – Validation User Input Before Use in LDAP Query

You can write your own sanitization function to validate or sanitize the input before using it to construct an LDAP search filter. In this example, assume that a 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;

    // Creating LDAP search parameters
    ld = ldap_open(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[512];
    char password[512];

    fread(idToSearch, sizeof(idToSearch), 1, stdin);
    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(ld, base, scope, filter, attrs, attrsonly);

    return 0;
}

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

  1. Specify the following 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 the following option with the analysis:

    -code-behavior-specifications sanitizeFunctions.dl

    See also -code-behavior-specifications.

Check Information

Category: Data Neutralization Issues

Version History

Introduced in R2023a