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