#include "apu.h"
#include "apr_pools.h"
#include "apr_general.h"
#include "apr_strings.h"
#include "apr_ldap.h"
#if APR_HAS_LDAP
#if APR_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifndef LDAPS_PORT
#define LDAPS_PORT 636
#endif
#define APR_LDAP_URL_PREFIX "ldap://"
#define APR_LDAP_URL_PREFIX_LEN (sizeof(APR_LDAP_URL_PREFIX)-1)
#define APR_LDAPS_URL_PREFIX "ldaps://"
#define APR_LDAPS_URL_PREFIX_LEN (sizeof(APR_LDAPS_URL_PREFIX)-1)
#define APR_LDAPI_URL_PREFIX "ldapi://"
#define APR_LDAPI_URL_PREFIX_LEN (sizeof(APR_LDAPI_URL_PREFIX)-1)
#define APR_LDAP_URL_URLCOLON "URL:"
#define APR_LDAP_URL_URLCOLON_LEN (sizeof(APR_LDAP_URL_URLCOLON)-1)
static const char* skip_url_prefix(const char *url,
int *enclosedp,
const char **scheme);
static void apr_ldap_pvt_hex_unescape(char *s);
static int apr_ldap_pvt_unhex(int c);
static char **apr_ldap_str2charray(apr_pool_t *pool,
const char *str,
const char *brkstr);
APU_DECLARE(int) apr_ldap_is_ldap_url(const char *url)
{
int enclosed;
const char * scheme;
if( url == NULL ) {
return 0;
}
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
return 0;
}
return 1;
}
APU_DECLARE(int) apr_ldap_is_ldaps_url(const char *url)
{
int enclosed;
const char * scheme;
if( url == NULL ) {
return 0;
}
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
return 0;
}
return strcmp(scheme, "ldaps") == 0;
}
APU_DECLARE(int) apr_ldap_is_ldapi_url(const char *url)
{
int enclosed;
const char * scheme;
if( url == NULL ) {
return 0;
}
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
return 0;
}
return strcmp(scheme, "ldapi") == 0;
}
static const char *skip_url_prefix(const char *url, int *enclosedp,
const char **scheme)
{
const char *p;
if ( url == NULL ) {
return( NULL );
}
p = url;
if ( *p == '<' ) {
*enclosedp = 1;
++p;
} else {
*enclosedp = 0;
}
if ( strncasecmp( p, APR_LDAP_URL_URLCOLON, APR_LDAP_URL_URLCOLON_LEN ) == 0 ) {
p += APR_LDAP_URL_URLCOLON_LEN;
}
if ( strncasecmp( p, APR_LDAP_URL_PREFIX, APR_LDAP_URL_PREFIX_LEN ) == 0 ) {
p += APR_LDAP_URL_PREFIX_LEN;
*scheme = "ldap";
return( p );
}
if ( strncasecmp( p, APR_LDAPS_URL_PREFIX, APR_LDAPS_URL_PREFIX_LEN ) == 0 ) {
p += APR_LDAPS_URL_PREFIX_LEN;
*scheme = "ldaps";
return( p );
}
if ( strncasecmp( p, APR_LDAPI_URL_PREFIX, APR_LDAPI_URL_PREFIX_LEN ) == 0 ) {
p += APR_LDAPI_URL_PREFIX_LEN;
*scheme = "ldapi";
return( p );
}
return( NULL );
}
static int str2scope(const char *p)
{
if ( strcasecmp( p, "one" ) == 0 ) {
return LDAP_SCOPE_ONELEVEL;
} else if ( strcasecmp( p, "onetree" ) == 0 ) {
return LDAP_SCOPE_ONELEVEL;
} else if ( strcasecmp( p, "base" ) == 0 ) {
return LDAP_SCOPE_BASE;
} else if ( strcasecmp( p, "sub" ) == 0 ) {
return LDAP_SCOPE_SUBTREE;
} else if ( strcasecmp( p, "subtree" ) == 0 ) {
return LDAP_SCOPE_SUBTREE;
}
return( -1 );
}
APU_DECLARE(int) apr_ldap_url_parse_ext(apr_pool_t *pool,
const char *url_in,
apr_ldap_url_desc_t **ludpp,
apr_ldap_err_t **result_err)
{
apr_ldap_url_desc_t *ludp;
char *p, *q, *r;
int i, enclosed;
const char *scheme = NULL;
const char *url_tmp;
char *url;
apr_ldap_err_t *result = (apr_ldap_err_t *)apr_pcalloc(pool, sizeof(apr_ldap_err_t));
*result_err = result;
if( url_in == NULL || ludpp == NULL ) {
result->reason = "Either the LDAP URL, or the URL structure was NULL. Oops.";
result->rc = APR_LDAP_URL_ERR_PARAM;
return APR_EGENERAL;
}
*ludpp = NULL;
url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
if ( url_tmp == NULL ) {
result->reason = "The scheme was not recognised as a valid LDAP URL scheme.";
result->rc = APR_LDAP_URL_ERR_BADSCHEME;
return APR_EGENERAL;
}
url = (char *)apr_pstrdup(pool, url_tmp);
if ( url == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
if ( enclosed ) {
p = &url[strlen(url)-1];
if( *p != '>' ) {
result->reason = "Bad enclosure error while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADENCLOSURE;
return APR_EGENERAL;
}
*p = '\0';
}
ludp = (apr_ldap_url_desc_t *)apr_pcalloc(pool, sizeof(apr_ldap_url_desc_t));
if ( ludp == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
ludp->lud_next = NULL;
ludp->lud_host = NULL;
ludp->lud_port = LDAP_PORT;
ludp->lud_dn = NULL;
ludp->lud_attrs = NULL;
ludp->lud_filter = NULL;
ludp->lud_scope = -1;
ludp->lud_filter = NULL;
ludp->lud_exts = NULL;
ludp->lud_scheme = (char *)apr_pstrdup(pool, scheme);
if ( ludp->lud_scheme == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
ludp->lud_port = LDAPS_PORT;
}
p = strchr( url, '/' );
if( p != NULL ) {
*p++ = '\0';
}
if ( *url == '[' ) {
r = strchr( url, ']' );
if ( r == NULL ) {
result->reason = "Bad LDAP URL while parsing IPV6 syntax.";
result->rc = APR_LDAP_URL_ERR_BADURL;
return APR_EGENERAL;
}
*r++ = '\0';
q = strrchr( r, ':' );
} else {
q = strrchr( url, ':' );
}
if ( q != NULL ) {
apr_ldap_pvt_hex_unescape( ++q );
if( *q == '\0' ) {
result->reason = "Bad LDAP URL while parsing.";
result->rc = APR_LDAP_URL_ERR_BADURL;
return APR_EGENERAL;
}
ludp->lud_port = atoi( q );
}
apr_ldap_pvt_hex_unescape( url );
ludp->lud_host = (char *)apr_pstrdup(pool, url + ( *url == '[' ));
if( ludp->lud_host == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
q++;
if( *q == '?') {
q++;
if( *q != '\0' ) {
apr_ldap_pvt_hex_unescape( q );
ludp->lud_dn = (char *)apr_pstrdup(pool, q);
} else {
ludp->lud_dn = (char *)apr_pstrdup(pool, "");
}
if( ludp->lud_dn == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
}
}
if( p == NULL ) {
*ludpp = ludp;
return APR_SUCCESS;
}
q = strchr( p, '?' );
if( q != NULL ) {
*q++ = '\0';
}
if( *p != '\0' ) {
apr_ldap_pvt_hex_unescape( p );
ludp->lud_dn = (char *)apr_pstrdup(pool, p);
} else {
ludp->lud_dn = (char *)apr_pstrdup(pool, "");
}
if( ludp->lud_dn == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
if( q == NULL ) {
*ludpp = ludp;
return APR_SUCCESS;
}
p = q;
q = strchr( p, '?' );
if( q != NULL ) {
*q++ = '\0';
}
if( *p != '\0' ) {
apr_ldap_pvt_hex_unescape( p );
ludp->lud_attrs = apr_ldap_str2charray(pool, p, ",");
if( ludp->lud_attrs == NULL ) {
result->reason = "Bad attributes encountered while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADATTRS;
return APR_EGENERAL;
}
}
if ( q == NULL ) {
*ludpp = ludp;
return APR_SUCCESS;
}
p = q;
q = strchr( p, '?' );
if( q != NULL ) {
*q++ = '\0';
}
if( *p != '\0' ) {
apr_ldap_pvt_hex_unescape( p );
ludp->lud_scope = str2scope( p );
if( ludp->lud_scope == -1 ) {
result->reason = "Bad scope encountered while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADSCOPE;
return APR_EGENERAL;
}
}
if ( q == NULL ) {
*ludpp = ludp;
return APR_SUCCESS;
}
p = q;
q = strchr( p, '?' );
if( q != NULL ) {
*q++ = '\0';
}
if( *p != '\0' ) {
apr_ldap_pvt_hex_unescape( p );
if( ! *p ) {
result->reason = "Bad filter encountered while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADFILTER;
return APR_EGENERAL;
}
ludp->lud_filter = (char *)apr_pstrdup(pool, p);
if( ludp->lud_filter == NULL ) {
result->reason = "Out of memory parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_MEM;
return APR_EGENERAL;
}
}
if ( q == NULL ) {
*ludpp = ludp;
return APR_SUCCESS;
}
p = q;
q = strchr( p, '?' );
if( q != NULL ) {
result->reason = "Bad URL encountered while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADURL;
return APR_EGENERAL;
}
ludp->lud_exts = apr_ldap_str2charray(pool, p, ",");
if( ludp->lud_exts == NULL ) {
result->reason = "Bad extensions encountered while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADEXTS;
return APR_EGENERAL;
}
for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
apr_ldap_pvt_hex_unescape( ludp->lud_exts[i] );
if( *ludp->lud_exts[i] == '!' ) {
ludp->lud_crit_exts++;
}
}
if( i == 0 ) {
result->reason = "Bad extensions encountered while parsing LDAP URL.";
result->rc = APR_LDAP_URL_ERR_BADEXTS;
return APR_EGENERAL;
}
*ludpp = ludp;
return APR_SUCCESS;
}
APU_DECLARE(int) apr_ldap_url_parse(apr_pool_t *pool,
const char *url_in,
apr_ldap_url_desc_t **ludpp,
apr_ldap_err_t **result_err)
{
int rc = apr_ldap_url_parse_ext(pool, url_in, ludpp, result_err);
if( rc != APR_SUCCESS ) {
return rc;
}
if ((*ludpp)->lud_scope == -1) {
(*ludpp)->lud_scope = LDAP_SCOPE_BASE;
}
if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
(*ludpp)->lud_host = NULL;
}
return rc;
}
static void apr_ldap_pvt_hex_unescape(char *s)
{
char *p;
for ( p = s; *s != '\0'; ++s ) {
if ( *s == '%' ) {
if ( *++s == '\0' ) {
break;
}
*p = apr_ldap_pvt_unhex( *s ) << 4;
if ( *++s == '\0' ) {
break;
}
*p++ += apr_ldap_pvt_unhex( *s );
} else {
*p++ = *s;
}
}
*p = '\0';
}
static int apr_ldap_pvt_unhex(int c)
{
return( c >= '0' && c <= '9' ? c - '0'
: c >= 'A' && c <= 'F' ? c - 'A' + 10
: c - 'a' + 10 );
}
static char **apr_ldap_str2charray(apr_pool_t *pool,
const char *str_in,
const char *brkstr)
{
char **res;
char *str, *s;
char *lasts;
int i;
str = (char *)apr_pstrdup(pool, str_in);
if( str == NULL ) {
return NULL;
}
i = 1;
for ( s = str; *s; s++ ) {
if ( strchr( brkstr, *s ) != NULL ) {
i++;
}
}
res = (char **) apr_pcalloc(pool, (i + 1) * sizeof(char *));
if( res == NULL ) {
return NULL;
}
i = 0;
for ( s = (char *)apr_strtok( str, brkstr, &lasts );
s != NULL;
s = (char *)apr_strtok( NULL, brkstr, &lasts ) ) {
res[i] = (char *)apr_pstrdup(pool, s);
if(res[i] == NULL) {
return NULL;
}
i++;
}
res[i] = NULL;
return( res );
}
#endif