| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- /******************************************************************************
- Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- version 2 as published by the Free Software Foundation.
- 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- Contributors:
- Hewlett-Packard Development Company, L.P.
- ******************************************************************************/
- /* Account Policy plugin */
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <time.h>
- #include "slapi-plugin.h"
- #include "acctpolicy.h"
- /*
- * acct_policy_dn_is_config()
- *
- * Checks if dn is a plugin config entry.
- */
- static int
- acct_policy_dn_is_config(Slapi_DN *sdn)
- {
- int ret = 0;
- slapi_log_error(SLAPI_LOG_TRACE, PLUGIN_NAME,
- "--> automember_dn_is_config\n");
- if (sdn == NULL) {
- goto bail;
- }
- /* If an alternate config area is configured, treat it's child
- * entries as config entries. If the alternate config area is
- * not configured, treat children of the top-level plug-in
- * config entry as our config entries. */
- if (acct_policy_get_config_area()) {
- if (slapi_sdn_issuffix(sdn, acct_policy_get_config_area()) &&
- slapi_sdn_compare(sdn, acct_policy_get_config_area())) {
- ret = 1;
- }
- } else {
- if (slapi_sdn_issuffix(sdn, acct_policy_get_plugin_sdn()) &&
- slapi_sdn_compare(sdn, acct_policy_get_plugin_sdn())) {
- ret = 1;
- }
- }
- bail:
- slapi_log_error(SLAPI_LOG_TRACE, PLUGIN_NAME,
- "<-- automember_dn_is_config\n");
- return ret;
- }
- /*
- Checks bind entry for last login state and compares current time with last
- login time plus the limit to decide whether to deny the bind.
- */
- static int
- acct_inact_limit( Slapi_PBlock *pb, const char *dn, Slapi_Entry *target_entry, acctPolicy *policy )
- {
- char *lasttimestr = NULL;
- time_t lim_t, last_t, cur_t;
- int rc = 0; /* Optimistic default */
- acctPluginCfg *cfg;
- config_rd_lock();
- cfg = get_config();
- if( ( lasttimestr = get_attr_string_val( target_entry,
- cfg->state_attr_name ) ) != NULL ) {
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "\"%s\" login timestamp is %s\n", dn, lasttimestr );
- } else if( cfg->alt_state_attr_name && (( lasttimestr = get_attr_string_val( target_entry,
- cfg->alt_state_attr_name ) ) != NULL) ) {
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "\"%s\" alternate timestamp is %s\n", dn, lasttimestr );
- } else {
- /* the primary or alternate attribute might not yet exist eg.
- * if only lastlogintime is specified and it id the first login
- */
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "\"%s\" has no value for stateattr or altstateattr \n", dn );
- goto done;
- }
- last_t = gentimeToEpochtime( lasttimestr );
- cur_t = time( (time_t*)0 );
- lim_t = policy->inactivitylimit;
- /* Finally do the time comparison */
- if( cur_t > last_t + lim_t ) {
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "\"%s\" has exceeded inactivity limit (%ld > (%ld + %ld))\n",
- dn, cur_t, last_t, lim_t );
- rc = 1;
- goto done;
- } else {
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "\"%s\" is within inactivity limit (%ld < (%ld + %ld))\n",
- dn, cur_t, last_t, lim_t );
- }
- done:
- config_unlock();
- /* Deny bind; the account has exceeded the inactivity limit */
- if( rc == 1 ) {
- slapi_send_ldap_result( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
- "Account inactivity limit exceeded."
- " Contact system administrator to reset.", 0, NULL );
- }
- slapi_ch_free_string( &lasttimestr );
- return( rc );
- }
- /*
- This is called after binds, it updates an attribute in the account
- with the current time.
- */
- static int
- acct_record_login( const char *dn )
- {
- int ldrc;
- int rc = 0; /* Optimistic default */
- LDAPMod *mods[2];
- LDAPMod mod;
- struct berval *vals[2];
- struct berval val;
- char *timestr = NULL;
- acctPluginCfg *cfg;
- void *plugin_id;
- Slapi_PBlock *modpb = NULL;
- int skip_mod_attrs = 1; /* value doesn't matter as long as not NULL */
- config_rd_lock();
- cfg = get_config();
- /* if we are not allowed to modify the state attr we're done
- * this could be intentional, so just return
- */
- if (! update_is_allowed_attr(cfg->always_record_login_attr) )
- goto done;
-
- plugin_id = get_identity();
- timestr = epochtimeToGentime( time( (time_t*)0 ) );
- val.bv_val = timestr;
- val.bv_len = strlen( val.bv_val );
- vals [0] = &val;
- vals [1] = NULL;
- mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
- mod.mod_type = cfg->always_record_login_attr;
- mod.mod_bvalues = vals;
- mods[0] = &mod;
- mods[1] = NULL;
- modpb = slapi_pblock_new();
- slapi_modify_internal_set_pb( modpb, dn, mods, NULL, NULL,
- plugin_id, SLAPI_OP_FLAG_NO_ACCESS_CHECK |
- SLAPI_OP_FLAG_BYPASS_REFERRALS );
- slapi_pblock_set( modpb, SLAPI_SKIP_MODIFIED_ATTRS, &skip_mod_attrs );
- slapi_modify_internal_pb( modpb );
- slapi_pblock_get( modpb, SLAPI_PLUGIN_INTOP_RESULT, &ldrc );
- if (ldrc != LDAP_SUCCESS) {
- slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
- "Recording %s=%s failed on \"%s\" err=%d\n", cfg->always_record_login_attr,
- timestr, dn, ldrc );
- rc = -1;
- goto done;
- } else {
- slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
- "Recorded %s=%s on \"%s\"\n", cfg->always_record_login_attr, timestr, dn );
- }
- done:
- config_unlock();
- slapi_pblock_destroy( modpb );
- slapi_ch_free_string( ×tr );
- return( rc );
- }
- /*
- Handles bind preop callbacks
- */
- int
- acct_bind_preop( Slapi_PBlock *pb )
- {
- const char *dn = NULL;
- Slapi_DN *sdn = NULL;
- Slapi_Entry *target_entry = NULL;
- int rc = 0; /* Optimistic default */
- int ldrc;
- acctPolicy *policy = NULL;
- void *plugin_id;
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "=> acct_bind_preop\n" );
- plugin_id = get_identity();
- /* This does not give a copy, so don't free it */
- if( slapi_pblock_get( pb, SLAPI_BIND_TARGET_SDN, &sdn ) != 0 ) {
- slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
- "Error retrieving target DN\n" );
- rc = -1;
- goto done;
- }
- dn = slapi_sdn_get_dn(sdn);
- /* The plugin wouldn't get called for anonymous binds but let's check */
- if ( dn == NULL ) {
- goto done;
- }
- ldrc = slapi_search_internal_get_entry( sdn, NULL, &target_entry,
- plugin_id );
- /* There was a problem retrieving the entry */
- if( ldrc != LDAP_SUCCESS ) {
- if( ldrc != LDAP_NO_SUCH_OBJECT ) {
- /* The problem is not a bad bind or virtual entry; halt bind */
- slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
- "Failed to retrieve entry \"%s\": %d\n", dn, ldrc );
- rc = -1;
- }
- goto done;
- }
- if( get_acctpolicy( pb, target_entry, plugin_id, &policy ) ) {
- slapi_log_error( SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
- "Account Policy object for \"%s\" is missing\n", dn );
- rc = -1;
- goto done;
- }
- /* Null policy means target isnt's under the influence of a policy */
- if( policy == NULL ) {
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "\"%s\" is not governed by an account policy\n", dn);
- goto done;
- }
- /* Check whether the account is in violation of inactivity limit */
- rc = acct_inact_limit( pb, dn, target_entry, policy );
- /* ...Any additional account policy enforcement goes here... */
- done:
- /* Internal error */
- if( rc == -1 ) {
- slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
- }
- slapi_entry_free( target_entry );
- free_acctpolicy( &policy );
- slapi_log_error( SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "<= acct_bind_preop\n" );
- return( rc == 0 ? CALLBACK_OK : CALLBACK_ERR );
- }
- /*
- This is called after binds, it updates an attribute in the entry that the
- bind DN corresponds to with the current time if it has an account policy
- specifier.
- */
- int
- acct_bind_postop( Slapi_PBlock *pb )
- {
- char *dn = NULL;
- int ldrc, tracklogin = 0;
- int rc = 0; /* Optimistic default */
- Slapi_DN *sdn = NULL;
- Slapi_Entry *target_entry = NULL;
- acctPluginCfg *cfg;
- void *plugin_id;
- slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
- "=> acct_bind_postop\n" );
- plugin_id = get_identity();
- /* Retrieving SLAPI_CONN_DN from the pb gives a copy */
- if( slapi_pblock_get( pb, SLAPI_CONN_DN, &dn ) != 0 ) {
- slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
- "Error retrieving bind DN\n" );
- rc = -1;
- goto done;
- }
- /* Client is anonymously bound */
- if( dn == NULL ) {
- goto done;
- }
- config_rd_lock();
- cfg = get_config();
- tracklogin = cfg->always_record_login;
- /* We're not always tracking logins, so check whether the entry is
- covered by an account policy to decide whether we should track */
- if( tracklogin == 0 ) {
- sdn = slapi_sdn_new_normdn_byref( dn );
- ldrc = slapi_search_internal_get_entry( sdn, NULL, &target_entry,
- plugin_id );
- if( ldrc != LDAP_SUCCESS ) {
- slapi_log_error( SLAPI_LOG_FATAL, POST_PLUGIN_NAME,
- "Failed to retrieve entry \"%s\": %d\n", dn, ldrc );
- rc = -1;
- goto done;
- } else {
- if( target_entry && has_attr( target_entry,
- cfg->spec_attr_name, NULL ) ) {
- tracklogin = 1;
- }
- }
- }
- config_unlock();
- if( tracklogin ) {
- rc = acct_record_login( dn );
- }
- /* ...Any additional account policy postops go here... */
- done:
- if( rc == -1 ) {
- slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
- }
- slapi_entry_free( target_entry );
- slapi_sdn_free( &sdn );
- slapi_ch_free_string( &dn );
- slapi_log_error( SLAPI_LOG_PLUGIN, POST_PLUGIN_NAME,
- "<= acct_bind_postop\n" );
- return( rc == 0 ? CALLBACK_OK : CALLBACK_ERR );
- }
- static int acct_pre_op( Slapi_PBlock *pb, int modop )
- {
- Slapi_DN *sdn = 0;
- Slapi_Entry *e = 0;
- Slapi_Mods *smods = 0;
- LDAPMod **mods;
- int free_entry = 0;
- char *errstr = NULL;
- int ret = SLAPI_PLUGIN_SUCCESS;
- slapi_log_error(SLAPI_LOG_TRACE, PRE_PLUGIN_NAME, "--> acct_pre_op\n");
- slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
- if (acct_policy_dn_is_config(sdn)) {
- /* Validate config changes, but don't apply them.
- * This allows us to reject invalid config changes
- * here at the pre-op stage. Applying the config
- * needs to be done at the post-op stage. */
- if (LDAP_CHANGETYPE_ADD == modop) {
- slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
- /* If the entry doesn't exist, just bail and
- * let the server handle it. */
- if (e == NULL) {
- goto bail;
- }
- } else if (LDAP_CHANGETYPE_MODIFY == modop) {
- /* Fetch the entry being modified so we can
- * create the resulting entry for validation. */
- if (sdn) {
- slapi_search_internal_get_entry(sdn, 0, &e, get_identity());
- free_entry = 1;
- }
- /* If the entry doesn't exist, just bail and
- * let the server handle it. */
- if (e == NULL) {
- goto bail;
- }
- /* Grab the mods. */
- slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
- smods = slapi_mods_new();
- slapi_mods_init_byref(smods, mods);
- /* Apply the mods to create the resulting entry. */
- if (mods && (slapi_entry_apply_mods(e, mods) != LDAP_SUCCESS)) {
- /* The mods don't apply cleanly, so we just let this op go
- * to let the main server handle it. */
- goto bailmod;
- }
- } else if (modop == LDAP_CHANGETYPE_DELETE){
- ret = LDAP_UNWILLING_TO_PERFORM;
- slapi_log_error(SLAPI_LOG_FATAL, PRE_PLUGIN_NAME,
- "acct_pre_op: can not delete plugin config entry [%d]\n", ret);
- } else {
- errstr = slapi_ch_smprintf("acct_pre_op: invalid op type %d", modop);
- ret = LDAP_PARAM_ERROR;
- goto bail;
- }
- }
- bailmod:
- /* Clean up smods. */
- if (LDAP_CHANGETYPE_MODIFY == modop) {
- slapi_mods_free(&smods);
- }
- bail:
- if (free_entry && e)
- slapi_entry_free(e);
- if (ret) {
- slapi_log_error(SLAPI_LOG_PLUGIN, PRE_PLUGIN_NAME,
- "acct_pre_op: operation failure [%d]\n", ret);
- slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
- slapi_ch_free((void **)&errstr);
- slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret);
- ret = SLAPI_PLUGIN_FAILURE;
- }
- slapi_log_error(SLAPI_LOG_TRACE, PRE_PLUGIN_NAME, "<-- acct_pre_op\n");
- return ret;
- }
- int
- acct_add_pre_op( Slapi_PBlock *pb )
- {
- return acct_pre_op(pb, LDAP_CHANGETYPE_ADD);
- }
- int
- acct_mod_pre_op( Slapi_PBlock *pb )
- {
- return acct_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
- }
- int
- acct_del_pre_op( Slapi_PBlock *pb )
- {
- return acct_pre_op(pb, LDAP_CHANGETYPE_DELETE);
- }
- int
- acct_post_op(Slapi_PBlock *pb)
- {
- Slapi_DN *sdn = NULL;
- slapi_log_error(SLAPI_LOG_TRACE, POST_PLUGIN_NAME,
- "--> acct_policy_post_op\n");
- slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
- if (acct_policy_dn_is_config(sdn)){
- if( acct_policy_load_config_startup( pb, get_identity() ) ) {
- slapi_log_error( SLAPI_LOG_FATAL, PLUGIN_NAME,
- "acct_policy_start failed to load configuration\n" );
- return( CALLBACK_ERR );
- }
- }
- slapi_log_error(SLAPI_LOG_TRACE, POST_PLUGIN_NAME,
- "<-- acct_policy_mod_post_op\n");
- return SLAPI_PLUGIN_SUCCESS;
- }
|