1 /*
2 * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * The contents of this file are subject to the Netscape Public
8 * License Version 1.1 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.mozilla.org/NPL/
11 *
12 * Software distributed under the License is distributed on an "AS
13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is Netscape
21 * Communications Corporation. Portions created by Netscape are
22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
23 * Rights Reserved.
24 *
25 * Contributor(s):
26 */
27 /*
28 * Copyright (c) 1996 Regents of the University of Michigan.
29 * All rights reserved.
30 *
31 */
32 /* LIBLDAP url.c -- LDAP URL related routines
33 *
34 * LDAP URLs look like this:
35 * l d a p : / / hostport / dn [ ? attributes [ ? scope [ ? filter ] ] ]
36 *
37 * where:
38 * attributes is a comma separated list
39 * scope is one of these three strings: base one sub (default=base)
40 * filter is an string-represented filter as in RFC 1558
41 *
42 * e.g., ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich
43 *
44 * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
45 */
46
47 #if 0
48 #ifndef lint
49 static char copyright[] = "@(#) Copyright (c) 1996 Regents of the University of Michigan.\nAll rights reserved.\n";
50 #endif
51 #endif
52
53 #include "ldap-int.h"
54
55
56 static int skip_url_prefix( const char **urlp, int *enclosedp, int *securep );
57
58
59 int
60 LDAP_CALL
ldap_is_ldap_url(const char * url)61 ldap_is_ldap_url( const char *url )
62 {
63 int enclosed, secure;
64
65 return( url != NULL
66 && skip_url_prefix( &url, &enclosed, &secure ));
67 }
68
69
70 static int
skip_url_prefix(const char ** urlp,int * enclosedp,int * securep)71 skip_url_prefix( const char **urlp, int *enclosedp, int *securep )
72 {
73 /*
74 * return non-zero if this looks like a LDAP URL; zero if not
75 * if non-zero returned, *urlp will be moved past "ldap://" part of URL
76 * The data that *urlp points to is not changed by this function.
77 */
78 if ( *urlp == NULL ) {
79 return( 0 );
80 }
81
82 /* skip leading '<' (if any) */
83 if ( **urlp == '<' ) {
84 *enclosedp = 1;
85 ++*urlp;
86 } else {
87 *enclosedp = 0;
88 }
89
90 /* skip leading "URL:" (if any) */
91 if ( strlen( *urlp ) >= LDAP_URL_URLCOLON_LEN && strncasecmp(
92 *urlp, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
93 *urlp += LDAP_URL_URLCOLON_LEN;
94 }
95
96 /* check for an "ldap://" prefix */
97 if ( strlen( *urlp ) >= LDAP_URL_PREFIX_LEN && strncasecmp( *urlp,
98 LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
99 /* skip over URL prefix and return success */
100 *urlp += LDAP_URL_PREFIX_LEN;
101 *securep = 0;
102 return( 1 );
103 }
104
105 /* check for an "ldaps://" prefix */
106 if ( strlen( *urlp ) >= LDAPS_URL_PREFIX_LEN && strncasecmp( *urlp,
107 LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
108 /* skip over URL prefix and return success */
109 *urlp += LDAPS_URL_PREFIX_LEN;
110 *securep = 1;
111 return( 1 );
112 }
113
114 return( 0 ); /* not an LDAP URL */
115 }
116
117
118 int
119 LDAP_CALL
ldap_url_parse(const char * url,LDAPURLDesc ** ludpp)120 ldap_url_parse( const char *url, LDAPURLDesc **ludpp )
121 {
122 /*
123 * Pick apart the pieces of an LDAP URL.
124 */
125 int rc;
126
127 if (( rc = nsldapi_url_parse( url, ludpp, 1 )) == 0 ) {
128 if ( (*ludpp)->lud_scope == -1 ) {
129 (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
130 }
131 if ( (*ludpp)->lud_filter == NULL ) {
132 (*ludpp)->lud_filter = "(objectclass=*)";
133 }
134 if ( *((*ludpp)->lud_dn) == '\0' ) {
135 (*ludpp)->lud_dn = NULL;
136 }
137 }
138
139 return( rc );
140 }
141
142 /* same as ldap_url_parse(), but dn is not require */
143 int
144 LDAP_CALL
ldap_url_parse_nodn(const char * url,LDAPURLDesc ** ludpp)145 ldap_url_parse_nodn(const char *url, LDAPURLDesc **ludpp)
146 {
147 /*
148 * Pick apart the pieces of an LDAP URL.
149 */
150 int rc;
151
152 if ((rc = nsldapi_url_parse(url, ludpp, 0)) == 0) {
153 if ((*ludpp)->lud_scope == -1) {
154 (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
155 }
156 if ((*ludpp)->lud_filter == NULL) {
157 (*ludpp)->lud_filter = "(objectclass=*)";
158 }
159 if ((*ludpp)->lud_dn && *((*ludpp)->lud_dn) == '\0') {
160 (*ludpp)->lud_dn = NULL;
161 }
162 }
163
164 return (rc);
165 }
166
167
168 /*
169 * like ldap_url_parse() with a few exceptions:
170 * 1) if dn_required is zero, a missing DN does not generate an error
171 * (we just leave the lud_dn field NULL)
172 * 2) no defaults are set for lud_scope and lud_filter (they are set to -1
173 * and NULL respectively if no SCOPE or FILTER are present in the URL).
174 * 3) when there is a zero-length DN in a URL we do not set lud_dn to NULL.
175 * 4) if an LDAPv3 URL extensions are included,
176 */
177 int
nsldapi_url_parse(const char * url,LDAPURLDesc ** ludpp,int dn_required)178 nsldapi_url_parse( const char *url, LDAPURLDesc **ludpp, int dn_required )
179 {
180
181 LDAPURLDesc *ludp;
182 char *urlcopy, *attrs, *scope, *extensions = NULL, *p, *q;
183 int enclosed, secure, i, nattrs, at_start;
184
185 LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_url_parse(%s)\n", url, 0, 0 );
186
187 if ( url == NULL || ludpp == NULL ) {
188 return( LDAP_URL_ERR_PARAM );
189 }
190
191 *ludpp = NULL; /* pessimistic */
192
193 if ( !skip_url_prefix( &url, &enclosed, &secure )) {
194 return( LDAP_URL_ERR_NOTLDAP );
195 }
196
197 /* allocate return struct */
198 if (( ludp = (LDAPURLDesc *)NSLDAPI_CALLOC( 1, sizeof( LDAPURLDesc )))
199 == NULLLDAPURLDESC ) {
200 return( LDAP_URL_ERR_MEM );
201 }
202
203 if ( secure ) {
204 ludp->lud_options |= LDAP_URL_OPT_SECURE;
205 }
206
207 /* make working copy of the remainder of the URL */
208 if (( urlcopy = nsldapi_strdup( url )) == NULL ) {
209 ldap_free_urldesc( ludp );
210 return( LDAP_URL_ERR_MEM );
211 }
212
213 if ( enclosed && *((p = urlcopy + strlen( urlcopy ) - 1)) == '>' ) {
214 *p = '\0';
215 }
216
217 /* initialize scope and filter */
218 ludp->lud_scope = -1;
219 ludp->lud_filter = NULL;
220
221 /* lud_string is the only malloc'd string space we use */
222 ludp->lud_string = urlcopy;
223
224 /* scan forward for '/' that marks end of hostport and begin. of dn */
225 if (( ludp->lud_dn = strchr( urlcopy, '/' )) == NULL ) {
226 if ( dn_required ) {
227 ldap_free_urldesc( ludp );
228 return( LDAP_URL_ERR_NODN );
229 }
230 } else {
231 /* terminate hostport; point to start of dn */
232 *ludp->lud_dn++ = '\0';
233 }
234
235
236 if ( *urlcopy == '\0' ) {
237 ludp->lud_host = NULL;
238 } else {
239 ludp->lud_host = urlcopy;
240 nsldapi_hex_unescape( ludp->lud_host );
241
242 /*
243 * Locate and strip off optional port number (:#) in host
244 * portion of URL.
245 *
246 * If more than one space-separated host is listed, we only
247 * look for a port number within the right-most one since
248 * ldap_init() will handle host parameters that look like
249 * host:port anyway.
250 */
251 if (( p = strrchr( ludp->lud_host, ' ' )) == NULL ) {
252 p = ludp->lud_host;
253 } else {
254 ++p;
255 }
256 if ( *p == '[' && ( q = strchr( p, ']' )) != NULL ) {
257 /* square brackets present -- skip past them */
258 p = q++;
259 }
260 if (( p = strchr( p, ':' )) != NULL ) {
261 *p++ = '\0';
262 ludp->lud_port = atoi( p );
263 if ( *ludp->lud_host == '\0' ) {
264 /*
265 * no hostname and a port: invalid hostcode
266 * according to RFC 1738
267 */
268 ldap_free_urldesc(ludp);
269 return (LDAP_URL_ERR_HOSTPORT);
270 }
271 }
272 }
273
274 /* scan for '?' that marks end of dn and beginning of attributes */
275 attrs = NULL;
276 if ( ludp->lud_dn != NULL &&
277 ( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) {
278 /* terminate dn; point to start of attrs. */
279 *attrs++ = '\0';
280
281 /* scan for '?' that marks end of attrs and begin. of scope */
282 if (( p = strchr( attrs, '?' )) != NULL ) {
283 /*
284 * terminate attrs; point to start of scope and scan for
285 * '?' that marks end of scope and begin. of filter
286 */
287 *p++ = '\0';
288 scope = p;
289
290 if (( p = strchr( scope, '?' )) != NULL ) {
291 /* terminate scope; point to start of filter */
292 *p++ = '\0';
293 if ( *p != '\0' ) {
294 ludp->lud_filter = p;
295 /*
296 * scan for the '?' that marks the end
297 * of the filter and the start of any
298 * extensions
299 */
300 if (( p = strchr( ludp->lud_filter, '?' ))
301 != NULL ) {
302 *p++ = '\0'; /* term. filter */
303 extensions = p;
304 }
305 if ( *ludp->lud_filter == '\0' ) {
306 ludp->lud_filter = NULL;
307 } else {
308 nsldapi_hex_unescape( ludp->lud_filter );
309 }
310 }
311 }
312
313
314 if ( strcasecmp( scope, "one" ) == 0 ) {
315 ludp->lud_scope = LDAP_SCOPE_ONELEVEL;
316 } else if ( strcasecmp( scope, "base" ) == 0 ) {
317 ludp->lud_scope = LDAP_SCOPE_BASE;
318 } else if ( strcasecmp( scope, "sub" ) == 0 ) {
319 ludp->lud_scope = LDAP_SCOPE_SUBTREE;
320 } else if ( *scope != '\0' ) {
321 ldap_free_urldesc( ludp );
322 return( LDAP_URL_ERR_BADSCOPE );
323 }
324 }
325 }
326
327 if ( ludp->lud_dn != NULL ) {
328 nsldapi_hex_unescape( ludp->lud_dn );
329 }
330
331 /*
332 * if attrs list was included, turn it into a null-terminated array
333 */
334 if ( attrs != NULL && *attrs != '\0' ) {
335 nsldapi_hex_unescape( attrs );
336 for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) {
337 if ( *p == ',' ) {
338 ++nattrs;
339 }
340 }
341
342 if (( ludp->lud_attrs = (char **)NSLDAPI_CALLOC( nattrs + 1,
343 sizeof( char * ))) == NULL ) {
344 ldap_free_urldesc( ludp );
345 return( LDAP_URL_ERR_MEM );
346 }
347
348 for ( i = 0, p = attrs; i < nattrs; ++i ) {
349 ludp->lud_attrs[ i ] = p;
350 if (( p = strchr( p, ',' )) != NULL ) {
351 *p++ ='\0';
352 }
353 nsldapi_hex_unescape( ludp->lud_attrs[ i ] );
354 }
355 }
356
357 /* if extensions list was included, check for critical ones */
358 if ( extensions != NULL && *extensions != '\0' ) {
359 /* Note: at present, we do not recognize ANY extensions */
360 at_start = 1;
361 for ( p = extensions; *p != '\0'; ++p ) {
362 if ( at_start ) {
363 if ( *p == '!' ) { /* critical extension */
364 ldap_free_urldesc( ludp );
365 /* this is what iplanet did *
366 return( LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION );
367 * and this is what we do */
368 return( LDAP_URL_ERR_PARAM );
369 }
370 at_start = 0;
371 } else if ( *p == ',' ) {
372 at_start = 1;
373 }
374 }
375 }
376
377
378 *ludpp = ludp;
379
380 return( 0 );
381 }
382
383
384 void
385 LDAP_CALL
ldap_free_urldesc(LDAPURLDesc * ludp)386 ldap_free_urldesc( LDAPURLDesc *ludp )
387 {
388 if ( ludp != NULLLDAPURLDESC ) {
389 if ( ludp->lud_string != NULL ) {
390 NSLDAPI_FREE( ludp->lud_string );
391 }
392 if ( ludp->lud_attrs != NULL ) {
393 NSLDAPI_FREE( ludp->lud_attrs );
394 }
395 NSLDAPI_FREE( ludp );
396 }
397 }
398
399
400 int
401 LDAP_CALL
ldap_url_search(LDAP * ld,const char * url,int attrsonly)402 ldap_url_search( LDAP *ld, const char *url, int attrsonly )
403 {
404 int err, msgid;
405 LDAPURLDesc *ludp;
406 BerElement *ber;
407 LDAPServer *srv;
408 char *host;
409
410 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
411 return( -1 ); /* punt */
412 }
413
414 if ( ldap_url_parse( url, &ludp ) != 0 ) {
415 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
416 return( -1 );
417 }
418
419 LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
420 msgid = ++ld->ld_msgid;
421 LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
422
423 if ( nsldapi_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
424 ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
425 -1, -1, msgid, &ber ) != LDAP_SUCCESS ) {
426 return( -1 );
427 }
428
429 err = 0;
430
431 if ( ludp->lud_host == NULL ) {
432 host = ld->ld_defhost;
433 } else {
434 host = ludp->lud_host;
435 }
436
437 if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer )))
438 == NULL || ( host != NULL &&
439 ( srv->lsrv_host = nsldapi_strdup( host )) == NULL )) {
440 if ( srv != NULL ) {
441 NSLDAPI_FREE( srv );
442 }
443 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
444 err = -1;
445 } else {
446 if ( ludp->lud_port != 0 ) {
447 /* URL includes a port - use it */
448 srv->lsrv_port = ludp->lud_port;
449 } else if ( ludp->lud_host == NULL ) {
450 /* URL has no port or host - use port from ld */
451 srv->lsrv_port = ld->ld_defport;
452 } else if (( ludp->lud_options & LDAP_URL_OPT_SECURE ) == 0 ) {
453 /* ldap URL has a host but no port - use std. port */
454 srv->lsrv_port = LDAP_PORT;
455 } else {
456 /* ldaps URL has a host but no port - use std. port */
457 srv->lsrv_port = LDAPS_PORT;
458 }
459 }
460
461 if (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 ) {
462 srv->lsrv_options |= LDAP_SRV_OPT_SECURE;
463 }
464
465 if ( err != 0 ) {
466 ber_free( ber, 1 );
467 } else {
468 err = nsldapi_send_server_request( ld, ber, msgid, NULL, srv,
469 NULL, NULL, 1 );
470 }
471
472 ldap_free_urldesc( ludp );
473 return( err );
474 }
475
476
477 int
478 LDAP_CALL
ldap_url_search_st(LDAP * ld,const char * url,int attrsonly,struct timeval * timeout,LDAPMessage ** res)479 ldap_url_search_st( LDAP *ld, const char *url, int attrsonly,
480 struct timeval *timeout, LDAPMessage **res )
481 {
482 int msgid;
483
484 /*
485 * It is an error to pass in a zero'd timeval.
486 */
487 if ( timeout != NULL && timeout->tv_sec == 0 &&
488 timeout->tv_usec == 0 ) {
489 if ( ld != NULL ) {
490 LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
491 }
492 if ( res != NULL ) {
493 *res = NULL;
494 }
495 return( LDAP_PARAM_ERROR );
496 }
497
498 if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
499 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
500 }
501
502 if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
503 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
504 }
505
506 if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) {
507 (void) ldap_abandon( ld, msgid );
508 LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL );
509 return( LDAP_TIMEOUT );
510 }
511
512 return( ldap_result2error( ld, *res, 0 ));
513 }
514
515
516 int
517 LDAP_CALL
ldap_url_search_s(LDAP * ld,const char * url,int attrsonly,LDAPMessage ** res)518 ldap_url_search_s( LDAP *ld, const char *url, int attrsonly, LDAPMessage **res )
519 {
520 int msgid;
521
522 if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
523 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
524 }
525
526 if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
527 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
528 }
529
530 return( ldap_result2error( ld, *res, 0 ));
531 }
532
533 #ifdef _SOLARIS_SDK
534 /*
535 * Locate the LDAP URL associated with a DNS domain name.
536 *
537 * The supplied DNS domain name is converted into a distinguished
538 * name. The directory entry specified by that distinguished name
539 * is searched for a labeledURI attribute. If successful then the
540 * LDAP URL is returned. If unsuccessful then that entry's parent
541 * is searched and so on until the target distinguished name is
542 * reduced to only two nameparts.
543 *
544 * For example, if 'ny.eng.wiz.com' is the DNS domain then the
545 * following entries are searched until one succeeds:
546 * dc=ny,dc=eng,dc=wiz,dc=com
547 * dc=eng,dc=wiz,dc=com
548 * dc=wiz,dc=com
549 *
550 * If dns_name is NULL then the environment variable LOCALDOMAIN is used.
551 * If attrs is not NULL then it is appended to the URL's attribute list.
552 * If scope is not NULL then it overrides the URL's scope.
553 * If filter is not NULL then it is merged with the URL's filter.
554 *
555 * If an error is encountered then zero is returned, otherwise a string
556 * URL is returned. The caller should free the returned string if it is
557 * non-zero.
558 */
559
560 char *
ldap_dns_to_url(LDAP * ld,char * dns_name,char * attrs,char * scope,char * filter)561 ldap_dns_to_url(
562 LDAP *ld,
563 char *dns_name,
564 char *attrs,
565 char *scope,
566 char *filter
567 )
568 {
569 char *dn;
570 char *url = 0;
571 char *url2 = 0;
572 LDAPURLDesc *urldesc;
573 char *cp;
574 char *cp2;
575 size_t attrs_len = 0;
576 size_t scope_len = 0;
577 size_t filter_len = 0;
578 int nameparts;
579 int no_attrs = 0;
580 int no_scope = 0;
581
582 if (dns_name == 0) {
583 dns_name = (char *)getenv("LOCALDOMAIN");
584 }
585
586 if ((ld == NULL) || ((dn = ldap_dns_to_dn(dns_name, &nameparts)) ==
587 NULL))
588 return (0);
589
590 if ((url = ldap_dn_to_url(ld, dn, nameparts)) == NULL) {
591 free(dn);
592 return (0);
593 }
594 free(dn);
595
596 /* merge filter and/or scope and/or attributes with URL */
597 if (attrs || scope || filter) {
598
599 if (attrs)
600 attrs_len = strlen(attrs) + 2; /* for comma and NULL */
601
602 if (scope)
603 scope_len = strlen(scope) + 1; /* for NULL */
604
605 if (filter)
606 filter_len = strlen(filter) + 4;
607 /* for ampersand, parentheses and NULL */
608
609 if (ldap_is_ldap_url(url)) {
610
611 if ((url2 = (char *)malloc(attrs_len + scope_len +
612 filter_len + strlen(url) + 1)) == NULL) {
613 return (0);
614 }
615 cp = url;
616 cp2 = url2;
617
618 /* copy URL scheme, hostname, port number and DN */
619 while (*cp && (*cp != '?')) {
620 *cp2++ = *cp++;
621 }
622
623 /* handle URL attributes */
624
625 if (*cp == '?') { /* test first '?' */
626 *cp2++ = *cp++; /* copy first '?' */
627
628 if (*cp == '?') { /* test second '?' */
629
630 /* insert supplied attributes */
631 if (attrs) {
632 while (*attrs) {
633 *cp2++ = *attrs++;
634 }
635 } else {
636 no_attrs = 1;
637 }
638
639 } else {
640
641 /* copy URL attributes */
642 while (*cp && (*cp != '?')) {
643 *cp2++ = *cp++;
644 }
645
646 /* append supplied attributes */
647 if (attrs) {
648 *cp2++ = ',';
649 while (*attrs) {
650 *cp2++ = *attrs++;
651 }
652 }
653 }
654
655 } else {
656 /* append supplied attributes */
657 if (attrs) {
658 *cp2++ = '?';
659 while (*attrs) {
660 *cp2++ = *attrs++;
661 }
662 } else {
663 no_attrs = 1;
664 }
665 }
666
667 /* handle URL scope */
668
669 if (*cp == '?') { /* test second '?' */
670 *cp2++ = *cp++; /* copy second '?' */
671
672 if (*cp == '?') { /* test third '?' */
673
674 /* insert supplied scope */
675 if (scope) {
676 while (*scope) {
677 *cp2++ = *scope++;
678 }
679 } else {
680 no_scope = 1;
681 }
682
683 } else {
684
685 if (scope) {
686 /* skip over URL scope */
687 while (*cp && (*cp != '?')) {
688 *cp++;
689 }
690 /* insert supplied scope */
691 while (*scope) {
692 *cp2++ = *scope++;
693 }
694 } else {
695
696 /* copy URL scope */
697 while (*cp && (*cp != '?')) {
698 *cp2++ = *cp++;
699 }
700 }
701 }
702
703 } else {
704 /* append supplied scope */
705 if (scope) {
706 if (no_attrs) {
707 *cp2++ = '?';
708 }
709 *cp2++ = '?';
710 while (*scope) {
711 *cp2++ = *scope++;
712 }
713 } else {
714 no_scope = 1;
715 }
716 }
717
718 /* handle URL filter */
719
720 if (*cp == '?') { /* test third '?' */
721 *cp2++ = *cp++; /* copy third '?' */
722
723 if (filter) {
724
725 /* merge URL and supplied filters */
726
727 *cp2++ = '(';
728 *cp2++ = '&';
729 /* copy URL filter */
730 while (*cp) {
731 *cp2++ = *cp++;
732 }
733 /* append supplied filter */
734 while (*filter) {
735 *cp2++ = *filter++;
736 }
737 *cp2++ = ')';
738 } else {
739
740 /* copy URL filter */
741 while (*cp) {
742 *cp2++ = *cp++;
743 }
744 }
745
746 } else {
747 /* append supplied filter */
748 if (filter) {
749 if (no_scope) {
750 if (no_attrs) {
751 *cp2++ = '?';
752 }
753 *cp2++ = '?';
754 }
755 *cp2++ = '?';
756 while (*filter) {
757 *cp2++ = *filter++;
758 }
759 }
760 }
761
762 *cp2++ = '\0';
763 free (url);
764 url = url2;
765
766 } else {
767 return (0); /* not an LDAP URL */
768 }
769 }
770 return (url);
771 }
772
773 /*
774 * Locate the LDAP URL associated with a distinguished name.
775 *
776 * The number of nameparts in the supplied distinguished name must be
777 * provided. The specified directory entry is searched for a labeledURI
778 * attribute. If successful then the LDAP URL is returned. If unsuccessful
779 * then that entry's parent is searched and so on until the target
780 * distinguished name is reduced to only two nameparts.
781 *
782 * For example, if 'l=ny,ou=eng,o=wiz,c=us' is the distinguished name
783 * then the following entries are searched until one succeeds:
784 * l=ny,ou=eng,o=wiz,c=us
785 * ou=eng,o=wiz,c=us
786 * o=wiz,c=us
787 *
788 * If an error is encountered then zero is returned, otherwise a string
789 * URL is returned. The caller should free the returned string if it is
790 * non-zero.
791 */
792
793 char *
ldap_dn_to_url(LDAP * ld,char * dn,int nameparts)794 ldap_dn_to_url(
795 LDAP *ld,
796 char *dn,
797 int nameparts
798 )
799 {
800 char *next_dn = dn;
801 char *url = 0;
802 char *attrs[2] = {"labeledURI", 0};
803 LDAPMessage *res, *e;
804 char **vals;
805
806 /*
807 * Search for a URL in the named entry or its parent entry.
808 * Continue until only 2 nameparts remain.
809 */
810 while (dn && (nameparts > 1) && (! url)) {
811
812 /* search for the labeledURI attribute */
813 if (ldap_search_s(ld, dn, LDAP_SCOPE_BASE,
814 "(objectClass=*)", attrs, 0, &res) == LDAP_SUCCESS) {
815
816 /* locate the first entry returned */
817 if ((e = ldap_first_entry(ld, res)) != NULL) {
818
819 /* locate the labeledURI attribute */
820 if ((vals =
821 ldap_get_values(ld, e, "labeledURI")) !=
822 NULL) {
823
824 /* copy the attribute value */
825 if ((url = strdup((char *)vals[0])) !=
826 NULL) {
827 ldap_value_free(vals);
828 }
829 }
830 }
831 /* free the search results */
832 if (res != NULL) {
833 ldap_msgfree(res);
834 }
835 }
836
837 if (! url) {
838 /* advance along the DN by one namepart */
839 if (next_dn = strchr(dn, ',')) {
840 next_dn++;
841 dn = next_dn;
842 nameparts--;
843 }
844 }
845 }
846
847 return (url);
848 }
849
850 #endif /* _SOLARIS_SDK */
851