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