Commit c6a1f241 authored by Pierangelo Masarati's avatar Pierangelo Masarati
Browse files

add support for Chaining Behavior control (<draft-sermersheim-ldap-chaining>, a work in progress)

parent f6ed0b01
......@@ -80,6 +80,12 @@ int protocol = -1;
int verbose = 0;
int version = 0;
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
int chaining = 0;
static int chainingResolve = -1;
static int chainingContinuation = -1;
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
/* Set in main() */
char *prog = NULL;
......@@ -107,6 +113,11 @@ N_(" [!]noop\n")
#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
N_(" ppolicy\n")
#endif
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
N_(" [!]chaining[=<resolveBehavior>[,<continuationBehavior>]]\n")
N_(" one of \"chainingPreferred\", \"chainingRequired\",\n")
N_(" \"referralsPreferred\", \"referralsRequired\"\n")
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
N_(" [!]postread[=<attrs>] (a comma-separated attribute list)\n")
N_(" [!]preread[=<attrs>] (a comma-separated attribute list)\n"),
N_(" -f file read operations from `file'\n"),
......@@ -286,6 +297,52 @@ tool_args( int argc, char **argv )
postread = 1 + crit;
postread_attrs = cvalue;
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
} else if ( strcasecmp( control, "chaining" ) == 0 ) {
chaining = 1 + crit;
if ( cvalue != NULL ) {
char *continuation;
continuation = strchr( cvalue, ',' );
if ( continuation ) {
/* FIXME: this makes sense only in searches */
*continuation++ = '\0';
if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
chainingContinuation = LDAP_CHAINING_PREFERRED;
} else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
chainingContinuation = LDAP_CHAINING_REQUIRED;
} else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
chainingContinuation = LDAP_REFERRALS_PREFERRED;
} else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
chainingContinuation = LDAP_REFERRALS_REQUIRED;
} else {
fprintf( stderr,
"chaining behavior control "
"continuation value \"%s\" invalid\n",
continuation );
exit( EXIT_FAILURE );
}
}
if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
chainingResolve = LDAP_CHAINING_PREFERRED;
} else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
chainingResolve = LDAP_CHAINING_REQUIRED;
} else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
chainingResolve = LDAP_REFERRALS_PREFERRED;
} else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
chainingResolve = LDAP_REFERRALS_REQUIRED;
} else {
fprintf( stderr,
"chaining behavior control "
"resolve value \"%s\" invalid\n",
cvalue);
exit( EXIT_FAILURE );
}
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
} else {
fprintf( stderr, "Invalid general control name: %s\n",
control );
......@@ -867,7 +924,7 @@ void
tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
{
int i = 0, j, crit = 0, err;
LDAPControl c[8], **ctrls;
LDAPControl c[9], **ctrls;
ctrls = (LDAPControl**) malloc(sizeof(c) + (count+1)*sizeof(LDAPControl*));
if ( ctrls == NULL ) {
......@@ -915,8 +972,7 @@ tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
if ( manageDSAit ) {
c[i].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
c[i].ldctl_value.bv_val = NULL;
c[i].ldctl_value.bv_len = 0;
BER_BVZERO( &c[i].ldctl_value );
c[i].ldctl_iscritical = manageDSAit > 1;
ctrls[i] = &c[i];
i++;
......@@ -924,8 +980,7 @@ tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
if ( noop ) {
c[i].ldctl_oid = LDAP_CONTROL_NOOP;
c[i].ldctl_value.bv_val = NULL;
c[i].ldctl_value.bv_len = 0;
BER_BVZERO( &c[i].ldctl_value );
c[i].ldctl_iscritical = noop > 1;
ctrls[i] = &c[i];
i++;
......@@ -991,6 +1046,52 @@ tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
if( attrs ) ldap_charray_free( attrs );
}
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
if ( chaining ) {
if ( chainingResolve > -1 ) {
BerElementBuffer berbuf;
BerElement *ber = (BerElement *)&berbuf;
ber_init2( ber, NULL, LBER_USE_DER );
err = ber_printf( ber, "{e" /* } */, chainingResolve );
if ( err == -1 ) {
ber_free( ber, 1 );
fprintf( stderr, _("Chaining behavior control encoding error!\n") );
exit( EXIT_FAILURE );
}
if ( chainingContinuation > -1 ) {
err = ber_printf( ber, "e", chainingContinuation );
if ( err == -1 ) {
ber_free( ber, 1 );
fprintf( stderr, _("Chaining behavior control encoding error!\n") );
exit( EXIT_FAILURE );
}
}
err = ber_printf( ber, /* { */ "N}" );
if ( err == -1 ) {
ber_free( ber, 1 );
fprintf( stderr, _("Chaining behavior control encoding error!\n") );
exit( EXIT_FAILURE );
}
if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
exit( EXIT_FAILURE );
}
} else {
BER_BVZERO( &c[i].ldctl_value );
}
c[i].ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
c[i].ldctl_iscritical = chaining > 1;
ctrls[i] = &c[i];
i++;
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
while ( count-- ) {
ctrls[i++] = extra_c++;
}
......
......@@ -49,6 +49,9 @@ extern int manageDSAit;
extern int noop;
extern int ppolicy;
extern int preread, postread;
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
extern int chaining;
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
extern int not;
extern int want_bindpw;
......
......@@ -379,7 +379,7 @@ handle_private_option( int i )
if( crit ) subentries *= -1;
#endif
} else if ( strcasecmp( control, "sync" ) == 0 ) {
} else if ( strcasecmp( control, "sync" ) == 0 ) {
char *cookiep;
char *slimitp;
if ( ldapsync ) {
......@@ -664,31 +664,34 @@ getNextPage:
#ifdef LDAP_CONTROL_PAGEDRESULTS
|| pagedResults
#endif
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
|| chaining
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
|| ldapsync
|| subentries || valuesReturnFilter )
{
int err;
int i=0;
LDAPControl c[6];
LDAPControl c[10];
#ifdef LDAP_CONTROL_X_DOMAIN_SCOPE
if ( domainScope ) {
c[i].ldctl_oid = LDAP_CONTROL_X_DOMAIN_SCOPE;
c[i].ldctl_value.bv_val = NULL;
c[i].ldctl_value.bv_len = 0;
c[i].ldctl_iscritical = domainScope > 1;
i++;
}
if ( domainScope ) {
c[i].ldctl_oid = LDAP_CONTROL_X_DOMAIN_SCOPE;
c[i].ldctl_value.bv_val = NULL;
c[i].ldctl_value.bv_len = 0;
c[i].ldctl_iscritical = domainScope > 1;
i++;
}
#endif
#ifdef LDAP_CONTROL_SUBENTRIES
if ( subentries ) {
if (( seber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
if (( seber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
return EXIT_FAILURE;
}
err = ber_printf( seber, "b", abs(subentries) == 1 ? 0 : 1 );
if ( err == -1 ) {
if ( err == -1 ) {
ber_free( seber, 1 );
fprintf( stderr, _("Subentries control encoding error!\n") );
return EXIT_FAILURE;
......
......@@ -277,6 +277,18 @@ typedef struct ldapcontrol {
#define LDAP_SEARCH_FLAG_DOMAIN_SCOPE 1 /* do not generate referrals */
#define LDAP_SEARCH_FLAG_PHANTOM_ROOT 2 /* search all NCs subordinate to base */
/* LDAP Chaining Behavior Control *//* work in progress */
/* <draft-sermersheim-ldap-chaining>;
* see also LDAP_REQUIRES_CHAINING, LDAP_CANNOT_CHAIN */
#ifdef LDAP_DEVEL
#define LDAP_CONTROL_X_CHAINING_BEHAVIOR "1.3.6.1.4.1.4203.666.11.3"
#define LDAP_CHAINING_PREFERRED 0
#define LDAP_CHAINING_REQUIRED 1
#define LDAP_REFERRALS_PREFERRED 2
#define LDAP_REFERRALS_REQUIRED 3
#endif
/* LDAP Unsolicited Notifications */
#define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036" /* RFC 2251 */
#define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION
......@@ -550,6 +562,13 @@ typedef struct ldapcontrol {
/* for the Assertion control */
#define LDAP_ASSERTION_FAILED 0x410f
/* for the Chaining Behavior control (consecutive result codes requested;
* see <draft-sermersheim-ldap-chaining> ) */
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
#define LDAP_REQUIRES_CHAINING 0x4110
#define LDAP_CANNOT_CHAIN 0x4111
#endif
/* API Error Codes
*
* Based on draft-ietf-ldap-c-api-xx
......
......@@ -29,7 +29,31 @@
#include "slap.h"
#include "back-ldap.h"
static BackendInfo *lback;
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
#define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
#define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_DEFAULT SLAP_CH_RESOLVE_CHAINING_PREFERRED
#define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
#define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_DEFAULT SLAP_CH_CONTINUATION_CHAINING_PREFERRED
#define o_chaining o_ctrlflag[sc_chainingBehavior]
#define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
#define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
#define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
#define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
static int sc_chainingBehavior;
static BackendInfo *lback;
static int
ldap_chain_operational( Operation *op, SlapReply *rs )
......@@ -204,10 +228,44 @@ ldap_chain_response( Operation *op, SlapReply *rs )
struct ldapinfo li, *lip = (struct ldapinfo *)on->on_bi.bi_private;
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
int sr_err = rs->sr_err;
slap_reply_t sr_type = rs->sr_type;
slap_mask_t chain_mask = 0;
ber_len_t chain_shift = 0;
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
return SLAP_CB_CONTINUE;
}
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
switch ( get_resolveBehavior( op ) ) {
case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
return SLAP_CB_CONTINUE;
default:
chain_mask = SLAP_CH_RESOLVE_MASK;
chain_shift = SLAP_CH_RESOLVE_SHIFT;
break;
}
} else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
switch ( get_continuationBehavior( op ) ) {
case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
return SLAP_CB_CONTINUE;
default:
chain_mask = SLAP_CH_CONTINUATION_MASK;
chain_shift = SLAP_CH_CONTINUATION_SHIFT;
break;
}
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
/*
* TODO: add checks on who/when chain operations; e.g.:
* a) what identities are authorized
......@@ -393,12 +451,33 @@ ldap_chain_response( Operation *op, SlapReply *rs )
break;
}
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
if ( rc != LDAP_SUCCESS ) {
switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
case LDAP_CHAINING_REQUIRED:
op->o_callback = NULL;
send_ldap_error( op, rs, LDAP_CANNOT_CHAIN, "operation cannot be completed without chaining" );
break;
default:
rc = SLAP_CB_CONTINUE;
rs->sr_err = sr_err;
rs->sr_type = sr_type;
break;
}
goto dont_chain;
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
if ( sc2.sc_private == NULL ) {
op->o_callback = NULL;
rc = rs->sr_err = slap_map_api2result( rs );
send_ldap_result( op, rs );
}
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
dont_chain:;
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
op->o_do_not_cache = cache;
op->o_bd->be_private = private;
op->o_callback = sc;
......@@ -476,11 +555,151 @@ ldap_chain_db_destroy(
return rc;
}
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
static int
ldap_chain_parse_ctrl(
Operation *op,
SlapReply *rs,
LDAPControl *ctrl )
{
ber_tag_t tag;
BerElement *ber;
ber_int_t mode,
behavior;
if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
rs->sr_text = "Chaining behavior control specified multiple times";
return LDAP_PROTOCOL_ERROR;
}
if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
rs->sr_text = "Chaining behavior control specified with pagedResults control";
return LDAP_PROTOCOL_ERROR;
}
if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
} else {
ber_len_t len;
/* Parse the control value
* ChainingBehavior ::= SEQUENCE {
* resolveBehavior Behavior OPTIONAL,
* continuationBehavior Behavior OPTIONAL }
*
* Behavior :: = ENUMERATED {
* chainingPreferred (0),
* chainingRequired (1),
* referralsPreferred (2),
* referralsRequired (3) }
*/
ber = ber_init( &ctrl->ldctl_value );
if( ber == NULL ) {
rs->sr_text = "internal error";
return LDAP_OTHER;
}
tag = ber_scanf( ber, "{e" /* } */, &behavior );
/* FIXME: since the whole SEQUENCE is optional,
* should we accept no enumerations at all? */
if ( tag != LBER_ENUMERATED ) {
rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
return LDAP_PROTOCOL_ERROR;
}
switch ( behavior ) {
case LDAP_CHAINING_PREFERRED:
mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
break;
case LDAP_CHAINING_REQUIRED:
mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
break;
case LDAP_REFERRALS_PREFERRED:
mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
break;
case LDAP_REFERRALS_REQUIRED:
mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
break;
default:
rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
return LDAP_PROTOCOL_ERROR;
}
tag = ber_peek_tag( ber, &len );
if ( tag == LBER_ENUMERATED ) {
tag = ber_scanf( ber, "e", &behavior );
if ( tag == LBER_ERROR ) {
rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
return LDAP_PROTOCOL_ERROR;
}
}
if ( tag == LBER_DEFAULT ) {
mode |= SLAP_CH_CONTINUATION_DEFAULT;
} else {
switch ( behavior ) {
case LDAP_CHAINING_PREFERRED:
mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
break;
case LDAP_CHAINING_REQUIRED:
mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
break;
case LDAP_REFERRALS_PREFERRED:
mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
break;
case LDAP_REFERRALS_REQUIRED:
mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
break;
default:
rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
return LDAP_PROTOCOL_ERROR;
}
}
if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
rs->sr_text = "Chaining behavior control: decoding error";
return LDAP_PROTOCOL_ERROR;
}
(void) ber_free( ber, 1 );
}
op->o_chaining = mode | ( ctrl->ldctl_iscritical
? SLAP_CONTROL_CRITICAL
: SLAP_CONTROL_NONCRITICAL );
return LDAP_SUCCESS;
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
static slap_overinst ldapchain;
int
chain_init( void )
{
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
int rc;
rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
SLAP_CTRL_ACCESS, NULL,
ldap_chain_parse_ctrl, &sc_chainingBehavior );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "Failed to register chaining behavior control: %d\n", rc );
return rc;
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
ldapchain.on_bi.bi_type = "chain";
ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment