/* backover.c - backend overlay routines */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2003-2005 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* Functions to overlay other modules over a backend. */ #include "portable.h" #include #include #include #define SLAPD_TOOLS #include "slap.h" #include "config.h" static slap_overinst *overlays; enum db_which { db_open = 0, db_close, db_destroy, db_last }; static int over_db_func( BackendDB *be, enum db_which which ) { slap_overinfo *oi = be->bd_info->bi_private; slap_overinst *on = oi->oi_list; BackendInfo *bi_orig = be->bd_info; BI_db_open **func; int rc = 0; func = &oi->oi_orig->bi_db_open; if ( func[which] ) { be->bd_info = oi->oi_orig; rc = func[which]( be ); } for (; on && rc == 0; on=on->on_next) { be->bd_info = &on->on_bi; func = &on->on_bi.bi_db_open; if (func[which]) { rc = func[which]( be ); } } be->bd_info = bi_orig; return rc; } static int over_db_config( BackendDB *be, const char *fname, int lineno, int argc, char **argv ) { slap_overinfo *oi = be->bd_info->bi_private; slap_overinst *on = oi->oi_list; BackendInfo *bi_orig = be->bd_info; struct ConfigOCs *be_cf_ocs = be->be_cf_ocs; ConfigArgs ca = {0}; int rc = 0; if ( oi->oi_orig->bi_db_config ) { be->bd_info = oi->oi_orig; be->be_cf_ocs = oi->oi_orig->bi_cf_ocs; rc = oi->oi_orig->bi_db_config( be, fname, lineno, argc, argv ); if ( be->bd_info != oi->oi_orig ) { slap_overinfo *oi2; slap_overinst *on2, **onp; BackendDB be2 = *be; int i; /* a database added an overlay; * work it around... */ assert( overlay_is_over( be ) ); oi2 = ( slap_overinfo * )be->bd_info->bi_private; on2 = oi2->oi_list; /* need to put a uniqueness check here as well; * note that in principle there could be more than * one overlay as a result of multiple calls to * overlay_config() */ be2.bd_info = (BackendInfo *)oi; for ( i = 0, onp = &on2; *onp; i++, onp = &(*onp)->on_next ) { if ( overlay_is_inst( &be2, (*onp)->on_bi.bi_type ) ) { Debug( LDAP_DEBUG_ANY, "over_db_config(): " "warning, freshly added " "overlay #%d \"%s\" is already in list\n", i, (*onp)->on_bi.bi_type, 0 ); /* NOTE: if the overlay already exists, * there is no way to merge the results * of the configuration that may have * occurred during bi_db_config(); we * just issue a warning, and the * administrator should deal with this */ } } *onp = oi->oi_list; oi->oi_list = on2; ch_free( be->bd_info ); } be->bd_info = (BackendInfo *)oi; if ( rc != SLAP_CONF_UNKNOWN ) return rc; } ca.argv = argv; ca.argc = argc; ca.fname = fname; ca.lineno = lineno; ca.be = be; for (; on; on=on->on_next) { rc = SLAP_CONF_UNKNOWN; if (on->on_bi.bi_cf_ocs) { ConfigTable *ct; ca.bi = &on->on_bi; ct = config_find_keyword( on->on_bi.bi_cf_ocs->co_table, &ca ); if ( ct ) { rc = config_add_vals( ct, &ca ); } } if (on->on_bi.bi_db_config && rc == SLAP_CONF_UNKNOWN) { be->bd_info = &on->on_bi; rc = on->on_bi.bi_db_config( be, fname, lineno, argc, argv ); if ( rc != SLAP_CONF_UNKNOWN ) break; } } be->bd_info = bi_orig; be->be_cf_ocs = be_cf_ocs; return rc; } static int over_db_open( BackendDB *be ) { return over_db_func( be, db_open ); } static int over_db_close( BackendDB *be ) { slap_overinfo *oi = be->bd_info->bi_private; slap_overinst *on = oi->oi_list; BackendInfo *bi_orig = be->bd_info; int rc = 0; for (; on && rc == 0; on=on->on_next) { be->bd_info = &on->on_bi; if ( be->bd_info->bi_db_close ) { rc = be->bd_info->bi_db_close( be ); } } if ( oi->oi_orig->bi_db_close ) { be->bd_info = oi->oi_orig; rc = be->bd_info->bi_db_close( be ); } be->bd_info = bi_orig; return rc; } static int over_db_destroy( BackendDB *be ) { slap_overinfo *oi = be->bd_info->bi_private; slap_overinst *on = oi->oi_list, *next; int rc; rc = over_db_func( be, db_destroy ); for (next = on->on_next; on; on=next) { next = on->on_next; free( on ); } free( oi ); return rc; } static int over_back_response ( Operation *op, SlapReply *rs ) { slap_overinfo *oi = op->o_callback->sc_private; slap_overinst *on = oi->oi_list; int rc = SLAP_CB_CONTINUE; BackendDB *be = op->o_bd, db = *op->o_bd; db.be_flags |= SLAP_DBFLAG_OVERLAY; op->o_bd = &db; for (; on; on=on->on_next ) { if ( on->on_response ) { db.bd_info = (BackendInfo *)on; rc = on->on_response( op, rs ); if ( rc != SLAP_CB_CONTINUE ) break; } } op->o_bd = be; return rc; } #ifdef SLAP_OVERLAY_ACCESS static int over_access_allowed( Operation *op, Entry *e, AttributeDescription *desc, struct berval *val, slap_access_t access, AccessControlState *state, slap_mask_t *maskp ) { slap_overinfo *oi; slap_overinst *on; BackendInfo *bi = op->o_bd->bd_info; BackendDB *be = op->o_bd, db; int rc = SLAP_CB_CONTINUE; /* FIXME: used to happen for instance during abandon * when global overlays are used... */ assert( op->o_bd != NULL ); oi = op->o_bd->bd_info->bi_private; on = oi->oi_list; for ( ; on; on = on->on_next ) { if ( on->on_bi.bi_access_allowed ) { /* NOTE: do not copy the structure until required */ if ( !SLAP_ISOVERLAY( op->o_bd ) ) { db = *op->o_bd; db.be_flags |= SLAP_DBFLAG_OVERLAY; op->o_bd = &db; } op->o_bd->bd_info = (BackendInfo *)on; rc = on->on_bi.bi_access_allowed( op, e, desc, val, access, state, maskp ); if ( rc != SLAP_CB_CONTINUE ) break; } } if ( rc == SLAP_CB_CONTINUE ) { BI_access_allowed *bi_access_allowed; /* if the database structure was changed, o_bd points to a * copy of the structure; put the original bd_info in place */ if ( SLAP_ISOVERLAY( op->o_bd ) ) { op->o_bd->bd_info = oi->oi_orig; } if ( oi->oi_orig->bi_access_allowed ) { bi_access_allowed = oi->oi_orig->bi_access_allowed; } else { bi_access_allowed = slap_access_allowed; } rc = bi_access_allowed( op, e, desc, val, access, state, maskp ); } /* should not fall thru this far without anything happening... */ if ( rc == SLAP_CB_CONTINUE ) { /* access not allowed */ rc = 0; } op->o_bd = be; op->o_bd->bd_info = bi; return rc; } static int over_acl_group( Operation *op, Entry *e, struct berval *gr_ndn, struct berval *op_ndn, ObjectClass *group_oc, AttributeDescription *group_at ) { slap_overinfo *oi; slap_overinst *on; BackendInfo *bi = op->o_bd->bd_info; BackendDB *be = op->o_bd, db; int rc = SLAP_CB_CONTINUE; /* FIXME: used to happen for instance during abandon * when global overlays are used... */ assert( op->o_bd != NULL ); oi = op->o_bd->bd_info->bi_private; on = oi->oi_list; for ( ; on; on = on->on_next ) { if ( on->on_bi.bi_acl_group ) { /* NOTE: do not copy the structure until required */ if ( !SLAP_ISOVERLAY( op->o_bd ) ) { db = *op->o_bd; db.be_flags |= SLAP_DBFLAG_OVERLAY; op->o_bd = &db; } op->o_bd->bd_info = (BackendInfo *)on; rc = on->on_bi.bi_acl_group( op, e, gr_ndn, op_ndn, group_oc, group_at ); if ( rc != SLAP_CB_CONTINUE ) break; } } if ( rc == SLAP_CB_CONTINUE ) { BI_acl_group *bi_acl_group; /* if the database structure was changed, o_bd points to a * copy of the structure; put the original bd_info in place */ if ( SLAP_ISOVERLAY( op->o_bd ) ) { op->o_bd->bd_info = oi->oi_orig; } if ( oi->oi_orig->bi_acl_group ) { bi_acl_group = oi->oi_orig->bi_acl_group; } else { bi_acl_group = backend_group; } rc = bi_acl_group( op, e, gr_ndn, op_ndn, group_oc, group_at ); } /* should not fall thru this far without anything happening... */ if ( rc == SLAP_CB_CONTINUE ) { /* access not allowed */ rc = 0; } op->o_bd = be; op->o_bd->bd_info = bi; return rc; } static int over_acl_attribute( Operation *op, Entry *target, struct berval *entry_ndn, AttributeDescription *entry_at, BerVarray *vals, slap_access_t access ) { slap_overinfo *oi; slap_overinst *on; BackendInfo *bi = op->o_bd->bd_info; BackendDB *be = op->o_bd, db; int rc = SLAP_CB_CONTINUE; /* FIXME: used to happen for instance during abandon * when global overlays are used... */ assert( op->o_bd != NULL ); oi = op->o_bd->bd_info->bi_private; on = oi->oi_list; for ( ; on; on = on->on_next ) { if ( on->on_bi.bi_acl_attribute ) { /* NOTE: do not copy the structure until required */ if ( !SLAP_ISOVERLAY( op->o_bd ) ) { db = *op->o_bd; db.be_flags |= SLAP_DBFLAG_OVERLAY; op->o_bd = &db; } op->o_bd->bd_info = (BackendInfo *)on; rc = on->on_bi.bi_acl_attribute( op, target, entry_ndn, entry_at, vals, access ); if ( rc != SLAP_CB_CONTINUE ) break; } } if ( rc == SLAP_CB_CONTINUE ) { BI_acl_attribute *bi_acl_attribute; /* if the database structure was changed, o_bd points to a * copy of the structure; put the original bd_info in place */ if ( SLAP_ISOVERLAY( op->o_bd ) ) { op->o_bd->bd_info = oi->oi_orig; } if ( oi->oi_orig->bi_acl_attribute ) { bi_acl_attribute = oi->oi_orig->bi_acl_attribute; } else { bi_acl_attribute = backend_attribute; } rc = bi_acl_attribute( op, target, entry_ndn, entry_at, vals, access ); } /* should not fall thru this far without anything happening... */ if ( rc == SLAP_CB_CONTINUE ) { /* access not allowed */ rc = 0; } op->o_bd = be; op->o_bd->bd_info = bi; return rc; } #endif /* SLAP_OVERLAY_ACCESS */ /* * default return code in case of missing backend function * and overlay stack returning SLAP_CB_CONTINUE */ static int op_rc[ op_last ] = { LDAP_UNWILLING_TO_PERFORM, /* bind */ LDAP_UNWILLING_TO_PERFORM, /* unbind */ LDAP_UNWILLING_TO_PERFORM, /* search */ SLAP_CB_CONTINUE, /* compare; pass to frontend */ LDAP_UNWILLING_TO_PERFORM, /* modify */ LDAP_UNWILLING_TO_PERFORM, /* modrdn */ LDAP_UNWILLING_TO_PERFORM, /* add */ LDAP_UNWILLING_TO_PERFORM, /* delete */ LDAP_UNWILLING_TO_PERFORM, /* abandon */ LDAP_UNWILLING_TO_PERFORM, /* cancel */ LDAP_UNWILLING_TO_PERFORM, /* extended */ LDAP_SUCCESS, /* aux_operational */ LDAP_SUCCESS, /* aux_chk_referrals */ SLAP_CB_CONTINUE /* aux_chk_controls; pass to frontend */ }; int overlay_op_walk( Operation *op, SlapReply *rs, slap_operation_t which, slap_overinfo *oi, slap_overinst *on ) { BI_op_bind **func; int rc = SLAP_CB_CONTINUE; for (; on; on=on->on_next ) { func = &on->on_bi.bi_op_bind; if ( func[which] ) { op->o_bd->bd_info = (BackendInfo *)on; rc = func[which]( op, rs ); if ( rc != SLAP_CB_CONTINUE ) break; } } func = &oi->oi_orig->bi_op_bind; if ( func[which] && rc == SLAP_CB_CONTINUE ) { op->o_bd->bd_info = oi->oi_orig; rc = func[which]( op, rs ); } /* should not fall thru this far without anything happening... */ if ( rc == SLAP_CB_CONTINUE ) { rc = op_rc[ which ]; } /* The underlying backend didn't handle the request, make sure * overlay cleanup is processed. */ if ( rc == LDAP_UNWILLING_TO_PERFORM ) { slap_callback *sc_next; for ( ; op->o_callback && op->o_callback->sc_response != over_back_response; op->o_callback = sc_next ) { sc_next = op->o_callback->sc_next; if ( op->o_callback->sc_cleanup ) { op->o_callback->sc_cleanup( op, rs ); } } } return rc; } static int over_op_func( Operation *op, SlapReply *rs, slap_operation_t which ) { slap_overinfo *oi; slap_overinst *on; BackendDB *be = op->o_bd, db; slap_callback cb = {NULL, over_back_response, NULL, NULL}; int rc = SLAP_CB_CONTINUE; /* FIXME: used to happen for instance during abandon * when global overlays are used... */ assert( op->o_bd != NULL ); oi = op->o_bd->bd_info->bi_private; on = oi->oi_list; if ( !SLAP_ISOVERLAY( op->o_bd )) { db = *op->o_bd; db.be_flags |= SLAP_DBFLAG_OVERLAY; op->o_bd = &db; } cb.sc_next = op->o_callback; cb.sc_private = oi; op->o_callback = &cb; rc = overlay_op_walk( op, rs, which, oi, on ); op->o_bd = be; op->o_callback = cb.sc_next; return rc; } static int over_op_bind( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_bind ); } static int over_op_unbind( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_unbind ); } static int over_op_search( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_search ); } static int over_op_compare( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_compare ); } static int over_op_modify( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_modify ); } static int over_op_modrdn( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_modrdn ); } static int over_op_add( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_add ); } static int over_op_delete( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_delete ); } static int over_op_abandon( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_abandon ); } static int over_op_cancel( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_cancel ); } static int over_op_extended( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_extended ); } static int over_aux_operational( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_aux_operational ); } static int over_aux_chk_referrals( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_aux_chk_referrals ); } static int over_aux_chk_controls( Operation *op, SlapReply *rs ) { return over_op_func( op, rs, op_aux_chk_controls ); } enum conn_which { conn_init = 0, conn_destroy, conn_last }; static int over_connection_func( BackendDB *bd, Connection *conn, enum conn_which which ) { slap_overinfo *oi; slap_overinst *on; BackendDB db; int rc = SLAP_CB_CONTINUE; BI_connection_init **func; /* FIXME: used to happen for instance during abandon * when global overlays are used... */ assert( bd != NULL ); oi = bd->bd_info->bi_private; on = oi->oi_list; if ( !SLAP_ISOVERLAY( bd ) ) { db = *bd; db.be_flags |= SLAP_DBFLAG_OVERLAY; bd = &db; } for ( ; on; on = on->on_next ) { func = &on->on_bi.bi_connection_init; if ( func[ which ] ) { bd->bd_info = (BackendInfo *)on; rc = func[ which ]( bd, conn ); if ( rc != SLAP_CB_CONTINUE ) break; } } func = &oi->oi_orig->bi_connection_init; if ( func[ which ] && rc == SLAP_CB_CONTINUE ) { bd->bd_info = oi->oi_orig; rc = func[ which ]( bd, conn ); } /* should not fall thru this far without anything happening... */ if ( rc == SLAP_CB_CONTINUE ) { rc = LDAP_UNWILLING_TO_PERFORM; } return rc; } static int over_connection_init( BackendDB *bd, Connection *conn ) { return over_connection_func( bd, conn, conn_init ); } static int over_connection_destroy( BackendDB *bd, Connection *conn ) { return over_connection_func( bd, conn, conn_destroy ); } int overlay_register( slap_overinst *on ) { on->on_next = overlays; overlays = on; return 0; } /* * iterator on registered overlays; overlay_next( NULL ) returns the first * overlay; * subsequent calls with the previously returned value allow to * iterate * over the entire list; returns NULL when no more overlays are * registered. */ slap_overinst * overlay_next( slap_overinst *on ) { if ( on == NULL ) { return overlays; } return on->on_next; } /* * returns a specific registered overlay based on the type; NULL if not * registered. */ slap_overinst * overlay_find( const char *over_type ) { slap_overinst *on = overlays; assert( over_type != NULL ); for ( ; on; on = on->on_next ) { if ( strcmp( on->on_bi.bi_type, over_type ) == 0 ) { break; } } return on; } static const char overtype[] = "over"; /* * returns TRUE (1) if the database is actually an overlay instance; * FALSE (0) otherwise. */ int overlay_is_over( BackendDB *be ) { return be->bd_info->bi_type == overtype; } /* * returns TRUE (1) if the given database is actually an overlay * instance and, somewhere in the list, contains the requested overlay; * FALSE (0) otherwise. */ int overlay_is_inst( BackendDB *be, const char *over_type ) { slap_overinst *on; assert( be != NULL ); if ( !overlay_is_over( be ) ) { return 0; } on = ((slap_overinfo *)be->bd_info->bi_private)->oi_list; for ( ; on; on = on->on_next ) { if ( strcmp( on->on_bi.bi_type, over_type ) == 0 ) { return 1; } } return 0; } int overlay_register_control( BackendDB *be, const char *oid ) { int rc = 0; int gotit = 0; int cid; if ( slap_find_control_id( oid, &cid ) == LDAP_CONTROL_NOT_FOUND ) { return -1; } if ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_GLOBAL_OVERLAY ) { BackendDB *bd; /* add to all backends... */ LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) { if ( be == bd ) { gotit = 1; } bd->be_ctrls[ cid ] = 1; bd->be_ctrls[ SLAP_MAX_CIDS ] = 1; } } if ( rc == 0 && !gotit ) { be->be_ctrls[ cid ] = 1; be->be_ctrls[ SLAP_MAX_CIDS ] = 1; } return rc; } void overlay_destroy_one( BackendDB *be, slap_overinst *on ) { slap_overinfo *oi = on->on_info; slap_overinst **oidx; for ( oidx = &oi->oi_list; *oidx; oidx = &(*oidx)->on_next ) { if ( *oidx == on ) { *oidx = on->on_next; if ( on->on_bi.bi_db_destroy ) { BackendInfo *bi_orig = be->bd_info; be->bd_info = (BackendInfo *)on; on->on_bi.bi_db_destroy( be ); be->bd_info = bi_orig; } free( on ); break; } } } /* add an overlay to a particular backend. */ int overlay_config( BackendDB *be, const char *ov ) { slap_overinst *on = NULL, *on2 = NULL; slap_overinfo *oi = NULL; BackendInfo *bi = NULL; on = overlay_find( ov ); if ( !on ) { Debug( LDAP_DEBUG_ANY, "overlay \"%s\" not found\n", ov, 0, 0 ); return 1; } /* If this is the first overlay on this backend, set up the * overlay info structure */ if ( !overlay_is_over( be ) ) { oi = ch_malloc( sizeof( slap_overinfo ) ); oi->oi_orig = be->bd_info; oi->oi_bi = *be->bd_info; /* NOTE: the first time a global overlay is configured, * frontendDB gets this flag; it is used later by overlays * to determine if they're stacked on top of the frontendDB */ if ( oi->oi_orig == frontendDB->bd_info ) { SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLOBAL_OVERLAY; } /* Save a pointer to ourself in bi_private. */ oi->oi_bi.bi_private = oi; oi->oi_list = NULL; bi = (BackendInfo *)oi; bi->bi_type = (char *)overtype; bi->bi_db_config = over_db_config; bi->bi_db_open = over_db_open; bi->bi_db_close = over_db_close; bi->bi_db_destroy = over_db_destroy; bi->bi_op_bind = over_op_bind; bi->bi_op_unbind = over_op_unbind; bi->bi_op_search = over_op_search; bi->bi_op_compare = over_op_compare; bi->bi_op_modify = over_op_modify; bi->bi_op_modrdn = over_op_modrdn; bi->bi_op_add = over_op_add; bi->bi_op_delete = over_op_delete; bi->bi_op_abandon = over_op_abandon; bi->bi_op_cancel = over_op_cancel; bi->bi_extended = over_op_extended; /* * this is fine because it has the same * args of the operations; we need to rework * all the hooks to share the same args * of the operations... */ bi->bi_operational = over_aux_operational; bi->bi_chk_referrals = over_aux_chk_referrals; bi->bi_chk_controls = over_aux_chk_controls; #ifdef SLAP_OVERLAY_ACCESS /* these have specific arglists */ bi->bi_access_allowed = over_access_allowed; bi->bi_acl_group = over_acl_group; bi->bi_acl_attribute = over_acl_attribute; #endif /* SLAP_OVERLAY_ACCESS */ bi->bi_connection_init = over_connection_init; bi->bi_connection_destroy = over_connection_destroy; be->bd_info = bi; } else { if ( overlay_is_inst( be, ov ) ) { Debug( LDAP_DEBUG_ANY, "overlay_config(): " "warning, overlay \"%s\" " "already in list\n", ov, 0, 0 ); } oi = be->bd_info->bi_private; } /* Insert new overlay on head of list. Overlays are executed * in reverse of config order... */ on2 = ch_calloc( 1, sizeof(slap_overinst) ); *on2 = *on; on2->on_info = oi; on2->on_next = oi->oi_list; oi->oi_list = on2; /* Any initialization needed? */ if ( on->on_bi.bi_db_init ) { int rc; be->bd_info = (BackendInfo *)on2; rc = on2->on_bi.bi_db_init( be ); be->bd_info = (BackendInfo *)oi; if ( rc ) return rc; } return 0; }