diff options
Diffstat (limited to 'contrib/slapd-modules/lastbind')
-rw-r--r-- | contrib/slapd-modules/lastbind/Makefile | 56 | ||||
-rw-r--r-- | contrib/slapd-modules/lastbind/lastbind.c | 317 | ||||
-rw-r--r-- | contrib/slapd-modules/lastbind/slapo-lastbind.5 | 108 |
3 files changed, 481 insertions, 0 deletions
diff --git a/contrib/slapd-modules/lastbind/Makefile b/contrib/slapd-modules/lastbind/Makefile new file mode 100644 index 0000000..1745aa8 --- /dev/null +++ b/contrib/slapd-modules/lastbind/Makefile @@ -0,0 +1,56 @@ +# $OpenLDAP$ +# Copyright 2009 Jonathan Clarke <jonathan@phillipoux.net>. +# 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 +# <http://www.OpenLDAP.org/license.html>. + +LDAP_SRC = ../../.. +LDAP_BUILD = $(LDAP_SRC) +LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd +LDAP_LIB = $(LDAP_BUILD)/libraries/libldap_r/libldap_r.la \ + $(LDAP_BUILD)/libraries/liblber/liblber.la + +LIBTOOL = $(LDAP_BUILD)/libtool +CC = gcc +OPT = -g -O2 -Wall +DEFS = -DSLAPD_OVER_LASTBIND=SLAPD_MOD_DYNAMIC +INCS = $(LDAP_INC) +LIBS = $(LDAP_LIB) + +PROGRAMS = lastbind.la +LTVER = 0:0:0 + +prefix=/usr/local +exec_prefix=$(prefix) +ldap_subdir=/openldap + +libdir=$(exec_prefix)/lib +libexecdir=$(exec_prefix)/libexec +moduledir = $(libexecdir)$(ldap_subdir) + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $< + +all: $(PROGRAMS) + +lastbind.la: lastbind.lo + $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info $(LTVER) \ + -rpath $(moduledir) -module -o $@ $? $(LIBS) + +clean: + rm -rf *.o *.lo *.la .libs + +install: $(PROGRAMS) + mkdir -p $(DESTDIR)$(moduledir) + for p in $(PROGRAMS) ; do \ + $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \ + done + diff --git a/contrib/slapd-modules/lastbind/lastbind.c b/contrib/slapd-modules/lastbind/lastbind.c new file mode 100644 index 0000000..311be04 --- /dev/null +++ b/contrib/slapd-modules/lastbind/lastbind.c @@ -0,0 +1,317 @@ +/* lastbind.c - Record timestamp of the last successful bind to entries */ +/* $OpenLDAP$ */ +/* + * Copyright 2009 Jonathan Clarke <jonathan@phillipoux.net>. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work is loosely derived from the ppolicy overlay. + */ + +#include "portable.h" + +/* + * This file implements an overlay that stores the timestamp of the + * last successful bind operation in a directory entry. + * + * Optimization: to avoid performing a write on each bind, + * a precision for this timestamp may be configured, causing it to + * only be updated if it is older than a given number of seconds. + */ + +#ifdef SLAPD_OVER_LASTBIND + +#include <ldap.h> +#include "lutil.h" +#include "slap.h" +#include <ac/errno.h> +#include <ac/time.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include "config.h" + +/* Per-instance configuration information */ +typedef struct lastbind_info { + /* precision to update timestamp in authTimestamp attribute */ + int timestamp_precision; + int forward_updates; /* use frontend for authTimestamp updates */ +} lastbind_info; + +/* Operational attributes */ +static AttributeDescription *ad_authTimestamp; + +/* This is the definition used by ISODE, as supplied to us in + * ITS#6238 Followup #9 + */ +static struct schema_info { + char *def; + AttributeDescription **ad; +} lastBind_OpSchema[] = { + { "( 1.3.6.1.4.1.453.16.2.188 " + "NAME 'authTimestamp' " + "DESC 'last successful authentication using any method/mech' " + "EQUALITY generalizedTimeMatch " + "ORDERING generalizedTimeOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )", + &ad_authTimestamp}, + { NULL, NULL } +}; + +/* configuration attribute and objectclass */ +static ConfigTable lastbindcfg[] = { + { "lastbind-precision", "seconds", 2, 2, 0, + ARG_INT|ARG_OFFSET, + (void *)offsetof(lastbind_info, timestamp_precision), + "( OLcfgCtAt:5.1 " + "NAME 'olcLastBindPrecision' " + "DESC 'Precision of authTimestamp attribute' " + "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL }, + { "lastbind_forward_updates", "on|off", 1, 2, 0, + ARG_ON_OFF|ARG_OFFSET, + (void *)offsetof(lastbind_info,forward_updates), + "( OLcfgAt:5.2 NAME 'olcLastBindForwardUpdates' " + "DESC 'Allow authTimestamp updates to be forwarded via updateref' " + "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, + { NULL, NULL, 0, 0, 0, ARG_IGNORED } +}; + +static ConfigOCs lastbindocs[] = { + { "( OLcfgCtOc:5.1 " + "NAME 'olcLastBindConfig' " + "DESC 'Last Bind configuration' " + "SUP olcOverlayConfig " + "MAY ( olcLastBindPrecision $ olcLastBindForwardUpdates) )", + Cft_Overlay, lastbindcfg, NULL, NULL }, + { NULL, 0, NULL } +}; + +static time_t +parse_time( char *atm ) +{ + struct lutil_tm tm; + struct lutil_timet tt; + time_t ret = (time_t)-1; + + if ( lutil_parsetime( atm, &tm ) == 0) { + lutil_tm2time( &tm, &tt ); + ret = tt.tt_sec; + } + return ret; +} + +static int +lastbind_bind_response( Operation *op, SlapReply *rs ) +{ + Modifications *mod = NULL; + BackendInfo *bi = op->o_bd->bd_info; + Entry *e; + int rc; + + /* we're only interested if the bind was successful */ + if ( rs->sr_err != LDAP_SUCCESS ) + return SLAP_CB_CONTINUE; + + rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); + op->o_bd->bd_info = bi; + + if ( rc != LDAP_SUCCESS ) { + return SLAP_CB_CONTINUE; + } + + { + lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private; + + time_t now, bindtime = (time_t)-1; + Attribute *a; + Modifications *m; + char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ]; + struct berval timestamp; + + /* get the current time */ + now = slap_get_time(); + + /* get authTimestamp attribute, if it exists */ + if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL) { + bindtime = parse_time( a->a_nvals[0].bv_val ); + + if (bindtime != (time_t)-1) { + /* if the recorded bind time is within our precision, we're done + * it doesn't need to be updated (save a write for nothing) */ + if ((now - bindtime) < lbi->timestamp_precision) { + goto done; + } + } + } + + /* update the authTimestamp in the user's entry with the current time */ + timestamp.bv_val = nowstr; + timestamp.bv_len = sizeof(nowstr); + slap_timestamp( &now, ×tamp ); + + m = ch_calloc( sizeof(Modifications), 1 ); + m->sml_op = LDAP_MOD_REPLACE; + m->sml_flags = 0; + m->sml_type = ad_authTimestamp->ad_cname; + m->sml_desc = ad_authTimestamp; + m->sml_numvals = 1; + m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); + + ber_dupbv( &m->sml_values[0], ×tamp ); + ber_dupbv( &m->sml_nvalues[0], ×tamp ); + m->sml_next = mod; + mod = m; + } + +done: + be_entry_release_r( op, e ); + + /* perform the update, if necessary */ + if ( mod ) { + Operation op2 = *op; + SlapReply r2 = { REP_RESULT }; + slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; + LDAPControl c, *ca[2]; + lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private; + + /* This is a DSA-specific opattr, it never gets replicated. */ + op2.o_tag = LDAP_REQ_MODIFY; + op2.o_callback = &cb; + op2.orm_modlist = mod; + op2.orm_no_opattrs = 0; + op2.o_dn = op->o_bd->be_rootdn; + op2.o_ndn = op->o_bd->be_rootndn; + + /* + * Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy + * + * If this server is a shadow and forward_updates is true, + * use the frontend to perform this modify. That will trigger + * the update referral, which can then be forwarded by the + * chain overlay. Obviously the updateref and chain overlay + * must be configured appropriately for this to be useful. + */ + if ( SLAP_SHADOW( op->o_bd ) && lbi->forward_updates ) { + op2.o_bd = frontendDB; + + /* Must use Relax control since these are no-user-mod */ + op2.o_relax = SLAP_CONTROL_CRITICAL; + op2.o_ctrls = ca; + ca[0] = &c; + ca[1] = NULL; + BER_BVZERO( &c.ldctl_value ); + c.ldctl_iscritical = 1; + c.ldctl_oid = LDAP_CONTROL_RELAX; + } else { + /* If not forwarding, don't update opattrs and don't replicate */ + if ( SLAP_SINGLE_SHADOW( op->o_bd )) { + op2.orm_no_opattrs = 1; + op2.o_dont_replicate = 1; + } + /* TODO: not sure what this does in slapo-ppolicy */ + /* + op2.o_bd->bd_info = (BackendInfo *)on->on_info; + */ + } + + rc = op->o_bd->be_modify( &op2, &r2 ); + slap_mods_free( mod, 1 ); + } + + op->o_bd->bd_info = bi; + return SLAP_CB_CONTINUE; +} + +static int +lastbind_bind( Operation *op, SlapReply *rs ) +{ + slap_callback *cb; + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + + /* setup a callback to intercept result of this bind operation + * and pass along the lastbind_info struct */ + cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx ); + cb->sc_response = lastbind_bind_response; + cb->sc_next = op->o_callback->sc_next; + cb->sc_private = on->on_bi.bi_private; + op->o_callback->sc_next = cb; + + return SLAP_CB_CONTINUE; +} + +static int +lastbind_db_init( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *) be->bd_info; + + /* initialize private structure to store configuration */ + on->on_bi.bi_private = ch_calloc( 1, sizeof(lastbind_info) ); + + return 0; +} + +static int +lastbind_db_close( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *) be->bd_info; + lastbind_info *lbi = (lastbind_info *) on->on_bi.bi_private; + + /* free private structure to store configuration */ + free( lbi ); + + return 0; +} + +static slap_overinst lastbind; + +int lastbind_initialize() +{ + int i, code; + + /* register operational schema for this overlay (authTimestamp attribute) */ + for (i=0; lastBind_OpSchema[i].def; i++) { + code = register_at( lastBind_OpSchema[i].def, lastBind_OpSchema[i].ad, 0 ); + if ( code ) { + Debug( LDAP_DEBUG_ANY, + "lastbind_initialize: register_at failed\n", 0, 0, 0 ); + return code; + } + } + + ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE; + + lastbind.on_bi.bi_type = "lastbind"; + lastbind.on_bi.bi_db_init = lastbind_db_init; + lastbind.on_bi.bi_db_close = lastbind_db_close; + lastbind.on_bi.bi_op_bind = lastbind_bind; + + /* register configuration directives */ + lastbind.on_bi.bi_cf_ocs = lastbindocs; + code = config_register_schema( lastbindcfg, lastbindocs ); + if ( code ) return code; + + return overlay_register( &lastbind ); +} + +#if SLAPD_OVER_LASTBIND == SLAPD_MOD_DYNAMIC +int init_module(int argc, char *argv[]) { + return lastbind_initialize(); +} +#endif + +#endif /* defined(SLAPD_OVER_LASTBIND) */ diff --git a/contrib/slapd-modules/lastbind/slapo-lastbind.5 b/contrib/slapd-modules/lastbind/slapo-lastbind.5 new file mode 100644 index 0000000..d0da8b8 --- /dev/null +++ b/contrib/slapd-modules/lastbind/slapo-lastbind.5 @@ -0,0 +1,108 @@ +.TH SLAPO-LASTBIND 5 "RELEASEDATE" "OpenLDAP LDVERSION" +.\" Copyright 2009 Jonathan Clarke, All Rights Reserved. +.\" $OpenLDAP$ +.SH NAME +slapo-lastbind \- lastbind overlay to slapd +.SH SYNOPSIS +ETCDIR/slapd.conf +.SH DESCRIPTION +The +.B lastbind +overlay to +.BR slapd (8) +allows recording the timestamp of the last successful bind to entries +in the directory, in the +.B authTimestamp +attribute. +The overlay can be configured to update this timestamp only if it is +older than a given value, thus avoiding large numbers of write +operations penalizing performance. +One sample use for this overlay would be to detect unused accounts. + +.SH CONFIGURATION +The config directives that are specific to the +.B lastbind +overlay must be prefixed by +.BR lastbind\- , +to avoid potential conflicts with directives specific to the underlying +database or to other stacked overlays. + +.TP +.B overlay lastbind +This directive adds the +.B lastbind +overlay to the current database, see +.BR slapd.conf (5) +for details. + +.LP +This +.B slapd.conf +configuration option is defined for the lastbind overlay. It must +appear after the +.B overlay +directive: +.TP +.B lastbind-precision <seconds> +The value +.B <seconds> +is the number of seconds after which to update the +.B authTimestamp +attribute in an entry. If the existing value of +.B authTimestamp +is less than +.B <seconds> +old, it will not be changed. +If this configuration option is omitted, the +.B authTimestamp +attribute is updated on each successful bind operation. +.TP +.B lastbind_forward_updates +Specify that updates of the authTimestamp attribute +on a consumer should be forwarded +to a provider instead of being written directly into the consumer's local +database. This setting is only useful on a replication consumer, and +also requires the +.B updateref +setting and +.B chain +overlay to be appropriately configured. + +.SH EXAMPLE +This example configures the +.B lastbind +overlay to store +.B authTimestamp +in all entries in a database, with a 1 week precision. +Add the following to +.BR slapd.conf (5): + +.LP +.nf + database <database> + # ... + + overlay lastbind + lastbind-precision 604800 +.fi +.LP +.B slapd +must also load +.B lastbind.la, +if compiled as a run-time module; + +.SH FILES +.TP +ETCDIR/slapd.conf +default slapd configuration file +.SH SEE ALSO +.BR slapd.conf (5), +.BR slapd (8). +The +.BR slapo-lastbind (5) +overlay supports dynamic configuration via +.BR back-config. +.SH ACKNOWLEDGEMENTS +.P +This module was written in 2009 by Jonathan Clarke. It is loosely +derived from the password policy overlay. |