1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
17
/* Portions Copyright 1998-2002 The OpenLDAP Foundation
18
* All rights reserved.
20
* Redistribution and use in source and binary forms, with or without
21
* modification, are permitted only as authorized by the OpenLDAP
22
* Public License. A copy of this license is available at
23
* http://www.OpenLDAP.org/license.html or in file LICENSE in the
24
* top-level directory of the distribution.
26
* OpenLDAP is a registered trademark of the OpenLDAP Foundation.
28
* Individual files and/or contributed packages may be copyright by
29
* other parties and subject to additional restrictions.
31
* This work is derived from the University of Michigan LDAP v3.3
32
* distribution. Information concerning this software is available
33
* at: http://www.umich.edu/~dirsvcs/ldap/
35
* This work also contains materials derived from public sources.
37
* Additional information about OpenLDAP can be obtained at:
38
* http://www.openldap.org/
42
* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
43
* All rights reserved.
45
* Redistribution and use in source and binary forms are permitted
46
* provided that this notice is preserved and that due credit is given
47
* to the University of Michigan at Ann Arbor. The name of the University
48
* may not be used to endorse or promote products derived from this
49
* software without specific prior written permission. This software
50
* is provided ``as is'' without express or implied warranty.
53
/* apr_ldap_url.c -- LDAP URL (RFC 2255) related routines
55
* Win32 and perhaps other non-OpenLDAP based ldap libraries may be
56
* missing ldap_url_* APIs. We focus here on the one significant
57
* aspect, which is parsing. We have [for the time being] omitted
58
* the ldap_url_search APIs.
60
* LDAP URLs look like this:
61
* ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
64
* attributes is a comma separated list
65
* scope is one of these three strings: base one sub (default=base)
66
* filter is an string-represented filter as in RFC 2254
68
* e.g., ldap://host:port/dc=com?o,cn?base?o=openldap?extension
70
* Tolerates URLs that look like: <ldapurl> and <URL:ldapurl>
74
#include "apr_pools.h"
75
#include "apr_general.h"
76
#include "apr_strings.h"
86
#define LDAPS_PORT 636 /* ldaps:/// default LDAP over TLS port */
89
#define APR_LDAP_URL_PREFIX "ldap://"
90
#define APR_LDAP_URL_PREFIX_LEN (sizeof(APR_LDAP_URL_PREFIX)-1)
91
#define APR_LDAPS_URL_PREFIX "ldaps://"
92
#define APR_LDAPS_URL_PREFIX_LEN (sizeof(APR_LDAPS_URL_PREFIX)-1)
93
#define APR_LDAPI_URL_PREFIX "ldapi://"
94
#define APR_LDAPI_URL_PREFIX_LEN (sizeof(APR_LDAPI_URL_PREFIX)-1)
95
#define APR_LDAP_URL_URLCOLON "URL:"
96
#define APR_LDAP_URL_URLCOLON_LEN (sizeof(APR_LDAP_URL_URLCOLON)-1)
100
static const char* skip_url_prefix(const char *url,
102
const char **scheme);
104
static void apr_ldap_pvt_hex_unescape(char *s);
106
static int apr_ldap_pvt_unhex(int c);
108
static char **apr_ldap_str2charray(apr_pool_t *pool,
114
* Is this URL an ldap url?
117
APU_DECLARE(int) apr_ldap_is_ldap_url(const char *url)
126
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
134
* Is this URL a secure ldap url?
137
APU_DECLARE(int) apr_ldap_is_ldaps_url(const char *url)
146
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
150
return strcmp(scheme, "ldaps") == 0;
154
* Is this URL an ldap socket url?
157
APU_DECLARE(int) apr_ldap_is_ldapi_url(const char *url)
166
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
170
return strcmp(scheme, "ldapi") == 0;
174
static const char *skip_url_prefix(const char *url, int *enclosedp,
178
* return non-zero if this looks like a LDAP URL; zero if not
179
* if non-zero returned, *urlp will be moved past "ldap://" part of URL
189
/* skip leading '<' (if any) */
197
/* skip leading "URL:" (if any) */
198
if ( strncasecmp( p, APR_LDAP_URL_URLCOLON, APR_LDAP_URL_URLCOLON_LEN ) == 0 ) {
199
p += APR_LDAP_URL_URLCOLON_LEN;
202
/* check for "ldap://" prefix */
203
if ( strncasecmp( p, APR_LDAP_URL_PREFIX, APR_LDAP_URL_PREFIX_LEN ) == 0 ) {
204
/* skip over "ldap://" prefix and return success */
205
p += APR_LDAP_URL_PREFIX_LEN;
210
/* check for "ldaps://" prefix */
211
if ( strncasecmp( p, APR_LDAPS_URL_PREFIX, APR_LDAPS_URL_PREFIX_LEN ) == 0 ) {
212
/* skip over "ldaps://" prefix and return success */
213
p += APR_LDAPS_URL_PREFIX_LEN;
218
/* check for "ldapi://" prefix */
219
if ( strncasecmp( p, APR_LDAPI_URL_PREFIX, APR_LDAPI_URL_PREFIX_LEN ) == 0 ) {
220
/* skip over "ldapi://" prefix and return success */
221
p += APR_LDAPI_URL_PREFIX_LEN;
230
static int str2scope(const char *p)
232
if ( strcasecmp( p, "one" ) == 0 ) {
233
return LDAP_SCOPE_ONELEVEL;
235
} else if ( strcasecmp( p, "onetree" ) == 0 ) {
236
return LDAP_SCOPE_ONELEVEL;
238
} else if ( strcasecmp( p, "base" ) == 0 ) {
239
return LDAP_SCOPE_BASE;
241
} else if ( strcasecmp( p, "sub" ) == 0 ) {
242
return LDAP_SCOPE_SUBTREE;
244
} else if ( strcasecmp( p, "subtree" ) == 0 ) {
245
return LDAP_SCOPE_SUBTREE;
253
* Parse the URL provided into an apr_ldap_url_desc_t object.
255
* APR_SUCCESS is returned on success, APR_EGENERAL on failure.
256
* The LDAP result code and reason string is returned in the
257
* apr_ldap_err_t structure.
259
APU_DECLARE(int) apr_ldap_url_parse_ext(apr_pool_t *pool,
261
apr_ldap_url_desc_t **ludpp,
262
apr_ldap_err_t **result_err)
264
apr_ldap_url_desc_t *ludp;
267
const char *scheme = NULL;
271
apr_ldap_err_t *result = (apr_ldap_err_t *)apr_pcalloc(pool, sizeof(apr_ldap_err_t));
272
*result_err = result;
274
/* sanity check our parameters */
275
if( url_in == NULL || ludpp == NULL ) {
276
result->reason = "Either the LDAP URL, or the URL structure was NULL. Oops.";
277
result->rc = APR_LDAP_URL_ERR_PARAM;
281
*ludpp = NULL; /* pessimistic */
283
url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
284
if ( url_tmp == NULL ) {
285
result->reason = "The scheme was not recognised as a valid LDAP URL scheme.";
286
result->rc = APR_LDAP_URL_ERR_BADSCHEME;
290
/* make working copy of the remainder of the URL */
291
url = (char *)apr_pstrdup(pool, url_tmp);
293
result->reason = "Out of memory parsing LDAP URL.";
294
result->rc = APR_LDAP_URL_ERR_MEM;
299
p = &url[strlen(url)-1];
302
result->reason = "Bad enclosure error while parsing LDAP URL.";
303
result->rc = APR_LDAP_URL_ERR_BADENCLOSURE;
310
/* allocate return struct */
311
ludp = (apr_ldap_url_desc_t *)apr_pcalloc(pool, sizeof(apr_ldap_url_desc_t));
312
if ( ludp == NULL ) {
313
result->reason = "Out of memory parsing LDAP URL.";
314
result->rc = APR_LDAP_URL_ERR_MEM;
318
ludp->lud_next = NULL;
319
ludp->lud_host = NULL;
320
ludp->lud_port = LDAP_PORT;
322
ludp->lud_attrs = NULL;
323
ludp->lud_filter = NULL;
324
ludp->lud_scope = -1;
325
ludp->lud_filter = NULL;
326
ludp->lud_exts = NULL;
328
ludp->lud_scheme = (char *)apr_pstrdup(pool, scheme);
329
if ( ludp->lud_scheme == NULL ) {
330
result->reason = "Out of memory parsing LDAP URL.";
331
result->rc = APR_LDAP_URL_ERR_MEM;
335
if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
336
ludp->lud_port = LDAPS_PORT;
339
/* scan forward for '/' that marks end of hostport and begin. of dn */
340
p = strchr( url, '/' );
343
/* terminate hostport; point to start of dn */
347
/* IPv6 syntax with [ip address]:port */
349
r = strchr( url, ']' );
351
result->reason = "Bad LDAP URL while parsing IPV6 syntax.";
352
result->rc = APR_LDAP_URL_ERR_BADURL;
356
q = strrchr( r, ':' );
358
q = strrchr( url, ':' );
362
apr_ldap_pvt_hex_unescape( ++q );
365
result->reason = "Bad LDAP URL while parsing.";
366
result->rc = APR_LDAP_URL_ERR_BADURL;
370
ludp->lud_port = atoi( q );
373
apr_ldap_pvt_hex_unescape( url );
375
/* If [ip address]:port syntax, url is [ip and we skip the [ */
376
ludp->lud_host = (char *)apr_pstrdup(pool, url + ( *url == '[' ));
377
if( ludp->lud_host == NULL ) {
378
result->reason = "Out of memory parsing LDAP URL.";
379
result->rc = APR_LDAP_URL_ERR_MEM;
384
* Kludge. ldap://111.222.333.444:389??cn=abc,o=company
386
* On early Novell releases, search references/referrals were returned
387
* in this format, i.e., the dn was kind of in the scope position,
388
* but the required slash is missing. The whole thing is illegal syntax,
389
* but we need to account for it. Fortunately it can't be confused with
392
if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
394
/* ? immediately followed by question */
399
apr_ldap_pvt_hex_unescape( q );
400
ludp->lud_dn = (char *)apr_pstrdup(pool, q);
402
ludp->lud_dn = (char *)apr_pstrdup(pool, "");
405
if( ludp->lud_dn == NULL ) {
406
result->reason = "Out of memory parsing LDAP URL.";
407
result->rc = APR_LDAP_URL_ERR_MEM;
418
/* scan forward for '?' that may marks end of dn */
419
q = strchr( p, '?' );
422
/* terminate dn part */
428
apr_ldap_pvt_hex_unescape( p );
429
ludp->lud_dn = (char *)apr_pstrdup(pool, p);
431
ludp->lud_dn = (char *)apr_pstrdup(pool, "");
434
if( ludp->lud_dn == NULL ) {
435
result->reason = "Out of memory parsing LDAP URL.";
436
result->rc = APR_LDAP_URL_ERR_MEM;
446
/* scan forward for '?' that may marks end of attributes */
448
q = strchr( p, '?' );
451
/* terminate attributes part */
456
/* parse attributes */
457
apr_ldap_pvt_hex_unescape( p );
458
ludp->lud_attrs = apr_ldap_str2charray(pool, p, ",");
460
if( ludp->lud_attrs == NULL ) {
461
result->reason = "Bad attributes encountered while parsing LDAP URL.";
462
result->rc = APR_LDAP_URL_ERR_BADATTRS;
473
/* scan forward for '?' that may marks end of scope */
475
q = strchr( p, '?' );
478
/* terminate the scope part */
483
/* parse the scope */
484
apr_ldap_pvt_hex_unescape( p );
485
ludp->lud_scope = str2scope( p );
487
if( ludp->lud_scope == -1 ) {
488
result->reason = "Bad scope encountered while parsing LDAP URL.";
489
result->rc = APR_LDAP_URL_ERR_BADSCOPE;
500
/* scan forward for '?' that may marks end of filter */
502
q = strchr( p, '?' );
505
/* terminate the filter part */
510
/* parse the filter */
511
apr_ldap_pvt_hex_unescape( p );
515
result->reason = "Bad filter encountered while parsing LDAP URL.";
516
result->rc = APR_LDAP_URL_ERR_BADFILTER;
520
ludp->lud_filter = (char *)apr_pstrdup(pool, p);
521
if( ludp->lud_filter == NULL ) {
522
result->reason = "Out of memory parsing LDAP URL.";
523
result->rc = APR_LDAP_URL_ERR_MEM;
534
/* scan forward for '?' that may marks end of extensions */
536
q = strchr( p, '?' );
540
result->reason = "Bad URL encountered while parsing LDAP URL.";
541
result->rc = APR_LDAP_URL_ERR_BADURL;
545
/* parse the extensions */
546
ludp->lud_exts = apr_ldap_str2charray(pool, p, ",");
547
if( ludp->lud_exts == NULL ) {
548
result->reason = "Bad extensions encountered while parsing LDAP URL.";
549
result->rc = APR_LDAP_URL_ERR_BADEXTS;
553
for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
554
apr_ldap_pvt_hex_unescape( ludp->lud_exts[i] );
556
if( *ludp->lud_exts[i] == '!' ) {
557
/* count the number of critical extensions */
558
ludp->lud_crit_exts++;
563
/* must have 1 or more */
564
result->reason = "Bad extensions encountered while parsing LDAP URL.";
565
result->rc = APR_LDAP_URL_ERR_BADEXTS;
576
* Parse the URL provided into an apr_ldap_url_desc_t object.
578
* APR_SUCCESS is returned on success, APR_EGENERAL on failure.
579
* The LDAP result code and reason string is returned in the
580
* apr_ldap_err_t structure.
582
APU_DECLARE(int) apr_ldap_url_parse(apr_pool_t *pool,
584
apr_ldap_url_desc_t **ludpp,
585
apr_ldap_err_t **result_err)
588
int rc = apr_ldap_url_parse_ext(pool, url_in, ludpp, result_err);
589
if( rc != APR_SUCCESS ) {
593
if ((*ludpp)->lud_scope == -1) {
594
(*ludpp)->lud_scope = LDAP_SCOPE_BASE;
597
if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
598
(*ludpp)->lud_host = NULL;
606
static void apr_ldap_pvt_hex_unescape(char *s)
609
* Remove URL hex escapes from s... done in place. The basic concept for
610
* this routine is borrowed from the WWW library HTUnEscape() routine.
614
for ( p = s; *s != '\0'; ++s ) {
616
if ( *++s == '\0' ) {
619
*p = apr_ldap_pvt_unhex( *s ) << 4;
620
if ( *++s == '\0' ) {
623
*p++ += apr_ldap_pvt_unhex( *s );
633
static int apr_ldap_pvt_unhex(int c)
635
return( c >= '0' && c <= '9' ? c - '0'
636
: c >= 'A' && c <= 'F' ? c - 'A' + 10
642
* Convert a string to a character array
644
static char **apr_ldap_str2charray(apr_pool_t *pool,
653
/* protect the input string from strtok */
654
str = (char *)apr_pstrdup(pool, str_in);
660
for ( s = str; *s; s++ ) {
661
/* Warning: this strchr was previously ldap_utf8_strchr(), check
662
* whether this particular code has any charset issues.
664
if ( strchr( brkstr, *s ) != NULL ) {
669
res = (char **) apr_pcalloc(pool, (i + 1) * sizeof(char *));
676
for ( s = (char *)apr_strtok( str, brkstr, &lasts );
678
s = (char *)apr_strtok( NULL, brkstr, &lasts ) ) {
680
res[i] = (char *)apr_pstrdup(pool, s);
694
#endif /* APR_HAS_LDAP */