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