Example #1
0
/*
 * Chase v3 referrals
 *
 * Parameters:
 *  (IN) ld = LDAP connection handle
 *  (IN) lr = LDAP Request structure
 *  (IN) refs = array of pointers to referral strings that we will chase
 *              The array will be free'd by this function when no longer needed
 *  (IN) sref != 0 if following search reference
 *  (OUT) errstrp = Place to return a string of referrals which could not be followed
 *  (OUT) hadrefp = 1 if sucessfully followed referral
 *
 * Return value - number of referrals followed
 *
 * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
 */
int
ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
{
	char		*unfollowed;
	int		 unfollowedcnt = 0;
	LDAPRequest	*origreq;
	LDAPURLDesc	*srv = NULL;
	BerElement	*ber;
	char		**refarray = NULL;
	LDAPConn	*lc;
	int			 rc, count, i, j, id;
	LDAPreqinfo  rinfo;
	LDAP_NEXTREF_PROC	*nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );

	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
	*hadrefp = 0;

	unfollowed = NULL;
	rc = count = 0;

	/* If no referrals in array, return */
	if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
		rc = 0;
		goto done;
	}

	/* Check for hop limit exceeded */
	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
		Debug( LDAP_DEBUG_ANY,
		    "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
		ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
		rc = -1;
		goto done;
	}

	/* find original request */
	for ( origreq = lr;
		origreq->lr_parent != NULL;
		origreq = origreq->lr_parent )
	{
		/* empty */ ;
	}

	refarray = refs;
	refs = NULL;

	/* parse out & follow referrals */
	/* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
	i = -1;
	for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
			i != -1;
			nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
	{

		/* Parse the referral URL */
		rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
		if ( rc != LDAP_URL_SUCCESS ) {
			/* ldap_url_parse_ext() returns LDAP_URL_* errors
			 * which do not map on API errors */
			ld->ld_errno = LDAP_PARAM_ERROR;
			rc = -1;
			goto done;
		}

		if( srv->lud_crit_exts ) {
			int ok = 0;
#ifdef HAVE_TLS
			/* If StartTLS is the only critical ext, OK. */
			if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
				ok = 1;
#endif
			if ( !ok ) {
				/* we do not support any other extensions */
				ld->ld_errno = LDAP_NOT_SUPPORTED;
				rc = -1;
				goto done;
			}
		}

		/* check connection for re-bind in progress */
		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
			/* See if we've already requested this DN with this conn */
			LDAPRequest *lp;
			int looped = 0;
			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
			for ( lp = origreq; lp; ) {
				if ( lp->lr_conn == lc
					&& len == lp->lr_dn.bv_len
					&& len
					&& strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
				{
					looped = 1;
					break;
				}
				if ( lp == origreq ) {
					lp = lp->lr_child;
				} else {
					lp = lp->lr_refnext;
				}
			}
			if ( looped ) {
				ldap_free_urllist( srv );
				srv = NULL;
				ld->ld_errno = LDAP_CLIENT_LOOP;
				rc = -1;
				continue;
			}

			if ( lc->lconn_rebind_inprogress ) {
				/* We are already chasing a referral or search reference and a
				 * bind on that connection is in progress.  We must queue
				 * referrals on that connection, so we don't get a request
				 * going out before the bind operation completes. This happens
				 * if two search references come in one behind the other
				 * for the same server with different contexts.
				 */
				Debug( LDAP_DEBUG_TRACE,
					"ldap_chase_v3referrals: queue referral \"%s\"\n",
					refarray[i], 0, 0);
				if( lc->lconn_rebind_queue == NULL ) {
					/* Create a referral list */
					lc->lconn_rebind_queue =
						(char ***) LDAP_MALLOC( sizeof(void *) * 2);

					if( lc->lconn_rebind_queue == NULL) {
						ld->ld_errno = LDAP_NO_MEMORY;
						rc = -1;
						goto done;
					}

					lc->lconn_rebind_queue[0] = refarray;
					lc->lconn_rebind_queue[1] = NULL;
					refarray = NULL;

				} else {
					/* Count how many referral arrays we already have */
					for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
						/* empty */;
					}

					/* Add the new referral to the list */
					lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
						lc->lconn_rebind_queue, sizeof(void *) * (j + 2));

					if( lc->lconn_rebind_queue == NULL ) {
						ld->ld_errno = LDAP_NO_MEMORY;
						rc = -1;
						goto done;
					}
					lc->lconn_rebind_queue[j] = refarray;
					lc->lconn_rebind_queue[j+1] = NULL;
					refarray = NULL;
				}

				/* We have queued the referral/reference, now just return */
				rc = 0;
				*hadrefp = 1;
				count = 1; /* Pretend we already followed referral */
				goto done;
			}
		} 
		/* Re-encode the request with the new starting point of the search.
		 * Note: In the future we also need to replace the filter if one
		 * was provided with the search reference
		 */

		/* For references we don't want old dn if new dn empty */
		if ( sref && srv->lud_dn == NULL ) {
			srv->lud_dn = LDAP_STRDUP( "" );
		}

		LDAP_NEXT_MSGID( ld, id );
		ber = re_encode_request( ld, origreq->lr_ber, id,
			sref, srv, &rinfo.ri_request );

		if( ber == NULL ) {
			ld->ld_errno = LDAP_ENCODING_ERROR;
			rc = -1;
			goto done;
		}

		Debug( LDAP_DEBUG_TRACE,
			"ldap_chase_v3referral: msgid %d, url \"%s\"\n",
			lr->lr_msgid, refarray[i], 0);

		/* Send the new request to the server - may require a bind */
		rinfo.ri_msgid = origreq->lr_origid;
		rinfo.ri_url = refarray[i];
		rc = ldap_send_server_request( ld, ber, id,
			origreq, &srv, NULL, &rinfo, 0, 1 );
		if ( rc < 0 ) {
			/* Failure, try next referral in the list */
			Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", 
				refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
			unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
			ldap_free_urllist( srv );
			srv = NULL;
			ld->ld_errno = LDAP_REFERRAL;
		} else {
			/* Success, no need to try this referral list further */
			rc = 0;
			++count;
			*hadrefp = 1;

			/* check if there is a queue of referrals that came in during bind */
			if ( lc == NULL) {
				lc = find_connection( ld, srv, 1 );
				if ( lc == NULL ) {
					ld->ld_errno = LDAP_OPERATIONS_ERROR;
					rc = -1;
					LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
					goto done;
				}
			}

			if ( lc->lconn_rebind_queue != NULL ) {
				/* Release resources of previous list */
				LDAP_VFREE( refarray );
				refarray = NULL;
				ldap_free_urllist( srv );
				srv = NULL;

				/* Pull entries off end of queue so list always null terminated */
				for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
					;
				refarray = lc->lconn_rebind_queue[j - 1];
				lc->lconn_rebind_queue[j-1] = NULL;
				/* we pulled off last entry from queue, free queue */
				if ( j == 1 ) {
					LDAP_FREE( lc->lconn_rebind_queue );
					lc->lconn_rebind_queue = NULL;
				}
				/* restart the loop the with new referral list */
				i = -1;
				continue;
			}
			break; /* referral followed, break out of for loop */
		}
	} /* end for loop */
done:
	LDAP_VFREE( refarray );
	ldap_free_urllist( srv );
	LDAP_FREE( *errstrp );
	
	if( rc == 0 ) {
		*errstrp = NULL;
		LDAP_FREE( unfollowed );
		return count;
	} else {
		*errstrp = unfollowed;
		return rc;
	}
}
Example #2
0
/*
 * XXX merging of errors in this routine needs to be improved
 * Protected by res_mutex, conn_mutex and req_mutex	(try_read1msg)
 */
int
ldap_chase_referrals( LDAP *ld,
	LDAPRequest *lr,
	char **errstrp,
	int sref,
	int *hadrefp )
{
	int		rc, count, id;
	unsigned	len;
	char		*p, *ref, *unfollowed;
	LDAPRequest	*origreq;
	LDAPURLDesc	*srv;
	BerElement	*ber;
	LDAPreqinfo  rinfo;
	LDAPConn	*lc;

	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
	LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
	Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );

	ld->ld_errno = LDAP_SUCCESS;	/* optimistic */
	*hadrefp = 0;

	if ( *errstrp == NULL ) {
		return( 0 );
	}

	len = strlen( *errstrp );
	for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
		if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
			*p = '\0';
			p += LDAP_REF_STR_LEN;
			break;
		}
	}

	if ( len < LDAP_REF_STR_LEN ) {
		return( 0 );
	}

	if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
		Debug( LDAP_DEBUG_ANY,
		    "more than %d referral hops (dropping)\n",
		    ld->ld_refhoplimit, 0, 0 );
		    /* XXX report as error in ld->ld_errno? */
		    return( 0 );
	}

	/* find original request */
	for ( origreq = lr; origreq->lr_parent != NULL;
	     origreq = origreq->lr_parent ) {
		/* empty */;
	}

	unfollowed = NULL;
	rc = count = 0;

	/* parse out & follow referrals */
	for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
		p = strchr( ref, '\n' );
		if ( p != NULL ) {
			*p++ = '\0';
		}

		rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
		if ( rc != LDAP_URL_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE,
				"ignoring %s referral <%s>\n",
				ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 );
			rc = ldap_append_referral( ld, &unfollowed, ref );
			*hadrefp = 1;
			continue;
		}

		Debug( LDAP_DEBUG_TRACE,
		    "chasing LDAP referral: <%s>\n", ref, 0, 0 );

		*hadrefp = 1;

		/* See if we've already been here */
		if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
			LDAPRequest *lp;
			int looped = 0;
			ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
			for ( lp = lr; lp; lp = lp->lr_parent ) {
				if ( lp->lr_conn == lc
					&& len == lp->lr_dn.bv_len )
				{
					if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
							continue;
					looped = 1;
					break;
				}
			}
			if ( looped ) {
				ldap_free_urllist( srv );
				ld->ld_errno = LDAP_CLIENT_LOOP;
				rc = -1;
				continue;
			}
		}

		LDAP_NEXT_MSGID( ld, id );
		ber = re_encode_request( ld, origreq->lr_ber,
		    id, sref, srv, &rinfo.ri_request );

		if ( ber == NULL ) {
			return -1 ;
		}

		/* copy the complete referral for rebind process */
		rinfo.ri_url = LDAP_STRDUP( ref );

		rinfo.ri_msgid = origreq->lr_origid;

		rc = ldap_send_server_request( ld, ber, id,
			lr, &srv, NULL, &rinfo, 0, 1 );
		LDAP_FREE( rinfo.ri_url );

		if( rc >= 0 ) {
			++count;
		} else {
			Debug( LDAP_DEBUG_ANY,
				"Unable to chase referral \"%s\" (%d: %s)\n", 
				ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
			rc = ldap_append_referral( ld, &unfollowed, ref );
		}

		ldap_free_urllist(srv);
	}

	LDAP_FREE( *errstrp );
	*errstrp = unfollowed;

	return(( rc == 0 ) ? count : rc );
}
Example #3
0
/*
 * returns an LDAP error code
 *
 * XXXmcs: this function used to have #ifdef LDAP_DNS code in it but I
 *	removed it when I improved the parsing (we don't define LDAP_DNS
 *	here at Netscape).
 */
static int
chase_one_referral( LDAP *ld, LDAPRequest *lr, LDAPRequest *origreq,
    char *refurl, char *desc, int *unknownp )
{
	int		rc, tmprc, secure, msgid;
	LDAPServer	*srv;
	BerElement	*ber;
	LDAPURLDesc	*ludp;

	*unknownp = 0;
	ludp = NULLLDAPURLDESC;

	if ( nsldapi_url_parse( refurl, &ludp, 0 ) != 0 ) {
		LDAPDebug( LDAP_DEBUG_TRACE,
		    "ignoring unknown %s <%s>\n", desc, refurl, 0 );
		*unknownp = 1;
		rc = LDAP_SUCCESS;
		goto cleanup_and_return;
	}

	secure = (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 );

/* XXXmcs: can't tell if secure is supported by connect callback */
	if ( secure && ld->ld_extconnect_fn == NULL ) {
		LDAPDebug( LDAP_DEBUG_TRACE,
		    "ignoring LDAPS %s <%s>\n", desc, refurl, 0 );
		*unknownp = 1;
		rc = LDAP_SUCCESS;
		goto cleanup_and_return;
	}

	LDAPDebug( LDAP_DEBUG_TRACE, "chasing LDAP%s %s: <%s>\n",
	    secure ? "S" : "", desc, refurl );

	LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
	msgid = ++ld->ld_msgid;
	LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );

	if (( tmprc = re_encode_request( ld, origreq->lr_ber, msgid,
	    ludp, &ber )) != LDAP_SUCCESS ) {
		rc = tmprc;
		goto cleanup_and_return;
	}

	if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer )))
	    == NULL ) {
		ber_free( ber, 1 );
		rc = LDAP_NO_MEMORY;
		goto cleanup_and_return;
	}

	if (ludp->lud_host == NULL && ld->ld_defhost == NULL) {
		srv->lsrv_host = NULL;
	} else {
		if (ludp->lud_host == NULL) {
			srv->lsrv_host =
			    nsldapi_strdup( origreq->lr_conn->lconn_server->lsrv_host );
			LDAPDebug(LDAP_DEBUG_TRACE,
			    "chase_one_referral: using hostname '%s' from original "
			    "request on new request\n",
			    srv->lsrv_host, 0, 0);
		} else {
			srv->lsrv_host = nsldapi_strdup(ludp->lud_host);
			LDAPDebug(LDAP_DEBUG_TRACE,
			    "chase_one_referral: using hostname '%s' as specified "
			    "on new request\n",
			    srv->lsrv_host, 0, 0);
		}

		if (srv->lsrv_host == NULL) {
			NSLDAPI_FREE((char *)srv);
			ber_free(ber, 1);
			rc = LDAP_NO_MEMORY;
			goto cleanup_and_return;
		}
	}

	/*
	 * According to our reading of RFCs 2255 and 1738, the
	 * following algorithm applies:
	 * - no hostport (no host, no port) provided in LDAP URL, use those
	 *   of previous request
	 * - no port but a host, use default LDAP port
	 * - else use given hostport
	 */
	if (ludp->lud_port == 0 && ludp->lud_host == NULL) {
		srv->lsrv_port = origreq->lr_conn->lconn_server->lsrv_port;
		LDAPDebug(LDAP_DEBUG_TRACE,
		    "chase_one_referral: using port (%d) from original "
		    "request on new request\n",
		    srv->lsrv_port, 0, 0);
	} else if (ludp->lud_port == 0 && ludp->lud_host != NULL) {
		srv->lsrv_port = (secure) ? LDAPS_PORT : LDAP_PORT;
		LDAPDebug(LDAP_DEBUG_TRACE,
		    "chase_one_referral: using default port (%d) \n",
		    srv->lsrv_port, 0, 0);
	} else {
		srv->lsrv_port = ludp->lud_port;
		LDAPDebug(LDAP_DEBUG_TRACE,
		    "chase_one_referral: using port (%d) as specified on "
		    "new request\n",
		    srv->lsrv_port, 0, 0);
	}

	if ( secure ) {
		srv->lsrv_options |= LDAP_SRV_OPT_SECURE;
	}

	if ( nsldapi_send_server_request( ld, ber, msgid,
	    lr, srv, NULL, NULL, 1 ) < 0 ) {
		rc = LDAP_GET_LDERRNO( ld, NULL, NULL );
		LDAPDebug( LDAP_DEBUG_ANY, "Unable to chase %s %s (%s)\n",
		    desc, refurl, ldap_err2string( rc ));
	} else {
		rc = LDAP_SUCCESS;
	}

cleanup_and_return:
	if ( ludp != NULLLDAPURLDESC ) {
		ldap_free_urldesc( ludp );
	}

	return( rc );
}