From c6a1f2418612d8ce73173d0ad814a1a4032cafb6 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Tue, 25 Jan 2005 16:11:26 +0000 Subject: [PATCH] add support for Chaining Behavior control (, a work in progress) --- clients/tools/common.c | 111 +++++++++++++++- clients/tools/common.h | 3 + clients/tools/ldapsearch.c | 25 ++-- include/ldap.h | 19 +++ servers/slapd/back-ldap/chain.c | 221 +++++++++++++++++++++++++++++++- 5 files changed, 362 insertions(+), 17 deletions(-) diff --git a/clients/tools/common.c b/clients/tools/common.c index 415be46b9d..583da5a33c 100644 --- a/clients/tools/common.c +++ b/clients/tools/common.c @@ -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[=[,]]\n") +N_(" one of \"chainingPreferred\", \"chainingRequired\",\n") +N_(" \"referralsPreferred\", \"referralsRequired\"\n") +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ N_(" [!]postread[=] (a comma-separated attribute list)\n") N_(" [!]preread[=] (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++; } diff --git a/clients/tools/common.h b/clients/tools/common.h index 3022721d5b..d6903ac9f9 100644 --- a/clients/tools/common.h +++ b/clients/tools/common.h @@ -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; diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c index e2f9944959..c603281d97 100644 --- a/clients/tools/ldapsearch.c +++ b/clients/tools/ldapsearch.c @@ -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; diff --git a/include/ldap.h b/include/ldap.h index b06dd612c3..dda9db93df 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -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 */ +/* ; + * 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 ) */ +#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 diff --git a/servers/slapd/back-ldap/chain.c b/servers/slapd/back-ldap/chain.c index 67927c40d7..59f79f11aa 100644 --- a/servers/slapd/back-ldap/chain.c +++ b/servers/slapd/back-ldap/chain.c @@ -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; -- 2.39.5