]> git.sur5r.net Git - openldap/commitdiff
add support for Chaining Behavior control (<draft-sermersheim-ldap-chaining>, a work...
authorPierangelo Masarati <ando@openldap.org>
Tue, 25 Jan 2005 16:11:26 +0000 (16:11 +0000)
committerPierangelo Masarati <ando@openldap.org>
Tue, 25 Jan 2005 16:11:26 +0000 (16:11 +0000)
clients/tools/common.c
clients/tools/common.h
clients/tools/ldapsearch.c
include/ldap.h
servers/slapd/back-ldap/chain.c

index 415be46b9dc7bcfd6b1180a90a5d8e1d9cf6445c..583da5a33c6116f0a619d876b0069e9585bbfa90 100644 (file)
@@ -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++;
        }
index 3022721d5bc99518b54ebc76ab2f7e0935324d07..d6903ac9f9a6dd6cfa64dec0533a54ff3b7bfccc 100644 (file)
@@ -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;
index e2f99449591a1261bba2f79a951a703758e750cc..c603281d970e5f7209f065ee701c03dbfa67a36a 100644 (file)
@@ -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;
index b06dd612c3fda8a3e04e1f79772e64d80cec4d68..dda9db93df3b3faae62549d1c85dd836eea8b978 100644 (file)
@@ -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
index 67927c40d767c0c98eb6d788c79ee3af13abe3bf..59f79f11aa69c50f995e7bcc93ff0d8cfe04498e 100644 (file)
 #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;