/** BEGIN COPYRIGHT BLOCK * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * In addition, as a special exception, Red Hat, Inc. gives You the additional * right to link the code of this Program with code not covered under the GNU * General Public License ("Non-GPL Code") and to distribute linked combinations * including the two, subject to the limitations in this paragraph. Non-GPL Code * permitted under this exception must only link to the code of this Program * through those well defined interfaces identified in the file named EXCEPTION * found in the source code files (the "Approved Interfaces"). The files of * Non-GPL Code may instantiate templates or use macros or inline functions from * the Approved Interfaces without causing the resulting work to be covered by * the GNU General Public License. Only Red Hat, Inc. may make changes or * additions to the list of Approved Interfaces. You must obey the GNU General * Public License in all respects for all of the Program code and other code used * in conjunction with the Program except the Non-GPL Code covered by this * exception. If you modify this file, you may extend this exception to your * version of the file, but you are not obligated to do so. If you do not wish to * provide this exception without modification, you must delete this exception * statement from your version and license this file solely under the GPL without * exception. * * * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ #ifdef HAVE_CONFIG_H # include #endif #include "acl.h" static int __aclinit__RegisterLases(void); static int __aclinit__RegisterAttributes(void); static int __aclinit_handler(Slapi_Entry *e, void *callback_data); /*************************************************************************** * * aclinit_main() * Main routine which is called at the server boot up time. * * 1) Reads all the ACI entries from the database and creates * the ACL list. * 2) Registers all the LASes and the GetAttrs supported by the DS. * 3) Generates anonymous profiles. * 4) Registers proxy control * 5) Creates aclpb pool * * Input: * None. * * Returns: * 0 -- no error * 1 -- Error * * Error Handling: * If any error found during the ACL generation, error is logged. * **************************************************************************/ static int acl_initialized = 0; int aclinit_main() { Slapi_PBlock *pb; int rv; Slapi_DN *sdn; void *node; if (acl_initialized) { /* There is no need to do anything more */ return 0; } /* Initialize the LIBACCESS ACL library */ if (ACL_Init() != 0) { slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "ACL Library Initialization failed\n"); return 1; } /* register all the LASes supported by the DS */ if (ACL_ERR == __aclinit__RegisterLases()) { /* Error is already logged */ return 1; } /* Register all the Attrs */ if (ACL_ERR == __aclinit__RegisterAttributes()) { /* Error is already logged */ return 1; } /* * Register to get backend state changes so we can add/remove * acis from backends that come up and go down. */ slapi_register_backend_state_change((void *) NULL, acl_be_state_change_fnc); /* register the extensions */ /* ONREPL Moved to the acl_init function because extensions need to be registered before any operations are issued if ( 0 != acl_init_ext() ) { slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Unable to initialize the extensions\n"); return 1; } */ /* create the mutex array */ if ( 0 != aclext_alloc_lockarray ( ) ) { slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Unable to create the mutext array\n"); return 1; } /* Allocate the pool */ if ( 0 != acl_create_aclpb_pool () ) { slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Unable to create the acl private pool\n"); return 1; } /* * Now read all the ACLs from all the backends and put it * in a list */ /* initialize the ACLLIST sub-system */ if ( 0 != (rv = acllist_init ( ))) { slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Unable to initialize the plugin:%d\n", rv ); return 1; } /* Initialize the anonymous profile i.e., generate it */ rv = aclanom_init (); pb = slapi_pblock_new(); /* * search for the aci_attr_type attributes of all entries. * * slapi_get_fist_suffix() and slapi_get_next_suffix() do not return the * rootdse entry so we search for acis in there explicitly here. */ sdn = slapi_sdn_new_ndn_byval(""); slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Searching for all acis(scope base) at suffix ''\n"); aclinit_search_and_update_aci ( 0, /* thisbeonly */ sdn, /* base */ NULL, /* be name*/ LDAP_SCOPE_BASE, ACL_ADD_ACIS, DO_TAKE_ACLCACHE_WRITELOCK); slapi_sdn_free(&sdn); sdn = slapi_get_first_suffix( &node, 1 ); while (sdn) { slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Searching for all acis(scope subtree) at suffix '%s'\n", slapi_sdn_get_dn(sdn) ); aclinit_search_and_update_aci ( 0, /* thisbeonly */ sdn, /* base */ NULL, /* be name*/ LDAP_SCOPE_SUBTREE, ACL_ADD_ACIS, DO_TAKE_ACLCACHE_WRITELOCK); sdn = slapi_get_next_suffix( &node, 1 ); } /* Initialize it. */ acl_initialized = 1; /* generate the signatures */ acl_set_aclsignature ( aclutil_gen_signature ( 100 ) ); /* Initialize the user-group cache */ rv = aclgroup_init ( ); aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK); /* Register both of the proxied authorization controls (version 1 and 2) */ slapi_register_supported_control( LDAP_CONTROL_PROXYAUTH, SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN | SLAPI_OPERATION_EXTENDED ); slapi_register_supported_control( LDAP_CONTROL_PROXIEDAUTH, SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN | SLAPI_OPERATION_EXTENDED ); slapi_pblock_destroy ( pb ); return 0; } /* * This routine is the one that scans for acis and either adds them * to the internal cache (op==ACL_ADD_ACIS) or deletes them * (op==ACL_REMOVE_ACIS). * * If thisbeonly is 0 the search * is conducted on the base with the specifed scope and be_name is ignored. * This is used at startup time where we iterate over all suffixes, searching * for all the acis in the DIT to load the ACL cache. * * If thisbeonly is 1 then then a be_name must be specified. * In this case we will search in that backend ONLY. * This is used in the case where a backend is turned on and off--in this * case we only want to add/remove the acis in that particular backend and * not for example in any backends below that one. */ int aclinit_search_and_update_aci ( int thisbeonly, const Slapi_DN *base, char *be_name, int scope, int op, acl_lock_flag_t lock_flag ) { char *attrs[2] = { "aci", NULL }; /* Tell __aclinit_handler whether it's an add or a delete */ Slapi_PBlock *aPb; LDAPControl **ctrls=NULL; struct berval *bval; aclinit_handler_callback_data_t call_back_data; PR_ASSERT( lock_flag == DONT_TAKE_ACLCACHE_WRITELOCK || lock_flag == DO_TAKE_ACLCACHE_WRITELOCK); if ( thisbeonly && be_name == NULL) { slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Error: This be_name must be specified.\n"); return -1; } /* * We need to explicitly request (objectclass=ldapsubentry) * in order to get all the subentry acis too. * Note that subentries can be added under subentries (although its not * recommended) so that * there may be non-trivial acis under a subentry. */ /* Use new search internal API */ /* and never retrieve aci from a remote server */ aPb = slapi_pblock_new (); /* * Set up the control to say "Only get acis from this Backend-- * there may be more backends under this one. */ if ( thisbeonly ) { bval = (struct berval *)slapi_ch_malloc(sizeof(struct berval)); bval->bv_len = strlen(be_name) + 1; bval->bv_val = slapi_ch_strdup(be_name); ctrls = (LDAPControl **)slapi_ch_calloc( 2, sizeof(LDAPControl *)); ctrls[0] = NULL; ctrls[1] = NULL; slapi_build_control_from_berval( MTN_CONTROL_USE_ONE_BACKEND_OID, bval, 1 /* is critical */, ctrls); } slapi_search_internal_set_pb ( aPb, slapi_sdn_get_dn(base), scope, "(|(aci=*)(objectclass=ldapsubentry))", attrs, 0 /* attrsonly */, ctrls /* controls: SLAPI_ARGCONTROLS */, NULL /* uniqueid */, aclplugin_get_identity (ACL_PLUGIN_IDENTITY), SLAPI_OP_FLAG_NEVER_CHAIN /* actions : get local aci only */); if (thisbeonly) { slapi_pblock_set(aPb, SLAPI_REQCONTROLS, ctrls); } call_back_data.op = op; call_back_data.retCode = 0; call_back_data.lock_flag = lock_flag; slapi_search_internal_callback_pb(aPb, &call_back_data /* callback_data */, NULL/* result_callback */, __aclinit_handler, NULL /* referral_callback */); if (thisbeonly) { slapi_ch_free((void **)&bval); } /* * This frees the control oid, the bv_val and the control itself and the * ctrls array mem by caling ldap_controls_free()--so we * don't need to do it ourselves. */ slapi_pblock_destroy (aPb); return call_back_data.retCode; } /*************************************************************************** * * __aclinit_handler * * For each entry, finds if there is any ACL in that entry. If there is * then the ACL is processed and stored in the ACL LIST. * * * Input: * * * Returns: * None. * * Error Handling: * If any error found during the ACL generation, the ACL is * logged. Also, set in the callback_data so that caller can act upon it. * **************************************************************************/ static int __aclinit_handler ( Slapi_Entry *e, void *callback_data) { Slapi_Attr *attr; aclinit_handler_callback_data_t *call_back_data = (aclinit_handler_callback_data_t*)callback_data; Slapi_DN *e_sdn; int rv; Slapi_Value *sval=NULL; call_back_data->retCode = 0; /* assume success--if there's an error we overwrite it */ if (e != NULL) { e_sdn = slapi_entry_get_sdn ( e ); /* * Take the write lock around all the mods--so that * other operations will see the acicache either before the whole mod * or after but not, as it was before, during the mod. * This is in line with the LDAP concept of the operation * on the whole entry being the atomic unit. * */ if ( call_back_data->op == ACL_ADD_ACIS ) { slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Adding acis for entry '%s'\n", slapi_sdn_get_dn(e_sdn)); slapi_entry_attr_find ( e, aci_attr_type, &attr ); if ( attr ) { const struct berval *attrValue; int i; if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_LOCK(); } i= slapi_attr_first_value ( attr, &sval ); while(i != -1) { attrValue = slapi_value_get_berval(sval); if ( 0 != (rv=acllist_insert_aci_needsLock (e_sdn, attrValue))) { aclutil_print_err(rv, e_sdn, attrValue, NULL); /* We got an error; Log it and then march along */ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Error: This (%s) ACL will not be considered for evaluation" " because of syntax errors.\n", attrValue->bv_val ? attrValue->bv_val: "NULL"); call_back_data->retCode = rv; } i= slapi_attr_next_value( attr, i, &sval ); }/* while */ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_UNLOCK(); } } } else if (call_back_data->op == ACL_REMOVE_ACIS) { /* Here we are deleting the acis. */ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Removing acis\n"); if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_LOCK(); } if ( 0 != (rv=acllist_remove_aci_needsLock(e_sdn, NULL))) { aclutil_print_err(rv, e_sdn, NULL, NULL); /* We got an error; Log it and then march along */ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Error: ACls not deleted from %s\n", slapi_sdn_get_dn(e_sdn)); call_back_data->retCode = rv; } if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_UNLOCK(); } } } /* * If we get here it's success. * The call_back_data->error is the error code that counts as it's the * one that the original caller will see--this routine is called off a callbacl. */ return ACL_FALSE; /* "local" error code--it's 0 */ } /*************************************************************************** * * __acl__RegisterAttributes * * Register all the attributes supported by the DS. * * Input: * None. * * Returns: * ACL_OK - No error * ACL_ERR - in case of errror * * Error Handling: * None. * **************************************************************************/ static int __aclinit__RegisterAttributes(void) { ACLMethod_t methodinfo; NSErr_t errp; int rv; memset (&errp, 0, sizeof(NSErr_t)); rv = ACL_MethodRegister(&errp, DS_METHOD, &methodinfo); if (rv < 0) { acl_print_acllib_err(&errp, NULL); slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to Register the methods\n"); return ACL_ERR; } rv = ACL_MethodSetDefault (&errp, methodinfo); if (rv < 0) { acl_print_acllib_err(&errp, NULL); slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to Set the default method\n"); return ACL_ERR; } rv = ACL_AttrGetterRegister(&errp, ACL_ATTR_IP, DS_LASIpGetter, methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL); if (rv < 0) { acl_print_acllib_err(&errp, NULL); slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to Register Attr ip\n"); return ACL_ERR; } rv = ACL_AttrGetterRegister(&errp, ACL_ATTR_DNS, DS_LASDnsGetter, methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL); if (rv < 0) { acl_print_acllib_err(&errp, NULL); slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to Register Attr dns\n"); return ACL_ERR; } return ACL_OK; } /*************************************************************************** * * __acl__RegisterLases * Register all the LASes supported by the DS. * * The DS doesnot support user/group. We have defined our own LAS * so that we can display/print an error when the LAS is invoked. * Input: * None. * * Returns: * ACL_OK - No error * ACL_ERR - in case of errror * * Error Handling: * None. * **************************************************************************/ static int __aclinit__RegisterLases(void) { if (ACL_LasRegister(NULL, DS_LAS_USER, (LASEvalFunc_t) DS_LASUserEval, (LASFlushFunc_t) NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register USER Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_GROUP, (LASEvalFunc_t) DS_LASGroupEval, (LASFlushFunc_t) NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register GROUP Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_GROUPDN, (LASEvalFunc_t)DS_LASGroupDnEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register GROUPDN Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_ROLEDN, (LASEvalFunc_t)DS_LASRoleDnEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register ROLEDN Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_USERDN, (LASEvalFunc_t)DS_LASUserDnEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register USERDN Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_USERDNATTR, (LASEvalFunc_t)DS_LASUserDnAttrEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register USERDNATTR Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_AUTHMETHOD, (LASEvalFunc_t)DS_LASAuthMethodEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register CLIENTAUTHTYPE Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_GROUPDNATTR, (LASEvalFunc_t)DS_LASGroupDnAttrEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register GROUPDNATTR Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_USERATTR, (LASEvalFunc_t)DS_LASUserAttrEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register USERATTR Las\n"); return ACL_ERR; } if (ACL_LasRegister(NULL, DS_LAS_SSF, (LASEvalFunc_t)DS_LASSSFEval, (LASFlushFunc_t)NULL) < 0) { slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "Unable to register SSF Las\n"); return ACL_ERR; } return ACL_OK; }