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
18static 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
51typedef int (*cancelptype)( void *cancelparm );
52#else /* NEEDPROTOS */
53typedef int (*cancelptype)();
54#endif /* NEEDPROTOS */
55
56#ifdef NEEDPROTOS
57static 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 );
61static LDAPMessage *ldap_msg_merge( LDAP *ld, LDAPMessage *a, LDAPMessage *b );
62static LDAPMessage *ldap_ufn_expand( LDAP *ld, cancelptype cancelproc,
63	void *cancelparm, char **dns, char *filter, int scope,
64	char **attrs, int aonly, int *err );
65LDAPFiltDesc *ldap_ufn_setfilter( LDAP *ld, char *fname );
66#else /* NEEDPROTOS */
67static LDAPMessage *ldap_msg_merge();
68static LDAPMessage *ldap_ufn_expand();
69LDAPFiltDesc *ldap_ufn_setfilter();
70#endif /* NEEDPROTOS */
71static LDAPMessage *ldap_msg_merge();
72static 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
101static int
102ldap_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
277int
278ldap_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 */
387int
388ldap_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 */
398int
399ldap_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
418static LDAPMessage *
419ldap_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
482static LDAPMessage *
483ldap_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
544LDAPFiltDesc *
545ldap_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
565void
566ldap_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
580int
581ldap_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