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