Commit fe242e3c authored by Kurt Zeilenga's avatar Kurt Zeilenga
Browse files

LDAPv3 Referrals (using old style referral entries) initial commit.

Have not completed work on backends other than LDBM.
All send_() routines have new parameters (which will likely change
some more to support responses with controls).
slapd/back-ldbm only compiles.  NOT TESTED.  This is a work in progress.
parent 59a66633
This diff is collapsed.
......@@ -12,24 +12,23 @@
* is provided ``as is'' without express or implied warranty.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "slap.h"
#include "portable.h"
extern Backend *select_backend();
#include <stdio.h>
#include <ac/socket.h>
extern char *default_referral;
#include "slap.h"
void
int
do_abandon(
Connection *conn,
Operation *op
)
{
int id;
Backend *be;
ber_int_t id;
Operation *o;
Operation **oo;
int rc, notfound;
Debug( LDAP_DEBUG_TRACE, "do_abandon\n", 0, 0, 0 );
......@@ -40,31 +39,63 @@ do_abandon(
*/
if ( ber_scanf( op->o_ber, "i", &id ) == LBER_ERROR ) {
Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0 ,0 );
return;
Debug( LDAP_DEBUG_ANY, "do_abandon: ber_scanf failed\n", 0, 0 ,0 );
send_ldap_disconnect( conn, op,
LDAP_PROTOCOL_ERROR, "decoding error" );
return -1;
}
if( (rc = get_ctrls( conn, op, 0 )) != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "do_abandon: get_ctrls failed\n", 0, 0 ,0 );
return rc;
}
Debug( LDAP_DEBUG_ARGS, "do_abandon: id %d\n", id, 0 ,0 );
if( id <= 0 ) {
Debug( LDAP_DEBUG_ANY,
"do_abandon: bad msgid %ld\n", (long) id, 0, 0 );
return LDAP_SUCCESS;
}
notfound = 1; /* not found */
ldap_pvt_thread_mutex_lock( &conn->c_mutex );
/*
* find the operation being abandoned and set the o_abandon
* flag. It's up to the backend to periodically check this
* flag and abort the operation at a convenient time.
*/
pthread_mutex_lock( &conn->c_opsmutex );
for ( o = conn->c_ops; o != NULL; o = o->o_next ) {
if ( o->o_msgid == id )
break;
if ( o->o_msgid == id ) {
ldap_pvt_thread_mutex_lock( &o->o_abandonmutex );
o->o_abandon = 1;
ldap_pvt_thread_mutex_unlock( &o->o_abandonmutex );
notfound = 0;
goto done;
}
}
for ( oo = &conn->c_pending_ops;
(*oo != NULL) && ((*oo)->o_msgid != id);
oo = &(*oo)->o_next )
{
/* EMPTY */ ;
}
if ( o != NULL ) {
pthread_mutex_lock( &o->o_abandonmutex );
o->o_abandon = 1;
pthread_mutex_unlock( &o->o_abandonmutex );
} else {
Debug( LDAP_DEBUG_TRACE, "do_abandon: op not found\n", 0, 0,
0 );
if( *oo != NULL ) {
o = *oo;
*oo = (*oo)->o_next;
slap_op_free( o );
notfound = 0;
}
pthread_mutex_unlock( &conn->c_opsmutex );
done:
ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
Debug( LDAP_DEBUG_TRACE, "do_abandon: op=%ld %sfound\n",
id, notfound ? "not " : "", 0 );
return LDAP_SUCCESS;
}
......@@ -10,36 +10,38 @@
* is provided ``as is'' without express or implied warranty.
*/
#include "portable.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "slap.h"
extern Backend *select_backend();
extern char *dn_normalize();
#include <ac/string.h>
#include <ac/time.h>
#include <ac/socket.h>
extern char *default_referral;
extern time_t currenttime;
extern pthread_mutex_t currenttime_mutex;
extern int global_lastmod;
#include "slap.h"
static void add_created_attrs();
static void add_created_attrs(Operation *op, Entry *e);
void
do_add( conn, op )
Connection *conn;
Operation *op;
int
do_add( Connection *conn, Operation *op )
{
BerElement *ber = op->o_ber;
char *dn, *last;
unsigned long len, tag;
ber_len_t len;
ber_tag_t tag;
Entry *e;
Backend *be;
int rc = LDAP_SUCCESS;
Debug( LDAP_DEBUG_TRACE, "do_add\n", 0, 0, 0 );
if( op->o_bind_in_progress ) {
Debug( LDAP_DEBUG_ANY, "do_add: SASL bind in progress.\n", 0, 0, 0 );
send_ldap_result( conn, op, LDAP_SASL_BIND_IN_PROGRESS, NULL,
"SASL bind in progress", NULL );
return LDAP_SASL_BIND_IN_PROGRESS;
}
/*
* Parse the add request. It looks like this:
*
......@@ -52,18 +54,23 @@ do_add( conn, op )
* }
*/
e = (Entry *) ch_calloc( 1, sizeof(Entry) );
/* get the name */
if ( ber_scanf( ber, "{a", &dn ) == LBER_ERROR ) {
Debug( LDAP_DEBUG_ANY, "ber_scanf failed\n", 0, 0, 0 );
send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
"decoding error" );
return;
if ( ber_scanf( ber, "{a", /*}*/ &dn ) == LBER_ERROR ) {
Debug( LDAP_DEBUG_ANY, "do_add: ber_scanf failed\n", 0, 0, 0 );
send_ldap_disconnect( conn, op,
LDAP_PROTOCOL_ERROR, "decoding error" );
return -1;
}
e = (Entry *) ch_calloc( 1, sizeof(Entry) );
e->e_dn = dn;
dn = dn_normalize( strdup( dn ) );
Debug( LDAP_DEBUG_ARGS, " do_add: dn (%s)\n", dn, 0, 0 );
e->e_ndn = dn_normalize_case( ch_strdup( dn ) );
e->e_private = NULL;
dn = NULL;
Debug( LDAP_DEBUG_ARGS, " do_add: ndn (%s)\n", e->e_ndn, 0, 0 );
/* get the attrs */
e->e_attrs = NULL;
......@@ -73,19 +80,20 @@ do_add( conn, op )
struct berval **vals;
if ( ber_scanf( ber, "{a{V}}", &type, &vals ) == LBER_ERROR ) {
send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
NULL, "decoding error" );
send_ldap_disconnect( conn, op,
LDAP_PROTOCOL_ERROR, "decoding error" );
entry_free( e );
return;
return -1;
}
if ( vals == NULL ) {
Debug( LDAP_DEBUG_ANY, "no values for type %s\n", type,
0, 0 );
send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, NULL,
NULL );
send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
NULL, "no values for type", NULL );
free( type );
entry_free( e );
return;
return LDAP_PROTOCOL_ERROR;
}
attr_merge( e, type, vals );
......@@ -94,19 +102,34 @@ do_add( conn, op )
ber_bvecfree( vals );
}
if ( ber_scanf( ber, /*{*/ "}") == LBER_ERROR ) {
entry_free( e );
Debug( LDAP_DEBUG_ANY, "do_add: ber_scanf failed\n", 0, 0, 0 );
send_ldap_disconnect( conn, op,
LDAP_PROTOCOL_ERROR, "decoding error" );
return -1;
}
if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
entry_free( e );
Debug( LDAP_DEBUG_ANY, "do_add: get_ctrls failed\n", 0, 0, 0 );
return rc;
}
Statslog( LDAP_DEBUG_STATS, "conn=%d op=%d ADD dn=\"%s\"\n",
conn->c_connid, op->o_opid, dn, 0, 0 );
conn->c_connid, op->o_opid, e->e_ndn, 0, 0 );
/*
* We could be serving multiple database backends. Select the
* appropriate one, or send a referral to our "referral server"
* if we don't hold it.
*/
if ( (be = select_backend( dn )) == NULL ) {
be = select_backend( e->e_ndn );
if ( be == NULL ) {
entry_free( e );
send_ldap_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
default_referral );
return;
send_ldap_result( conn, op, LDAP_REFERRAL, NULL,
NULL, default_referral );
return rc;
}
/*
......@@ -115,38 +138,46 @@ do_add( conn, op )
* 2) this backend is master for what it holds;
* 3) it's a replica and the dn supplied is the updatedn.
*/
if ( be->be_add != NULL ) {
if ( be->be_add ) {
/* do the update here */
if ( be->be_updatedn == NULL || strcasecmp( be->be_updatedn,
op->o_dn ) == 0 ) {
if ( (be->be_lastmod == ON || be->be_lastmod == 0 &&
global_lastmod == ON) && be->be_updatedn == NULL ) {
if ( be->be_update_ndn == NULL ||
strcmp( be->be_update_ndn, op->o_ndn ) == 0 )
{
if ( (be->be_lastmod == ON || (be->be_lastmod == UNDEFINED &&
global_lastmod == ON)) && be->be_update_ndn == NULL ) {
add_created_attrs( op, e );
}
if ( (*be->be_add)( be, conn, op, e ) == 0 ) {
replog( be, LDAP_REQ_ADD, e->e_dn, e, 0 );
be_entry_release_w( be, e );
}
} else {
entry_free( e );
send_ldap_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
default_referral );
send_ldap_result( conn, op, rc = LDAP_REFERRAL, NULL,
NULL, default_referral );
}
} else {
Debug( LDAP_DEBUG_ARGS, " do_add: HHH\n", 0, 0, 0 );
entry_free( e );
send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, NULL,
"Function not implemented" );
send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
NULL, "Function not implemented", NULL );
}
return rc;
}
static void
add_created_attrs( Operation *op, Entry *e )
{
char buf[20];
char buf[22];
struct berval bv;
struct berval *bvals[2];
Attribute **a, **next;
Attribute *tmp;
struct tm *ltm;
time_t currenttime;
Debug( LDAP_DEBUG_TRACE, "add_created_attrs\n", 0, 0, 0 );
......@@ -155,8 +186,7 @@ add_created_attrs( Operation *op, Entry *e )
/* remove any attempts by the user to add these attrs */
for ( a = &e->e_attrs; *a != NULL; a = next ) {
if ( strcasecmp( (*a)->a_type, "createtimestamp" ) == 0
|| strcasecmp( (*a)->a_type, "creatorsname" ) == 0 ) {
if ( oc_check_no_usermod_attr( (*a)->a_type ) ) {
tmp = *a;
*a = (*a)->a_next;
attr_free( tmp );
......@@ -167,7 +197,7 @@ add_created_attrs( Operation *op, Entry *e )
}
if ( op->o_dn == NULL || op->o_dn[0] == '\0' ) {
bv.bv_val = "NULLDN";
bv.bv_val = "<anonymous>";
bv.bv_len = strlen( bv.bv_val );
} else {
bv.bv_val = op->o_dn;
......@@ -175,10 +205,16 @@ add_created_attrs( Operation *op, Entry *e )
}
attr_merge( e, "creatorsname", bvals );
pthread_mutex_lock( &currenttime_mutex );
ltm = localtime( &currenttime );
strftime( buf, sizeof(buf), "%y%m%d%H%M%SZ", ltm );
pthread_mutex_unlock( &currenttime_mutex );
currenttime = slap_get_time();
ldap_pvt_thread_mutex_lock( &gmtime_mutex );
#ifndef LDAP_LOCALTIME
ltm = gmtime( &currenttime );
strftime( buf, sizeof(buf), "%Y%m%d%H%M%SZ", ltm );
#else
ltm = localtime( &currenttime );
strftime( buf, sizeof(buf), "%y%m%d%H%M%SZ", ltm );
#endif
ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
bv.bv_val = buf;
bv.bv_len = strlen( bv.bv_val );
......
/* search.c - bdb2 backend search function */
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/socket.h>
#include <ac/time.h>
#include "slap.h"
#include "back-bdb2.h"
#include "proto-back-bdb2.h"
static ID_BLOCK *base_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
static ID_BLOCK *onelevel_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
static ID_BLOCK *subtree_candidates(BackendDB *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, Entry *e, int *err, int lookupbase);
#define GRABSIZE BUFSIZ
#define MAKE_SPACE( n ) { \
if ( rcur + (n) > rbuf + rmaxsize ) { \
int offset = rcur - rbuf; \
rbuf = ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
rmaxsize += GRABSIZE; \
rcur = rbuf + offset; \
} \
}
static int
bdb2i_back_search_internal(
BackendDB *be,
Connection *conn,
Operation *op,
char *base,
int scope,
int deref,
int slimit,
int tlimit,
Filter *filter,
char *filterstr,
char **attrs,
int attrsonly
)
{
struct ldbminfo *li = (struct ldbminfo *) be->be_private;
int err;
time_t stoptime;
ID_BLOCK *candidates;
ID id;
Entry *e;
Attribute *ref;
char *matched = NULL;
int rmaxsize, nrefs;
char *rbuf, *rcur;
int nentries = 0;
char *realBase;
Debug(LDAP_DEBUG_ARGS, "=> bdb2i_back_search\n", 0, 0, 0);
if ( tlimit == 0 && be_isroot( be, op->o_ndn ) ) {
tlimit = -1; /* allow root to set no limit */
} else {
tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
be->be_timelimit : tlimit;
stoptime = op->o_time + tlimit;
}
if ( slimit == 0 && be_isroot( be, op->o_ndn ) ) {
slimit = -1; /* allow root to set no limit */
} else {
slimit = (slimit > be->be_sizelimit || slimit < 1) ?
be->be_sizelimit : slimit;
}
/*
* check and apply aliasing where the dereferencing applies to
* the subordinates of the base
*/
switch ( deref ) {
case LDAP_DEREF_FINDING:
case LDAP_DEREF_ALWAYS:
realBase = bdb2i_derefDN ( be, conn, op, base );
break;
default:
realBase = ch_strdup(base);
}
(void) dn_normalize_case( realBase );
Debug( LDAP_DEBUG_TRACE, "using base \"%s\"\n",
realBase, 0, 0 );
switch ( scope ) {
case LDAP_SCOPE_BASE:
candidates = base_candidates( be, conn, op, realBase, filter,
attrs, attrsonly, &matched, &err );
break;
case LDAP_SCOPE_ONELEVEL:
candidates = onelevel_candidates( be, conn, op, realBase, filter,
attrs, attrsonly, &matched, &err );
break;
case LDAP_SCOPE_SUBTREE:
candidates = subtree_candidates( be, conn, op, realBase, filter,
attrs, attrsonly, &matched, NULL, &err, 1 );
break;
default:
send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, "",
"Bad scope" );
if( realBase != NULL) {
free( realBase );
}
return( -1 );
}
/* null candidates means we could not find the base object */
if ( candidates == NULL ) {
send_ldap_result( conn, op, err, matched, "" );
if ( matched != NULL ) {
free( matched );
}
if( realBase != NULL) {
free( realBase );
}
return( -1 );
}
if ( matched != NULL ) {
free( matched );
}
rmaxsize = 0;
nrefs = 0;
rbuf = rcur = NULL;
MAKE_SPACE( sizeof("Referral:") + 1 );
strcpy( rbuf, "Referral:" );
rcur = strchr( rbuf, '\0' );
for ( id = bdb2i_idl_firstid( candidates ); id != NOID;
id = bdb2i_idl_nextid( candidates, id ) ) {
/* check for abandon */
ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
if ( op->o_abandon ) {
ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
bdb2i_idl_free( candidates );
free( rbuf );
if( realBase != NULL) {
free( realBase );
}
return( 0 );
}
ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
/* check time limit */
if ( tlimit != -1 && slap_get_time() > stoptime ) {
send_search_result( conn, op,
LDAP_TIMELIMIT_EXCEEDED, NULL, nrefs > 0 ? rbuf :
NULL, nentries );
bdb2i_idl_free( candidates );
free( rbuf );
if( realBase != NULL) {
free( realBase );
}
return( 0 );
}
/* get the entry with reader lock */
if ( (e = bdb2i_id2entry_r( be, id )) == NULL ) {
Debug( LDAP_DEBUG_ARGS, "candidate %ld not found\n",
id, 0, 0 );
continue;
}
/*
* if it's a referral, add it to the list of referrals. only do
* this for subtree searches, and don't check the filter explicitly
* here since it's only a candidate anyway.
*/
if ( scope == LDAP_SCOPE_SUBTREE &&
e->e_ndn != NULL &&
strncmp( e->e_ndn, "REF=", 4 ) == 0 &&
(ref = attr_find( e->e_attrs, "ref" )) != NULL )
{
int i;
if ( ref->a_vals == NULL ) {
Debug( LDAP_DEBUG_ANY, "null ref in (%s)\n",
e->e_dn, 0, 0 );
} else {
if( op->o_protocol < LDAP_VERSION3 ) {
for ( i = 0; ref->a_vals[i] != NULL; i++ ) {
/* referral + newline + null */
MAKE_SPACE( ref->a_vals[i]->bv_len + 2 );
*rcur++ = '\n';
strncpy( rcur, ref->a_vals[i]->bv_val,
ref->a_vals[i]->bv_len );
rcur = rcur + ref->a_vals[i]->bv_len;
*rcur = '\0';
nrefs++;
}
} else {
send_search_reference( conn, op, ref->a_vals );
}
}
/* otherwise it's an entry - see if it matches the filter */
} else {
/* if it matches the filter and scope, send it */
if ( test_filter( be, conn, op, e, filter ) == 0 ) {
int scopeok;
char *dn;
/* check scope */
scopeok = 1;
if ( scope == LDAP_SCOPE_ONELEVEL ) {
if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
(void) dn_normalize_case( dn );
scopeok = (dn == realBase)
? 1
: (strcmp( dn, realBase ) ? 0 : 1 );
free( dn );
} else {
scopeok = (realBase == NULL || *realBase == '\0');
}
} else if ( scope == LDAP_SCOPE_SUBTREE ) {
dn = ch_strdup( e->e_ndn );
scopeok = dn_issuffix( dn, realBase );
free( dn );
}
if ( scopeok ) {
/* check size limit */
if ( --slimit == -1 ) {
bdb2i_cache_return_entry_r( &li->li_cache, e );
send_search_result( conn, op,
LDAP_SIZELIMIT_EXCEEDED, NULL,
nrefs > 0 ? rbuf : NULL, nentries );
bdb2i_idl_free( candidates );
free( rbuf );
if( realBase != NULL) {
free( realBase );
}
return( 0 );
}