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