xref: /onnv-gate/usr/src/lib/libldap4/common/referral.c (revision 3857:21b9b714e4ab)
1 /*
2  *
3  * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
4  * Use is subject to license terms.
5  *
6  *
7  * Comments:
8  *
9  */
10 
11 #pragma ident	"%Z%%M%	%I%	%E% SMI"
12 
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/errno.h>
19 #include "portable.h"
20 #include "lber.h"
21 #include "ldap.h"
22 #include "ldap-private.h"
23 #include "ldap-int.h"
24 
25 static BerElement *
26 re_encode_request( LDAP *ld, BerElement *origber, int msgid, LDAPURLDesc *urldesc );
27 static void addFollowedRef(LDAPRequest *lr, char *ref);
28 static void addToFollowRef(LDAPRequest *lr, char *ref);
29 static int addUnFollowedRef(LDAP *ld, LDAPRequest *lr, char *ref);
30 
ldap_errormsg2referrals(char * errmsg)31 char ** ldap_errormsg2referrals(char *errmsg) {
32 	char ** refs;
33 	int count;
34 	size_t  len;
35 	char *p, *ref;
36 
37 	if (errmsg == NULL){
38 		return (NULL);
39 	}
40 	len = strlen( errmsg );
41 	for ( p = errmsg; len >= LDAP_REF_STR_LEN; ++p, --len ) {
42 		if (( *p == 'R' || *p == 'r' ) && strncasecmp( p,
43 		    LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
44 			*p = '\0';
45 			p += LDAP_REF_STR_LEN;
46 			break;
47 		}
48 	}
49 
50 	if ( len < LDAP_REF_STR_LEN ) {
51 		return( NULL);
52 	}
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 
ldap_referral2error_msg(char ** refs)74 char *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 
98 int
chase_referrals(LDAP * ld,LDAPRequest * lr,char ** refs,int * count,int samerequest)99 chase_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 
addFollowedRef(LDAPRequest * lr,char * ref)224 static 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 
addToFollowRef(LDAPRequest * lr,char * ref)243 static 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 
addUnFollowedRef(LDAP * ld,LDAPRequest * lr,char * ref)262 static 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 
285 int
append_referral(LDAP * ld,char ** referralsp,char * s)286 append_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 
317 static BerElement *
re_encode_request(LDAP * ld,BerElement * origber,int msgid,LDAPURLDesc * urldesc)318 re_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 
499 LDAPRequest *
find_request_by_msgid(LDAP * ld,int msgid)500 find_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