|
|
|
@ -215,6 +215,137 @@ static void mod_authn_ldap_opt_err(server *srv, const char *file, unsigned long
|
|
|
|
|
mod_authn_ldap_err(srv, file, line, fn, err); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void mod_authn_append_ldap_dn_escape(buffer * const filter, const buffer * const raw) { |
|
|
|
|
/* [RFC4514] 2.4 Converting an AttributeValue from ASN.1 to a String
|
|
|
|
|
* |
|
|
|
|
* https://www.ldap.com/ldap-dns-and-rdns
|
|
|
|
|
* http://social.technet.microsoft.com/wiki/contents/articles/5312.active-directory-characters-to-escape.aspx
|
|
|
|
|
*/ |
|
|
|
|
const char * const b = raw->ptr; |
|
|
|
|
const size_t rlen = buffer_string_length(raw); |
|
|
|
|
if (0 == rlen) return; |
|
|
|
|
|
|
|
|
|
if (b[0] == ' ') { /* || b[0] == '#' handled below for MS Active Directory*/ |
|
|
|
|
/* escape leading ' ' */ |
|
|
|
|
buffer_append_string_len(filter, CONST_STR_LEN("\\")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < rlen; ++i) { |
|
|
|
|
size_t len = i; |
|
|
|
|
int bs = 0; |
|
|
|
|
do { |
|
|
|
|
/* encode all UTF-8 chars with high bit set
|
|
|
|
|
* (instead of validating UTF-8 and escaping only invalid UTF-8) */ |
|
|
|
|
if (((unsigned char *)b)[len] > 0x7f) |
|
|
|
|
break; |
|
|
|
|
switch (b[len]) { |
|
|
|
|
default: |
|
|
|
|
continue; |
|
|
|
|
case '"': case '+': case ',': case ';': case '\\': |
|
|
|
|
case '<': case '>': |
|
|
|
|
case '=': case '#': /* (for MS Active Directory) */ |
|
|
|
|
bs = 1; |
|
|
|
|
break; |
|
|
|
|
case '\0': |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} while (++len < rlen); |
|
|
|
|
len -= i; |
|
|
|
|
|
|
|
|
|
if (len) { |
|
|
|
|
buffer_append_string_len(filter, b+i, len); |
|
|
|
|
if ((i += len) == rlen) break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (bs) { |
|
|
|
|
buffer_append_string_len(filter, CONST_STR_LEN("\\")); |
|
|
|
|
buffer_append_string_len(filter, b+i, 1); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
/* escape NUL ('\0') (and all UTF-8 chars with high bit set) */ |
|
|
|
|
char *f; |
|
|
|
|
buffer_string_prepare_append(filter, 3); |
|
|
|
|
f = filter->ptr + buffer_string_length(filter); |
|
|
|
|
f[0] = '\\'; |
|
|
|
|
f[1] = "0123456789abcdef"[(((unsigned char *)b)[i] >> 4) & 0xf]; |
|
|
|
|
f[2] = "0123456789abcdef"[(((unsigned char *)b)[i] ) & 0xf]; |
|
|
|
|
buffer_commit(filter, 3); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (rlen > 1 && b[rlen-1] == ' ') { |
|
|
|
|
/* escape trailing ' ' */ |
|
|
|
|
filter->ptr[buffer_string_length(filter)-1] = '\\'; |
|
|
|
|
buffer_append_string_len(filter, CONST_STR_LEN(" ")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void mod_authn_append_ldap_filter_escape(buffer * const filter, const buffer * const raw) { |
|
|
|
|
/* [RFC4515] 3. String Search Filter Definition
|
|
|
|
|
* |
|
|
|
|
* [...] |
|
|
|
|
* |
|
|
|
|
* The <valueencoding> rule ensures that the entire filter string is a |
|
|
|
|
* valid UTF-8 string and provides that the octets that represent the |
|
|
|
|
* ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII |
|
|
|
|
* 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a |
|
|
|
|
* backslash "\" (ASCII 0x5c) followed by the two hexadecimal digits |
|
|
|
|
* representing the value of the encoded octet. |
|
|
|
|
* |
|
|
|
|
* [...] |
|
|
|
|
* |
|
|
|
|
* As indicated by the <valueencoding> rule, implementations MUST escape |
|
|
|
|
* all octets greater than 0x7F that are not part of a valid UTF-8 |
|
|
|
|
* encoding sequence when they generate a string representation of a |
|
|
|
|
* search filter. Implementations SHOULD accept as input strings that |
|
|
|
|
* are not valid UTF-8 strings. This is necessary because RFC 2254 did |
|
|
|
|
* not clearly define the term "string representation" (and in |
|
|
|
|
* particular did not mention that the string representation of an LDAP |
|
|
|
|
* search filter is a string of UTF-8-encoded Unicode characters). |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* https://www.ldap.com/ldap-filters
|
|
|
|
|
* Although not required, you may escape any other characters that you want |
|
|
|
|
* in the assertion value (or substring component) of a filter. This may be |
|
|
|
|
* accomplished by prefixing the hexadecimal representation of each byte of |
|
|
|
|
* the UTF-8 encoding of the character to escape with a backslash character. |
|
|
|
|
*/ |
|
|
|
|
const char * const b = raw->ptr; |
|
|
|
|
const size_t rlen = buffer_string_length(raw); |
|
|
|
|
for (size_t i = 0; i < rlen; ++i) { |
|
|
|
|
size_t len = i; |
|
|
|
|
char *f; |
|
|
|
|
do { |
|
|
|
|
/* encode all UTF-8 chars with high bit set
|
|
|
|
|
* (instead of validating UTF-8 and escaping only invalid UTF-8) */ |
|
|
|
|
if (((unsigned char *)b)[len] > 0x7f) |
|
|
|
|
break; |
|
|
|
|
switch (b[len]) { |
|
|
|
|
default: |
|
|
|
|
continue; |
|
|
|
|
case '\0': case '(': case ')': case '*': case '\\': |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} while (++len < rlen); |
|
|
|
|
len -= i; |
|
|
|
|
|
|
|
|
|
if (len) { |
|
|
|
|
buffer_append_string_len(filter, b+i, len); |
|
|
|
|
if ((i += len) == rlen) break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* escape * ( ) \ NUL ('\0') (and all UTF-8 chars with high bit set) */ |
|
|
|
|
buffer_string_prepare_append(filter, 3); |
|
|
|
|
f = filter->ptr + buffer_string_length(filter); |
|
|
|
|
f[0] = '\\'; |
|
|
|
|
f[1] = "0123456789abcdef"[(((unsigned char *)b)[i] >> 4) & 0xf]; |
|
|
|
|
f[2] = "0123456789abcdef"[(((unsigned char *)b)[i] ) & 0xf]; |
|
|
|
|
buffer_commit(filter, 3); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static LDAP * mod_authn_ldap_host_init(server *srv, plugin_config *s) { |
|
|
|
|
LDAP *ld; |
|
|
|
|
int ret; |
|
|
|
@ -400,7 +531,7 @@ static handler_t mod_authn_ldap_memberOf(server *srv, plugin_config *s, const ht
|
|
|
|
|
CONST_STR_LEN("member"))) { |
|
|
|
|
buffer_append_string(filter, userdn); |
|
|
|
|
} else { /*(assume "memberUid"; consider validating in SETDEFAULTS_FUNC)*/ |
|
|
|
|
buffer_append_string_buffer(filter, username); |
|
|
|
|
mod_authn_append_ldap_filter_escape(filter, username); |
|
|
|
|
} |
|
|
|
|
buffer_append_string_len(filter, CONST_STR_LEN(")")); |
|
|
|
|
|
|
|
|
@ -433,33 +564,6 @@ static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, c
|
|
|
|
|
if (pw[0] == '\0' && !p->conf.auth_ldap_allow_empty_pw) |
|
|
|
|
return HANDLER_ERROR; |
|
|
|
|
|
|
|
|
|
/* check username
|
|
|
|
|
* |
|
|
|
|
* we have to protect againt username which modifies our filter in |
|
|
|
|
* an unpleasant way |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
for (size_t i = 0, len = buffer_string_length(username); i < len; i++) { |
|
|
|
|
char c = username->ptr[i]; |
|
|
|
|
|
|
|
|
|
if (!isalpha(c) && |
|
|
|
|
!isdigit(c) && |
|
|
|
|
(c != ' ') && |
|
|
|
|
(c != '@') && |
|
|
|
|
(c != '-') && |
|
|
|
|
(c != '_') && |
|
|
|
|
(c != '.') ) { |
|
|
|
|
|
|
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sbd", |
|
|
|
|
"ldap: invalid character (- _.@a-zA-Z0-9 allowed) " |
|
|
|
|
"in username:", username, i); |
|
|
|
|
|
|
|
|
|
con->http_status = 400; /* Bad Request */ |
|
|
|
|
con->mode = DIRECT; |
|
|
|
|
return HANDLER_FINISHED; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template = p->conf.auth_ldap_filter; |
|
|
|
|
if (buffer_string_is_empty(template)) { |
|
|
|
|
return HANDLER_ERROR; |
|
|
|
@ -470,14 +574,14 @@ static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, c
|
|
|
|
|
if (*template->ptr == ',') { |
|
|
|
|
/* special-case filter template beginning with ',' to be explicit DN */ |
|
|
|
|
buffer_append_string_len(p->ldap_filter, CONST_STR_LEN("uid=")); |
|
|
|
|
buffer_append_string_buffer(p->ldap_filter, username); |
|
|
|
|
mod_authn_append_ldap_dn_escape(p->ldap_filter, username); |
|
|
|
|
buffer_append_string_buffer(p->ldap_filter, template); |
|
|
|
|
dn = p->ldap_filter->ptr; |
|
|
|
|
} else { |
|
|
|
|
for (char *b = template->ptr, *d; *b; b = d+1) { |
|
|
|
|
if (NULL != (d = strchr(b, '$'))) { |
|
|
|
|
buffer_append_string_len(p->ldap_filter, b, (size_t)(d - b)); |
|
|
|
|
buffer_append_string_buffer(p->ldap_filter, username); |
|
|
|
|
mod_authn_append_ldap_filter_escape(p->ldap_filter, username); |
|
|
|
|
} else { |
|
|
|
|
d = template->ptr + buffer_string_length(template); |
|
|
|
|
buffer_append_string_len(p->ldap_filter, b, (size_t)(d - b)); |
|
|
|
|