Skip to content

Commit

Permalink
Normalise group check DNs and LDAP DNs
Browse files Browse the repository at this point in the history
Some special characters can be escaped such as ',' can be escaped either as \2c or \2C or \,
We don't care about case, because we do case insensitive matches on DNs, which isn't
entirely correct, but it's good enough.

We do care about format. Wherever we find the \xx version of a special char, we need to convert
it to the \special form, so strcmps work as expected.
  • Loading branch information
arr2036 committed Nov 20, 2014
1 parent ae0caea commit 86495f2
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 4 deletions.
10 changes: 10 additions & 0 deletions src/modules/rlm_ldap/clients.c
Expand Up @@ -216,6 +216,16 @@ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs)
char **value;

id = dn = ldap_get_dn(conn->handle, entry);
if (!dn) {
int ldap_errno;

ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
LDAP_ERR("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));

goto finish;
}
rlm_ldap_normalise_dn(dn, dn);

cp = cf_pair_find(cs, "identifier");
if (cp) {
value = ldap_get_values(conn->handle, entry, cf_pair_value(cp));
Expand Down
19 changes: 18 additions & 1 deletion src/modules/rlm_ldap/groups.c
Expand Up @@ -137,13 +137,22 @@ static rlm_rcode_t rlm_ldap_group_name2dn(ldap_instance_t const *inst, REQUEST *

do {
*dn = ldap_get_dn((*pconn)->handle, entry);
if (!*dn) {
ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));

rcode = RLM_MODULE_FAIL;
goto finish;
}
rlm_ldap_normalise_dn(*dn, *dn);

RDEBUG("Got group DN \"%s\"", *dn);
dn++;
} while((entry = ldap_next_entry((*pconn)->handle, entry)));

*dn = NULL;

finish:
finish:
talloc_free(filter);
if (result) {
ldap_msgfree(result);
Expand Down Expand Up @@ -430,6 +439,14 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
do {
if (inst->cacheable_group_dn) {
dn = ldap_get_dn((*pconn)->handle, entry);
if (!dn) {
ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));

goto finish;
}
rlm_ldap_normalise_dn(dn, dn);

MEM(vp = pairmake_config(inst->cache_da->name, NULL, T_OP_ADD));
pairstrcpy(vp, dn);

Expand Down
76 changes: 73 additions & 3 deletions src/modules/rlm_ldap/ldap.c
Expand Up @@ -101,6 +101,77 @@ int rlm_ldap_is_dn(char const *str)
return strrchr(str, ',') == NULL ? false : true;
}

/** Normalise escape sequences in a DN
*
* Characters in a DN can either be escaped as
* @verbatim \<hex><hex> @endverbatim or @verbatim \<special> @endverbatim
*
* The LDAP directory chooses how characters are escaped, which can make
* local comparisons of DNs difficult.
*
* Here we search for hex sequences that match special chars, and convert
* them to the @verbatim \<special> @endverbatim form.
*
* @note the resulting output string will only ever be shorter than the
* input, so it's fine to use the same buffer for both out and in.
*
* @param out Where to write the normalised DN.
* @param in The input DN.
* @return The number of bytes written to out.
*/
size_t rlm_ldap_normalise_dn(char *out, char const *in)
{
char const *p;
char *o = out;

for (p = in; *p != '\0'; p++) {
if (p[0] == '\\') {
char c;

/*
* Double backslashes get processed specially
*/
if (p[1] == '\\') {
p += 1;
*o++ = p[0];
*o++ = p[1];
continue;
}

/*
* Hex encodings that have an alternative
* special encoding, get rewritten to the
* special encoding.
*/
if (fr_hex2bin((uint8_t *) &c, 1, p + 1, 2) == 1) {
switch (c) {
case ' ':
case '#':
case '=':
case '"':
case '+':
case ',':
case ';':
case '<':
case '>':
case '\'':
*o++ = '\\';
*o++ = c;
p += 2;
continue;

default:
break;
}
}
}
*o++ = *p;
}
*o = '\0';

return o - out;
}

/** Find the place at which the two DN strings diverge
*
* Returns the length of the non matching string in full.
Expand Down Expand Up @@ -962,12 +1033,11 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
dn = ldap_get_dn((*pconn)->handle, entry);
if (!dn) {
ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);

REDEBUG("Retrieving object DN from entry failed: %s",
ldap_err2string(ldap_errno));
REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));

goto finish;
}
rlm_ldap_normalise_dn(dn, dn);

/*
* We can't use pairmake here to copy the value into the
Expand Down
2 changes: 2 additions & 0 deletions src/modules/rlm_ldap/ldap.h
Expand Up @@ -314,6 +314,8 @@ size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, c

int rlm_ldap_is_dn(char const *str);

size_t rlm_ldap_normalise_dn(char *out, char const *in);

ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen);

ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
Expand Down
7 changes: 7 additions & 0 deletions src/modules/rlm_ldap/rlm_ldap.c
Expand Up @@ -379,6 +379,13 @@ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR
* Check if we can do cached membership verification
*/
check_is_dn = rlm_ldap_is_dn(check->vp_strvalue);
if (check_is_dn) {
char *norm;

MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue)));
rlm_ldap_normalise_dn(norm, check->vp_strvalue);
pairstrsteal(check, norm);
}
if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) {
switch (rlm_ldap_check_cached(inst, request, check)) {
case RLM_MODULE_NOTFOUND:
Expand Down

0 comments on commit 86495f2

Please sign in to comment.