xref: /onnv-gate/usr/src/lib/libldap4/common/ufn.c (revision 3857:21b9b714e4ab)
1 /*
2  *
3  * Portions Copyright 1998 Sun Microsystems, Inc.  All rights reserved.
4  * Use is subject to license terms.
5  *
6  */
7 
8 #pragma ident	"%Z%%M%	%I%	%E% SMI"
9 
10 /*
11  *  Copyright (c) 1990 Regents of the University of Michigan.
12  *  All rights reserved.
13  *
14  *  ufn.c
15  */
16 
17 #ifndef lint
18 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
19 #endif
20 
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <stdlib.h> /* malloc(), realloc(), free() */
25 
26 #ifdef MACOS
27 #include <stdlib.h>
28 #include "macos.h"
29 #else /* MACOS */
30 #if defined( DOS ) || defined( _WIN32 )
31 #include "msdos.h"
32 #else /* DOS */
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #endif /* DOS */
37 #endif /* MACOS */
38 
39 #include "lber.h"
40 #include "ldap.h"
41 #include "ldap-private.h"
42 #include "ldap-int.h"
43 #ifdef SUN
44 /*
45  * to include definition of FILTERFILE and or TEMPLATEFILE
46  */
47 #include "ldapconfig.h"
48 #endif
49 
50 #ifdef NEEDPROTOS
51 typedef int (*cancelptype)( void *cancelparm );
52 #else /* NEEDPROTOS */
53 typedef int (*cancelptype)();
54 #endif /* NEEDPROTOS */
55 
56 #ifdef NEEDPROTOS
57 static int ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp,
58 	char *prefix, char **attrs, int attrsonly, LDAPMessage **res,
59 	cancelptype cancelproc, void *cancelparm, char *tag1, char *tag2,
60 	char *tag3 );
61 static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b );
62 static LDAPMessage *ldap_ufn_expand( LDAP *ld, cancelptype cancelproc,
63 	void *cancelparm, char **dns, char *filter, int scope,
64 	char **attrs, int aonly, int *err );
65 LDAPFiltDesc *ldap_ufn_setfilter( LDAP *ld, char *fname );
66 #else /* NEEDPROTOS */
67 static LDAPMessage *ldap_msg_merge();
68 static LDAPMessage *ldap_ufn_expand();
69 LDAPFiltDesc *ldap_ufn_setfilter();
70 #endif /* NEEDPROTOS */
71 static LDAPMessage *ldap_msg_merge();
72 static LDAPMessage *ldap_ufn_expand();
73 
74 /*
75  * ldap_ufn_search_ctx - do user friendly searching; provide cancel feature;
76  *			specify ldapfilter.conf tags for each phase of search
77  *
78  *	ld		LDAP descriptor
79  *	ufncomp		the exploded user friendly name to look for
80  *	ncomp		number of elements in ufncomp
81  *	prefix		where to start searching
82  *	attrs		list of attribute types to return for matches
83  *	attrsonly	1 => attributes only 0 => attributes and values
84  *	res		will contain the result of the search
85  *	cancelproc	routine that returns non-zero if operation should be
86  *			cancelled.  This can be NULL.  If it is non-NULL, the
87  *			routine will be called periodically.
88  *	cancelparm	void * that is passed to cancelproc
89  *	tag[123]	the ldapfilter.conf tag that will be used in phases
90  *			1, 2, and 3 of the search, respectively
91  *
92  * Example:
93  *	char		*attrs[] = { "mail", "title", 0 };
94  *	char		*ufncomp[] = { "howes", "umich", "us", 0 }
95  *	LDAPMessage	*res;
96  *	error = ldap_ufn_search_ctx( ld, ufncomp, 3, NULL, attrs, attrsonly,
97  *			&res, acancelproc, along, "ufn first",
98  *			"ufn intermediate", "ufn last" );
99  */
100 
101 static int
ldap_ufn_search_ctx(LDAP * ld,char ** ufncomp,int ncomp,char * prefix,char ** attrs,int attrsonly,LDAPMessage ** res,cancelptype cancelproc,void * cancelparm,char * tag1,char * tag2,char * tag3)102 ldap_ufn_search_ctx( LDAP *ld, char **ufncomp, int ncomp, char *prefix,
103 	char **attrs, int attrsonly, LDAPMessage **res, cancelptype cancelproc,
104 	void *cancelparm, char *tag1, char *tag2, char *tag3 )
105 {
106 	char		*dn, *ftag;
107 	char		**dns;
108 	int		max, i, err, scope, phase, tries;
109 	LDAPFiltInfo	*fi;
110 	LDAPMessage	*tmpcand;
111 	LDAPMessage	*candidates;
112 	/* LDAPMessage	*ldap_msg_merge(), *ldap_ufn_expand(); */
113 	static char	*objattrs[] = { "objectClass", NULL };
114 
115 	/*
116 	 * look up ufn components from most to least significant.
117 	 * there are 3 phases.
118 	 * 	phase 1	search the root for orgs or countries
119 	 * 	phase 2	search for orgs
120 	 * 	phase 3	search for a person
121 	 * in phases 1 and 2, we are building a list of candidate DNs,
122 	 * below which we will search for the final component of the ufn.
123 	 * for each component we try the filters listed in the
124 	 * filterconfig file, first one-level (except the last compoment),
125 	 * then subtree.  if any of them produce any results, we go on to
126 	 * the next component.
127 	 */
128 
129 #if defined( SUN ) && defined( _REENTRANT )
130         LOCK_LDAP(ld);
131 #endif
132 	*res = NULL;
133 	candidates = NULL;
134 	phase = 1;
135 	for ( ncomp--; ncomp != -1; ncomp-- ) {
136 		if ( *ufncomp[ncomp] == '"' ) {
137 			char	*quote;
138 
139 			if ( (quote = strrchr( ufncomp[ncomp], '"' )) != NULL )
140 				*quote = '\0';
141 			(void) strcpy( ufncomp[ncomp], ufncomp[ncomp] + 1 );
142 		}
143 		if ( ncomp == 0 )
144 			phase = 3;
145 
146 		switch ( phase ) {
147 		case 1:
148 			ftag = tag1;
149 			scope = LDAP_SCOPE_ONELEVEL;
150 			break;
151 		case 2:
152 			ftag = tag2;
153 			scope = LDAP_SCOPE_ONELEVEL;
154 			break;
155 		case 3:
156 			ftag = tag3;
157 			scope = LDAP_SCOPE_SUBTREE;
158 			break;
159 		}
160 
161 		/*
162 		 * construct an array of DN's to search below from the
163 		 * list of candidates.
164 		 */
165 
166 		if ( candidates == NULL ) {
167 			if ( prefix != NULL ) {
168 				if ( (dns = (char **) malloc( sizeof(char *)
169 				    * 2 )) == NULL ) {
170 #if defined( SUN ) && defined( _REENTRANT )
171 					UNLOCK_LDAP(ld);
172 #endif
173 					return( ld->ld_errno = LDAP_NO_MEMORY );
174 				}
175 				dns[0] = strdup( prefix );
176 				dns[1] = NULL;
177 			} else {
178 				dns = NULL;
179 			}
180 		} else {
181 			i = 0, max = 0;
182 			for ( tmpcand = candidates; tmpcand != NULL &&
183 			    tmpcand->lm_msgtype != LDAP_RES_SEARCH_RESULT;
184 			    tmpcand = tmpcand->lm_chain )
185 			{
186 				if ( (dn = ldap_get_dn( ld, tmpcand )) == NULL )
187 					continue;
188 
189 				if ( dns == NULL ) {
190 					if ( (dns = (char **) malloc(
191 					    sizeof(char *) * 8 )) == NULL ) {
192 						ld->ld_errno = LDAP_NO_MEMORY;
193 #if defined( SUN ) && defined( _REENTRANT )
194 						UNLOCK_LDAP(ld);
195 #endif
196 						return( LDAP_NO_MEMORY );
197 					}
198 					max = 8;
199 				} else if ( i >= max ) {
200 					if ( (dns = (char **) realloc( dns,
201 					    sizeof(char *) * 2 * max ))
202 					    == NULL )
203 					{
204 						ld->ld_errno = LDAP_NO_MEMORY;
205 #if defined( SUN ) && defined( _REENTRANT )
206 						UNLOCK_LDAP(ld);
207 #endif
208 						return( LDAP_NO_MEMORY );
209 					}
210 					max *= 2;
211 				}
212 				dns[i++] = dn;
213 				dns[i] = NULL;
214 			}
215 			ldap_msgfree( candidates );
216 			candidates = NULL;
217 		}
218 		tries = 0;
219 	tryagain:
220 		tries++;
221 		for ( fi = ldap_getfirstfilter( ld->ld_filtd, ftag,
222 		    ufncomp[ncomp] ); fi != NULL;
223 		    fi = ldap_getnextfilter( ld->ld_filtd ) )
224 		{
225 			if ( (candidates = ldap_ufn_expand( ld, cancelproc,
226 			    cancelparm, dns, fi->lfi_filter, scope,
227 			    phase == 3 ? attrs : objattrs,
228 			    phase == 3 ? attrsonly : 1, &err )) != NULL )
229 			{
230 				break;
231 			}
232 
233 			if ( err == -1 || err == LDAP_USER_CANCELLED ) {
234 				if ( dns != NULL ) {
235 					ldap_value_free( dns );
236 					dns = NULL;
237 				}
238 #if defined( SUN ) && defined( _REENTRANT )
239 				UNLOCK_LDAP(ld);
240 #endif
241 				return( err );
242 			}
243 		}
244 
245 		if ( candidates == NULL ) {
246 			if ( tries < 2 && phase != 3 ) {
247 				scope = LDAP_SCOPE_SUBTREE;
248 				goto tryagain;
249 			} else {
250 				if ( dns != NULL ) {
251 					ldap_value_free( dns );
252 					dns = NULL;
253 				}
254 #if defined( SUN ) && defined( _REENTRANT )
255 				UNLOCK_LDAP(ld);
256 #endif
257 				return( err );
258 			}
259 		}
260 
261 		/* go on to the next component */
262 		if ( phase == 1 )
263 			phase++;
264 		if ( dns != NULL ) {
265 			ldap_value_free( dns );
266 			dns = NULL;
267 		}
268 	}
269 	*res = candidates;
270 
271 #if defined( SUN ) && defined( _REENTRANT )
272         UNLOCK_LDAP(ld);
273 #endif
274 	return( err );
275 }
276 
277 int
ldap_ufn_search_ct(LDAP * ld,char * ufn,char ** attrs,int attrsonly,LDAPMessage ** res,cancelptype cancelproc,void * cancelparm,char * tag1,char * tag2,char * tag3)278 ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs, int attrsonly,
279 	LDAPMessage **res, cancelptype cancelproc, void *cancelparm,
280 	char *tag1, char *tag2, char *tag3 )
281 {
282 	char	**ufncomp, **prefixcomp;
283 	char	*pbuf;
284 	int	ncomp, pcomp, i, err;
285 
286 #if defined( SUN ) && defined( _REENTRANT )
287         LOCK_LDAP(ld);
288 #endif
289 	/* initialize the getfilter stuff if it's not already */
290 	if ( ld->ld_filtd == NULL && ldap_ufn_setfilter( ld, FILTERFILE )
291 	    == NULL ) {
292 #if defined( SUN ) && defined( _REENTRANT )
293 		UNLOCK_LDAP(ld);
294 #endif
295 		return( ld->ld_errno = LDAP_LOCAL_ERROR );
296 	}
297 
298 	/* call ldap_explode_dn() to break the ufn into its components */
299 	if ( (ufncomp = ldap_explode_dn( ufn, 0 )) == NULL ) {
300 #if defined( SUN ) && defined( _REENTRANT )
301 		UNLOCK_LDAP(ld);
302 #endif
303 		return( ld->ld_errno = LDAP_LOCAL_ERROR );
304 	}
305 	for ( ncomp = 0; ufncomp[ncomp] != NULL; ncomp++ )
306 		;	/* NULL */
307 
308 	/* more than two components => try it fully qualified first */
309 	if ( ncomp > 2 || ld->ld_ufnprefix == NULL ) {
310 		err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, NULL, attrs,
311 		    attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
312 
313 		if ( ldap_count_entries( ld, *res ) > 0 ) {
314 			ldap_value_free( ufncomp );
315 #if defined( SUN ) && defined( _REENTRANT )
316 			UNLOCK_LDAP(ld);
317 #endif
318 			return( err );
319 		} else {
320 			ldap_msgfree( *res );
321 			*res = NULL;
322 		}
323 	}
324 
325 	if ( ld->ld_ufnprefix == NULL ) {
326 		ldap_value_free( ufncomp );
327 #if defined( SUN ) && defined( _REENTRANT )
328 		UNLOCK_LDAP(ld);
329 #endif
330 		return( err );
331 	}
332 
333 	/* if that failed, or < 2 components, use the prefix */
334 	if ( (prefixcomp = ldap_explode_dn( ld->ld_ufnprefix, 0 )) == NULL ) {
335 		ldap_value_free( ufncomp );
336 #if defined( SUN ) && defined( _REENTRANT )
337 		UNLOCK_LDAP(ld);
338 #endif
339 		return( ld->ld_errno = LDAP_LOCAL_ERROR );
340 	}
341 	for ( pcomp = 0; prefixcomp[pcomp] != NULL; pcomp++ )
342 		;	/* NULL */
343 	if ( (pbuf = (char *) malloc( strlen( ld->ld_ufnprefix ) + 1 ))
344 	    == NULL ) {
345 		ldap_value_free( ufncomp );
346 		ldap_value_free( prefixcomp );
347 #if defined( SUN ) && defined( _REENTRANT )
348 		UNLOCK_LDAP(ld);
349 #endif
350 		return( ld->ld_errno = LDAP_NO_MEMORY );
351 	}
352 
353 	for ( i = 0; i < pcomp; i++ ) {
354 		int	j;
355 
356 		*pbuf = '\0';
357 		for ( j = i; j < pcomp; j++ ) {
358 			(void) strcat( pbuf, prefixcomp[j] );
359 			if ( j + 1 < pcomp )
360 				(void) strcat( pbuf, "," );
361 		}
362 		err = ldap_ufn_search_ctx( ld, ufncomp, ncomp, pbuf, attrs,
363 		    attrsonly, res, cancelproc, cancelparm, tag1, tag2, tag3 );
364 
365 		if ( ldap_count_entries( ld, *res ) > 0 ) {
366 			break;
367 		} else {
368 			ldap_msgfree( *res );
369 			*res = NULL;
370 		}
371 	}
372 
373 	ldap_value_free( ufncomp );
374 	ldap_value_free( prefixcomp );
375 	free( pbuf );
376 
377 #if defined( SUN ) && defined( _REENTRANT )
378         UNLOCK_LDAP(ld);
379 #endif
380 	return( err );
381 }
382 
383 /*
384  * same as ldap_ufn_search_ct, except without the ability to specify
385  * ldapfilter.conf tags.
386  */
387 int
ldap_ufn_search_c(LDAP * ld,char * ufn,char ** attrs,int attrsonly,LDAPMessage ** res,cancelptype cancelproc,void * cancelparm)388 ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs, int attrsonly,
389 	LDAPMessage **res, cancelptype cancelproc, void *cancelparm )
390 {
391 	return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res, cancelproc,
392 	    cancelparm, "ufn first", "ufn intermediate", "ufn last" ) );
393 }
394 
395 /*
396  * same as ldap_ufn_search_c without the cancel function
397  */
398 int
ldap_ufn_search_s(LDAP * ld,char * ufn,char ** attrs,int attrsonly,LDAPMessage ** res)399 ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs, int attrsonly,
400 	LDAPMessage **res )
401 {
402 	struct timeval	tv;
403 
404 	tv.tv_sec = ld->ld_timelimit;
405 
406 	return( ldap_ufn_search_ct( ld, ufn, attrs, attrsonly, res,
407 		ld->ld_timelimit ? ldap_ufn_timeout : NULL,
408 		ld->ld_timelimit ? (void *) &tv : NULL,
409 		"ufn first", "ufn intermediate", "ufn last" ) );
410 }
411 
412 
413 /*
414  * ldap_msg_merge - merge two ldap search result chains.  the more
415  * serious of the two error result codes is kept.
416  */
417 
418 static LDAPMessage *
ldap_msg_merge(LDAP * ld,LDAPMessage * a,LDAPMessage * b)419 ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b )
420 {
421 	LDAPMessage	*end, *aprev, *aend, *bprev, *bend;
422 
423 	if ( a == NULL )
424 		return( b );
425 
426 	if ( b == NULL )
427 		return( a );
428 
429 	/* find the ends of the a and b chains */
430 	aprev = NULL;
431 	for ( aend = a; aend->lm_chain != NULL; aend = aend->lm_chain )
432 		aprev = aend;
433 	bprev = NULL;
434 	for ( bend = b; bend->lm_chain != NULL; bend = bend->lm_chain )
435 		bprev = bend;
436 
437 	/* keep result a */
438 	if ( ldap_result2error( ld, aend, 0 ) != LDAP_SUCCESS ) {
439 		/* remove result b */
440 		ldap_msgfree( bend );
441 		if ( bprev != NULL )
442 			bprev->lm_chain = NULL;
443 		else
444 			b = NULL;
445 		end = aend;
446 		if ( aprev != NULL )
447 			aprev->lm_chain = NULL;
448 		else
449 			a = NULL;
450 	/* keep result b */
451 	} else {
452 		/* remove result a */
453 		ldap_msgfree( aend );
454 		if ( aprev != NULL )
455 			aprev->lm_chain = NULL;
456 		else
457 			a = NULL;
458 		end = bend;
459 		if ( bprev != NULL )
460 			bprev->lm_chain = NULL;
461 		else
462 			b = NULL;
463 	}
464 
465 	if ( (a == NULL && b == NULL) || (a == NULL && bprev == NULL) ||
466 	    (b == NULL && aprev == NULL) )
467 		return( end );
468 
469 	if ( a == NULL ) {
470 		bprev->lm_chain = end;
471 		return( b );
472 	} else if ( b == NULL ) {
473 		aprev->lm_chain = end;
474 		return( a );
475 	} else {
476 		bprev->lm_chain = end;
477 		aprev->lm_chain = b;
478 		return( a );
479 	}
480 }
481 
482 static LDAPMessage *
ldap_ufn_expand(LDAP * ld,cancelptype cancelproc,void * cancelparm,char ** dns,char * filter,int scope,char ** attrs,int aonly,int * err)483 ldap_ufn_expand( LDAP *ld, cancelptype cancelproc, void *cancelparm,
484 	char **dns, char *filter, int scope, char **attrs, int aonly,
485 	int *err )
486 {
487 	LDAPMessage	*tmpcand, *tmpres;
488 	char		*dn;
489 	int		i, msgid;
490 	struct timeval	tv;
491 
492 	/* search for this component below the current candidates */
493 	tmpcand = NULL;
494 	i = 0;
495 	do {
496 		if ( dns != NULL )
497 			dn = dns[i];
498 		else
499 			dn = "";
500 
501 		if (( msgid = ldap_search( ld, dn, scope, filter, attrs,
502 		    aonly )) == -1 ) {
503 			ldap_msgfree( tmpcand );
504 			*err = ld->ld_errno;
505 			return( NULL );
506 		}
507 
508 		tv.tv_sec = 0;
509 		tv.tv_usec = 100000;	/* 1/10 of a second */
510 
511 		do {
512 			*err = ldap_result( ld, msgid, 1, &tv, &tmpres );
513 			if ( *err == 0 && cancelproc != NULL &&
514 			    (*cancelproc)( cancelparm ) != 0 ) {
515 				ldap_abandon( ld, msgid );
516 				*err = LDAP_USER_CANCELLED;
517 				ld->ld_errno = LDAP_USER_CANCELLED;
518 			}
519 		} while ( *err == 0 );
520 
521 		if ( *err == LDAP_USER_CANCELLED || *err < 0 ||
522 		    ( *err = ldap_result2error( ld, tmpres, 0 )) == -1 ) {
523 			ldap_msgfree( tmpcand );
524 			return( NULL );
525 		}
526 
527 		tmpcand = ldap_msg_merge( ld, tmpcand, tmpres );
528 
529 		i++;
530 	} while ( dns != NULL && dns[i] != NULL );
531 
532 	if ( ldap_count_entries( ld, tmpcand ) > 0 ) {
533 		return( tmpcand );
534 	} else {
535 		ldap_msgfree( tmpcand );
536 		return( NULL );
537 	}
538 }
539 
540 /*
541  * ldap_ufn_setfilter - set the filter config file used in ufn searching
542  */
543 
544 LDAPFiltDesc *
ldap_ufn_setfilter(LDAP * ld,char * fname)545 ldap_ufn_setfilter( LDAP *ld, char *fname )
546 {
547 #if defined( SUN ) && defined( _REENTRANT )
548 	LDAPFiltDesc *rv;
549 
550         LOCK_LDAP(ld);
551 #endif
552 	if ( ld->ld_filtd != NULL )
553 		ldap_getfilter_free( ld->ld_filtd );
554 
555 #if defined( SUN ) && defined( _REENTRANT )
556         ld->ld_filtd = ldap_init_getfilter( fname );
557 	rv = ld->ld_filtd;
558         UNLOCK_LDAP(ld);
559 	return( rv );
560 #else
561 	return( ld->ld_filtd = ldap_init_getfilter( fname ) );
562 #endif
563 }
564 
565 void
ldap_ufn_setprefix(LDAP * ld,char * prefix)566 ldap_ufn_setprefix( LDAP *ld, char *prefix )
567 {
568 #if defined( SUN ) && defined( _REENTRANT )
569         LOCK_LDAP(ld);
570 #endif
571 	if ( ld->ld_ufnprefix != NULL )
572 		free( ld->ld_ufnprefix );
573 
574 	ld->ld_ufnprefix = strdup( prefix );
575 #if defined( SUN ) && defined( _REENTRANT )
576         UNLOCK_LDAP(ld);
577 #endif
578 }
579 
580 int
ldap_ufn_timeout(void * tvparam)581 ldap_ufn_timeout( void *tvparam )
582 {
583 	struct timeval	*tv;
584 
585 	tv = (struct timeval *)tvparam;
586 
587 	if ( tv->tv_sec != 0 ) {
588 		tv->tv_usec = tv->tv_sec * 1000000;	/* sec => micro sec */
589 		tv->tv_sec = 0;
590 	}
591 	tv->tv_usec -= 100000;	/* 1/10 of a second */
592 
593 	return( tv->tv_usec <= 0 ? 1 : 0 );
594 }
595