referral.c revision 3857:21b9b714e4ab
1174294Sobrien/* 2174294Sobrien * 3174294Sobrien * Copyright 1999 Sun Microsystems, Inc. All rights reserved. 4174294Sobrien * Use is subject to license terms. 5174294Sobrien * 6174294Sobrien * 7174294Sobrien * Comments: 8174294Sobrien * 9174294Sobrien */ 10174294Sobrien 11174294Sobrien#pragma ident "%Z%%M% %I% %E% SMI" 12174294Sobrien 13174294Sobrien#include <stdio.h> 14174294Sobrien#include <string.h> 15174294Sobrien#include <sys/time.h> 16174294Sobrien#include <sys/types.h> 17174294Sobrien#include <sys/socket.h> 18174294Sobrien#include <sys/errno.h> 19174294Sobrien#include "portable.h" 20174294Sobrien#include "lber.h" 21174294Sobrien#include "ldap.h" 22174294Sobrien#include "ldap-private.h" 23174294Sobrien#include "ldap-int.h" 24174294Sobrien 25174294Sobrienstatic BerElement * 26174294Sobrienre_encode_request( LDAP *ld, BerElement *origber, int msgid, LDAPURLDesc *urldesc ); 27174294Sobrienstatic void addFollowedRef(LDAPRequest *lr, char *ref); 28174294Sobrienstatic void addToFollowRef(LDAPRequest *lr, char *ref); 29174294Sobrienstatic int addUnFollowedRef(LDAP *ld, LDAPRequest *lr, char *ref); 30174294Sobrien 31174294Sobrienchar ** ldap_errormsg2referrals(char *errmsg) { 32174294Sobrien char ** refs; 33174294Sobrien int count; 34174294Sobrien size_t len; 35174294Sobrien char *p, *ref; 36174294Sobrien 37174294Sobrien if (errmsg == NULL){ 38174294Sobrien return (NULL); 39174294Sobrien } 40174294Sobrien len = strlen( errmsg ); 41174294Sobrien for ( p = errmsg; len >= LDAP_REF_STR_LEN; ++p, --len ) { 42174294Sobrien if (( *p == 'R' || *p == 'r' ) && strncasecmp( p, 43174294Sobrien LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) { 44174294Sobrien *p = '\0'; 45174294Sobrien p += LDAP_REF_STR_LEN; 46174294Sobrien break; 47174294Sobrien } 48174294Sobrien } 49174294Sobrien 50174294Sobrien if ( len < LDAP_REF_STR_LEN ) { 51174294Sobrien return( NULL); 52174294Sobrien } 53 count = 1; 54 ref = p; 55 while ((ref = strchr(ref, '\n')) != NULL) 56 count++; 57 58 if ((refs = (char **)calloc(count + 1, sizeof(char *))) == NULL){ 59 return (NULL); 60 } 61 62 count = 0; 63 for (ref = p; ref != NULL; ref= p){ 64 if ((p = strchr(ref, '\n')) != NULL){ 65 *p = '\0'; 66 } 67 refs[count++] = strdup(ref); 68 if (p != NULL) 69 *p++ = '\n'; 70 } 71 return (refs); 72} 73 74char *ldap_referral2error_msg(char ** refs) 75{ 76 int i; 77 size_t len = 0; 78 char *msg = NULL; 79 80 if (refs == NULL) 81 return (msg); 82 83 for (i = 0; refs[i] != NULL; i++){ 84 len += strlen (refs[i]) + 1; 85 } 86 87 if ((len > 0) && ((msg = (char *)malloc(len + LDAP_REF_STR_LEN + 2)) != NULL)) { 88 strncpy(msg, LDAP_REF_STR, LDAP_REF_STR_LEN); 89 for (i = 0; refs[i] != NULL; i++) { 90 strcat(msg, refs[i]); 91 strcat(msg, "\n"); 92 } 93 } 94 return (msg); 95} 96 97 98int 99chase_referrals( LDAP *ld, LDAPRequest *lr, char **refs, int *count, int samerequest ) 100{ 101 int rc, len, newdn, i, j, refcnt, errCode; 102 char *p, *ports, *ref, *tmpref, *refdn; 103 LDAPRequest *origreq; 104 LDAPServer *srv; 105 BerElement *ber; 106 LDAPURLDesc *ludp; 107 108 109 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 215, "=> chase_referrals\n"), 0, 0, 0 ); 110 111 ld->ld_errno = LDAP_SUCCESS; /* optimistic */ 112 *count = 0; 113 if ( refs == NULL ) { 114 return( LDAP_SUCCESS ); 115 } 116 117#ifdef _REENTRANT 118 LOCK_LDAP(ld); 119#endif 120 121 if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { 122 Debug( LDAP_DEBUG_ANY, 123 catgets(slapdcat, 1, 216, "more than %d referral hops (dropping)\n"), 124 ld->ld_refhoplimit, 0, 0 ); 125 /* XXX report as error in ld->ld_errno? */ 126 rc = ld->ld_errno = (ld->ld_version >= LDAP_VERSION3) ? LDAP_REFERRAL_LIMIT_EXCEEDED : LDAP_OTHER; 127#ifdef _REENTRANT 128 UNLOCK_LDAP(ld); 129#endif 130 return( rc ); 131 } 132 133 /* find original request */ 134 for ( origreq = lr; origreq->lr_parent != NULL; 135 origreq = origreq->lr_parent ) { 136 ; 137 } 138 139 for (refcnt = 0; refs[refcnt] != NULL; refcnt++) 140 ; /* Count number of referrals */ 141 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 1277, "%d possible referrals to chase\n"), refcnt, 0,0); 142 143 rc = 0; 144 /* parse out & follow referrals */ 145 for (i = 0; rc == 0 && refs[i] != NULL; i++) { 146 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "Try to chase %s\n"), refs[i], 0,0); 147 148 /* Parse URL */ 149 if (ldap_url_parse(refs[i], &ludp) != 0){ 150 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "Bad URL for referral %s\n"), refs[i], 0,0); 151 errCode = LDAP_PARAM_ERROR; 152 addUnFollowedRef(ld, lr, refs[i]); 153 /* try next URL */ 154 continue; 155 } 156 157 /* Encode previous request with new URL */ 158 if (( ber = re_encode_request( ld, origreq->lr_ber, ++ld->ld_msgid, ludp )) == NULL ) { 159 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "Error while encoding request for referral\n"), 0, 0,0); 160 ldap_free_urldesc(ludp); 161 errCode = ld->ld_errno; 162 addUnFollowedRef(ld, lr, refs[i]); 163 /* try next URL */ 164 continue; 165 } 166 167 if (( srv = (LDAPServer *)calloc( 1, sizeof( LDAPServer ))) == NULL ) { 168 ldap_free_urldesc(ludp); 169 ber_free( ber, 1 ); 170 rc = ld->ld_errno = LDAP_NO_MEMORY; 171#ifdef _REENTRANT 172 UNLOCK_LDAP(ld); 173#endif 174 return( rc ); 175 } 176 177 if (( srv->lsrv_host = strdup( ludp->lud_host ? ludp->lud_host : ld->ld_defhost)) == NULL ) { 178 ldap_free_urldesc(ludp); 179 free( (char *)srv ); 180 ber_free( ber, 1 ); 181 rc = ld->ld_errno = LDAP_NO_MEMORY; 182#ifdef _REENTRANT 183 UNLOCK_LDAP(ld); 184#endif 185 return( rc ); 186 } 187 188 srv->lsrv_port = ludp->lud_port ? ludp->lud_port : LDAP_PORT; 189 190 if ( srv != NULL && send_server_request( ld, ber, ld->ld_msgid, 191 lr, srv, NULL, 1 ) >= 0 ) { 192 ++*count; 193 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "Request has been forwarded to %s\n"), refs[i], 0,0); 194 addFollowedRef(lr, refs[i]); 195 for (j = i+1; refs[j] != NULL; j++){ 196 addToFollowRef(lr, refs[j]); 197 } 198 ldap_free_urldesc(ludp); 199 break; 200 } else { 201 Debug( LDAP_DEBUG_ANY, 202 catgets(slapdcat, 1, 220, "Unable to chase referral (%s)\n"), 203 ldap_err2string( ld->ld_errno ), 0, 0 ); 204 addUnFollowedRef(ld, lr, refs[i]); 205 errCode = ld->ld_errno; 206 } 207 ldap_free_urldesc(ludp); /* So far spawn all requests */ 208 } 209 210#ifdef _REENTRANT 211 UNLOCK_LDAP(ld); 212#endif 213 if (refs[i] != NULL) { 214 rc = LDAP_SUCCESS; 215 } else { 216 Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, -1, "No referral was successfully chased (last error %d)\n"), errCode, 0, 0); 217 rc = errCode; 218 } 219 Debug ( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 1278, "<= chase_referrals --- \n"), 0,0,0); 220 221 return( rc ); 222} 223 224static void addFollowedRef(LDAPRequest *lr, char *ref) 225{ 226 int i; 227 228 if (lr->lr_ref_followed == NULL){ 229 if ((lr->lr_ref_followed = (char **)calloc(2, sizeof(char*))) == NULL) 230 return; 231 i = 0; 232 } else { 233 for (i = 0; lr->lr_ref_followed[i] != NULL; i++); 234 if ((lr->lr_ref_followed = (char **)realloc((char *)lr->lr_ref_followed, (i+2) * sizeof(char *))) == NULL){ 235 return; 236 } 237 } 238 lr->lr_ref_followed[i++] = strdup(ref); 239 lr->lr_ref_followed[i] = NULL; 240 return; 241} 242 243static void addToFollowRef(LDAPRequest *lr, char *ref) 244{ 245 int i; 246 247 if (lr->lr_ref_tofollow == NULL){ 248 if ((lr->lr_ref_tofollow = (char **)calloc(2, sizeof(char*))) == NULL) 249 return; 250 i = 0; 251 } else { 252 for (i = 0; lr->lr_ref_tofollow[i] != NULL; i++); 253 if ((lr->lr_ref_tofollow = (char **)realloc((char *)lr->lr_ref_tofollow, (i+2) * sizeof(char *))) == NULL){ 254 return; 255 } 256 } 257 lr->lr_ref_tofollow[i++] = strdup(ref); 258 lr->lr_ref_tofollow[i] = NULL; 259 return; 260} 261 262static int addUnFollowedRef(LDAP *ld, LDAPRequest *lr, char *ref) 263{ 264 int i; 265 266 if (lr->lr_ref_unfollowed == NULL){ 267 if ((lr->lr_ref_unfollowed = (char **)calloc(2, sizeof(char*))) == NULL){ 268 ld->ld_errno = LDAP_NO_MEMORY; 269 return (-1); 270 } 271 i = 0; 272 } else { 273 for (i = 0; lr->lr_ref_unfollowed[i] != NULL; i++); 274 if ((lr->lr_ref_unfollowed = (char **)realloc((char *)lr->lr_ref_unfollowed, (i+2) * sizeof(char *))) == NULL){ 275 ld->ld_errno = LDAP_NO_MEMORY; 276 return (-1); 277 } 278 } 279 lr->lr_ref_unfollowed[i++] = strdup(ref); 280 lr->lr_ref_unfollowed[i] = NULL; 281 return (0); 282} 283 284 285int 286append_referral( LDAP *ld, char **referralsp, char *s ) 287{ 288 int first; 289 290 if ( *referralsp == NULL ) { 291 first = 1; 292 *referralsp = (char *)malloc( strlen( s ) + LDAP_REF_STR_LEN 293 + 1 ); 294 } else { 295 first = 0; 296 *referralsp = (char *)realloc( *referralsp, 297 strlen( *referralsp ) + strlen( s ) + 2 ); 298 } 299 300 if ( *referralsp == NULL ) { 301 ld->ld_errno = LDAP_NO_MEMORY; 302 return( -1 ); 303 } 304 305 if ( first ) { 306 strcpy( *referralsp, LDAP_REF_STR ); 307 } else { 308 strcat( *referralsp, "\n" ); 309 } 310 strcat( *referralsp, s ); 311 312 return( 0 ); 313} 314 315 316 317static BerElement * 318re_encode_request( LDAP *ld, BerElement *origber, int msgid, LDAPURLDesc *urldesc ) 319{ 320/* 321 * XXX this routine knows way too much about how the lber library works! 322 */ 323 unsigned int along, tag, len; 324 int ver, scope, deref, sizelimit, timelimit, attrsonly; 325 int rc, hasCtrls; 326 BerElement tmpber, *ber; 327 char *dn, *seqstart; 328 329 Debug( LDAP_DEBUG_TRACE, 330 catgets(slapdcat, 1, 221, "re_encode_request: new msgid %1$d, new dn <%2$s>\n"), 331 msgid, ( urldesc->lud_dn == NULL ) ? "NONE" : urldesc->lud_dn, 0 ); 332 333 tmpber = *origber; 334 335 /* 336 * all LDAP requests are sequences that start with a message id, 337 * followed by a sequence that is tagged with the operation code 338 */ 339 /* Bad assumption : delete op is not a sequence. 340 * So we have a special processing for it : it's much simpler 341 */ 342 if ( ber_scanf( &tmpber, "{i", &along ) != LDAP_TAG_MSGID || 343 ( tag = ber_peek_tag( &tmpber, &along )) == LBER_DEFAULT ) { 344 ld->ld_errno = LDAP_DECODING_ERROR; 345 return( NULL ); 346 } 347 348 /* Special case : delete request is not a sequence of... */ 349 if (tag == LDAP_REQ_EXTENDED){ 350 /* return error, I don't know how to do it automatically */ 351 ld->ld_errno = LDAP_NOT_SUPPORTED; 352 return (NULL); 353 } 354 355 if ( (ber = alloc_ber_with_options( ld )) == NULLBER ) { 356 return (NULL); 357 } 358 359 if (tag == LDAP_REQ_DELETE) { 360 if ( ber_get_stringa( &tmpber, &dn ) == LBER_DEFAULT ) { 361 ld->ld_errno = LDAP_DECODING_ERROR; 362 Debug(LDAP_DEBUG_TRACE, 363 catgets(slapdcat, 1, 1279,"Error in decoding delete DN"),0,0,0); 364 ber_free( ber, 1); 365 return( NULL ); 366 } 367 /* Check if controls */ 368 hasCtrls = 0; 369 if (ber_peek_tag(&tmpber, &len) == LDAP_TAG_CONTROL_LIST){ 370 hasCtrls = 1; 371 } 372 373 if ( urldesc->lud_dn && *urldesc->lud_dn ) { 374 free( dn ); 375 dn = urldesc->lud_dn; 376 } 377 if ( ber_printf( ber, "{its", msgid, tag, dn ) == -1 ) { 378 Debug(LDAP_DEBUG_TRACE, "Error in re_encoding delete request",0,0,0); 379 ld->ld_errno = LDAP_ENCODING_ERROR; 380 ber_free( ber, 1 ); 381 return (NULL); 382 } 383 /* Now add controls if any */ 384 if (hasCtrls && ber_write( ber, tmpber.ber_ptr, len, 0 ) != len ) { 385 ld->ld_errno = LDAP_ENCODING_ERROR; 386 ber_free( ber, 1 ); 387 return( NULL ); 388 } 389 if (ber_printf( ber, "}" ) == -1 ) { 390 ld->ld_errno = LDAP_ENCODING_ERROR; 391 ber_free( ber, 1 ); 392 return( NULL ); 393 } 394 395#ifdef LDAP_DEBUG 396 if ( ldap_debug & LDAP_DEBUG_PACKETS ) { 397 Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, 222, "re_encode_request new request is:\n"), 398 0, 0, 0 ); 399 ber_dump( ber, 0 ); 400 } 401#endif /* LDAP_DEBUG */ 402 return (ber); 403 } 404 405 if (( tag = ber_skip_tag( &tmpber, &along )) == LBER_DEFAULT ) { 406 ld->ld_errno = LDAP_DECODING_ERROR; 407 return( NULL ); 408 } 409 /* Keep length and pointer */ 410 seqstart = tmpber.ber_ptr; 411 412 /* bind requests have a version number before the DN & other stuff */ 413 if ( tag == LDAP_REQ_BIND && ber_get_int( &tmpber, &ver ) == 414 LBER_DEFAULT ) { 415 ld->ld_errno = LDAP_DECODING_ERROR; 416 ber_free( ber, 1 ); 417 return( NULL ); 418 } 419 420 /* the rest of the request is the DN followed by other stuff */ 421 if ( ber_get_stringa( &tmpber, &dn ) == LBER_DEFAULT ) { 422 ber_free( ber, 1 ); 423 return( NULL ); 424 } 425 if ( urldesc->lud_dn != NULL ) { 426 free( dn ); 427 dn = urldesc->lud_dn; 428 } 429 430 /* see what to do with CONTROLS */ 431 432 if ( tag == LDAP_REQ_BIND ) { 433 rc = ber_printf( ber, "{it{is", msgid, tag, ver, dn ); 434 } else { 435 rc = ber_printf( ber, "{it{s", msgid, tag, dn ); 436 } 437 438 if ( rc == -1 ) { 439 ber_free( ber, 1 ); 440 return( NULL ); 441 } 442 443 if (tag == LDAP_REQ_SEARCH) { 444 /* Now for SEARCH, decode more of the request */ 445 if (ber_scanf(&tmpber, "iiiib", &scope, &deref, &sizelimit, &timelimit, &attrsonly) == LBER_DEFAULT){ 446 ld->ld_errno = LDAP_DECODING_ERROR; 447 ber_free( ber, 1 ); 448 return( NULL ); 449 } 450 if (ber_printf(ber, "iiiib", urldesc->lud_scope == LDAP_SCOPE_UNKNOWN ? scope : urldesc->lud_scope, 451 deref, sizelimit, timelimit, attrsonly) == -1) { 452 ld->ld_errno = LDAP_ENCODING_ERROR; 453 ber_free( ber, 1 ); 454 return( NULL ); 455 } 456 /* We should then decode and check the filter as opposed to ludp->lud_filter */ 457 /* Same for attributes */ 458 /* Later */ 459 } 460 /* The rest is the same for all requests */ 461 462 /* Copy Buffer from tmpber.ber_ptr for along - (tmpber.ber_ptr - seqstart) */ 463 /* It's the rest of the request */ 464 len = along - ( tmpber.ber_ptr - seqstart); 465 if ( ber_write( ber, tmpber.ber_ptr, len, 0) != len || 466 ber_printf( ber, "}" ) == -1 ) { 467 ld->ld_errno = LDAP_ENCODING_ERROR; 468 ber_free( ber, 1 ); 469 return( NULL ); 470 } 471 472 if (seqstart + along < tmpber.ber_end){ /* there's probably some controls, copy them also */ 473 len = tmpber.ber_end - seqstart - along; 474 if ( ber_write( ber, seqstart + along, len, 0) != len ){ 475 ld->ld_errno = LDAP_ENCODING_ERROR; 476 ber_free( ber, 1 ); 477 return( NULL ); 478 } 479 } 480 481 if ( ber_printf(ber, "}") == -1) { 482 ld->ld_errno = LDAP_ENCODING_ERROR; 483 ber_free( ber, 1 ); 484 return( NULL ); 485 } 486 487#ifdef LDAP_DEBUG 488 if ( ldap_debug & LDAP_DEBUG_PACKETS ) { 489 Debug( LDAP_DEBUG_ANY, catgets(slapdcat, 1, 222, "re_encode_request new request is:\n"), 490 0, 0, 0 ); 491 ber_dump( ber, 0 ); 492 } 493#endif /* LDAP_DEBUG */ 494 495 return( ber ); 496} 497 498 499LDAPRequest * 500find_request_by_msgid( LDAP *ld, int msgid ) 501{ 502 LDAPRequest *lr; 503 504 for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { 505 if ( msgid == lr->lr_msgid ) { 506 break; 507 } 508 } 509 510 return( lr ); 511} 512