1 1.2 christos /* $NetBSD: lastbind.c,v 1.3 2025/09/05 21:16:16 christos Exp $ */ 2 1.1 tron 3 1.1 tron /* lastbind.c - Record timestamp of the last successful bind to entries */ 4 1.1 tron /* $OpenLDAP$ */ 5 1.1 tron /* 6 1.1 tron * Copyright 2009 Jonathan Clarke <jonathan (at) phillipoux.net>. 7 1.1 tron * All rights reserved. 8 1.1 tron * 9 1.1 tron * Redistribution and use in source and binary forms, with or without 10 1.1 tron * modification, are permitted only as authorized by the OpenLDAP 11 1.1 tron * Public License. 12 1.1 tron * 13 1.1 tron * A copy of this license is available in the file LICENSE in the 14 1.1 tron * top-level directory of the distribution or, alternatively, at 15 1.1 tron * <http://www.OpenLDAP.org/license.html>. 16 1.1 tron */ 17 1.1 tron /* ACKNOWLEDGEMENTS: 18 1.1 tron * This work is loosely derived from the ppolicy overlay. 19 1.1 tron */ 20 1.1 tron 21 1.2 christos #include <sys/cdefs.h> 22 1.2 christos __RCSID("$NetBSD: lastbind.c,v 1.3 2025/09/05 21:16:16 christos Exp $"); 23 1.2 christos 24 1.1 tron #include "portable.h" 25 1.1 tron 26 1.1 tron /* 27 1.1 tron * This file implements an overlay that stores the timestamp of the 28 1.1 tron * last successful bind operation in a directory entry. 29 1.1 tron * 30 1.1 tron * Optimization: to avoid performing a write on each bind, 31 1.3 christos * a precision for this timestamp may be configured on the database, 32 1.3 christos * causing it to only be updated if it is older than a given number 33 1.3 christos * of seconds. 34 1.1 tron */ 35 1.1 tron 36 1.1 tron #ifdef SLAPD_OVER_LASTBIND 37 1.1 tron 38 1.1 tron #include <ldap.h> 39 1.1 tron #include "lutil.h" 40 1.1 tron #include "slap.h" 41 1.1 tron #include <ac/errno.h> 42 1.1 tron #include <ac/time.h> 43 1.1 tron #include <ac/string.h> 44 1.1 tron #include <ac/ctype.h> 45 1.2 christos #include "slap-config.h" 46 1.1 tron 47 1.1 tron /* Per-instance configuration information */ 48 1.1 tron typedef struct lastbind_info { 49 1.2 christos int forward_updates; /* use frontend for authTimestamp updates */ 50 1.1 tron } lastbind_info; 51 1.1 tron 52 1.1 tron /* Operational attributes */ 53 1.1 tron static AttributeDescription *ad_authTimestamp; 54 1.1 tron 55 1.1 tron /* This is the definition used by ISODE, as supplied to us in 56 1.1 tron * ITS#6238 Followup #9 57 1.1 tron */ 58 1.1 tron static struct schema_info { 59 1.1 tron char *def; 60 1.1 tron AttributeDescription **ad; 61 1.1 tron } lastBind_OpSchema[] = { 62 1.1 tron { "( 1.3.6.1.4.1.453.16.2.188 " 63 1.1 tron "NAME 'authTimestamp' " 64 1.1 tron "DESC 'last successful authentication using any method/mech' " 65 1.1 tron "EQUALITY generalizedTimeMatch " 66 1.1 tron "ORDERING generalizedTimeOrderingMatch " 67 1.1 tron "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 68 1.1 tron "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )", 69 1.1 tron &ad_authTimestamp}, 70 1.1 tron { NULL, NULL } 71 1.1 tron }; 72 1.1 tron 73 1.1 tron /* configuration attribute and objectclass */ 74 1.1 tron static ConfigTable lastbindcfg[] = { 75 1.2 christos { "lastbind_forward_updates", "on|off", 1, 2, 0, 76 1.2 christos ARG_ON_OFF|ARG_OFFSET, 77 1.2 christos (void *)offsetof(lastbind_info,forward_updates), 78 1.2 christos "( OLcfgAt:5.2 NAME 'olcLastBindForwardUpdates' " 79 1.2 christos "DESC 'Allow authTimestamp updates to be forwarded via updateref' " 80 1.2 christos "EQUALITY booleanMatch " 81 1.2 christos "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 82 1.1 tron { NULL, NULL, 0, 0, 0, ARG_IGNORED } 83 1.1 tron }; 84 1.1 tron 85 1.1 tron static ConfigOCs lastbindocs[] = { 86 1.2 christos { "( OLcfgCtOc:5.1 " 87 1.1 tron "NAME 'olcLastBindConfig' " 88 1.1 tron "DESC 'Last Bind configuration' " 89 1.1 tron "SUP olcOverlayConfig " 90 1.3 christos "MAY ( olcLastBindForwardUpdates) )", 91 1.1 tron Cft_Overlay, lastbindcfg, NULL, NULL }, 92 1.1 tron { NULL, 0, NULL } 93 1.1 tron }; 94 1.1 tron 95 1.1 tron static time_t 96 1.1 tron parse_time( char *atm ) 97 1.1 tron { 98 1.1 tron struct lutil_tm tm; 99 1.1 tron struct lutil_timet tt; 100 1.1 tron time_t ret = (time_t)-1; 101 1.1 tron 102 1.1 tron if ( lutil_parsetime( atm, &tm ) == 0) { 103 1.1 tron lutil_tm2time( &tm, &tt ); 104 1.1 tron ret = tt.tt_sec; 105 1.1 tron } 106 1.1 tron return ret; 107 1.1 tron } 108 1.1 tron 109 1.1 tron static int 110 1.1 tron lastbind_bind_response( Operation *op, SlapReply *rs ) 111 1.1 tron { 112 1.1 tron Modifications *mod = NULL; 113 1.1 tron BackendInfo *bi = op->o_bd->bd_info; 114 1.1 tron Entry *e; 115 1.1 tron int rc; 116 1.1 tron 117 1.1 tron /* we're only interested if the bind was successful */ 118 1.1 tron if ( rs->sr_err != LDAP_SUCCESS ) 119 1.1 tron return SLAP_CB_CONTINUE; 120 1.1 tron 121 1.1 tron rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 122 1.1 tron op->o_bd->bd_info = bi; 123 1.1 tron 124 1.1 tron if ( rc != LDAP_SUCCESS ) { 125 1.1 tron return SLAP_CB_CONTINUE; 126 1.1 tron } 127 1.1 tron 128 1.1 tron { 129 1.1 tron lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private; 130 1.1 tron 131 1.1 tron time_t now, bindtime = (time_t)-1; 132 1.1 tron Attribute *a; 133 1.1 tron Modifications *m; 134 1.1 tron char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 135 1.1 tron struct berval timestamp; 136 1.1 tron 137 1.1 tron /* get the current time */ 138 1.1 tron now = slap_get_time(); 139 1.1 tron 140 1.1 tron /* get authTimestamp attribute, if it exists */ 141 1.1 tron if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL) { 142 1.1 tron bindtime = parse_time( a->a_nvals[0].bv_val ); 143 1.1 tron 144 1.1 tron if (bindtime != (time_t)-1) { 145 1.1 tron /* if the recorded bind time is within our precision, we're done 146 1.1 tron * it doesn't need to be updated (save a write for nothing) */ 147 1.3 christos if ((now - bindtime) < op->o_bd->be_lastbind_precision) { 148 1.1 tron goto done; 149 1.1 tron } 150 1.1 tron } 151 1.1 tron } 152 1.1 tron 153 1.1 tron /* update the authTimestamp in the user's entry with the current time */ 154 1.1 tron timestamp.bv_val = nowstr; 155 1.1 tron timestamp.bv_len = sizeof(nowstr); 156 1.1 tron slap_timestamp( &now, ×tamp ); 157 1.1 tron 158 1.1 tron m = ch_calloc( sizeof(Modifications), 1 ); 159 1.1 tron m->sml_op = LDAP_MOD_REPLACE; 160 1.1 tron m->sml_flags = 0; 161 1.1 tron m->sml_type = ad_authTimestamp->ad_cname; 162 1.1 tron m->sml_desc = ad_authTimestamp; 163 1.1 tron m->sml_numvals = 1; 164 1.1 tron m->sml_values = ch_calloc( sizeof(struct berval), 2 ); 165 1.1 tron m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); 166 1.1 tron 167 1.1 tron ber_dupbv( &m->sml_values[0], ×tamp ); 168 1.1 tron ber_dupbv( &m->sml_nvalues[0], ×tamp ); 169 1.1 tron m->sml_next = mod; 170 1.1 tron mod = m; 171 1.1 tron } 172 1.1 tron 173 1.1 tron done: 174 1.1 tron be_entry_release_r( op, e ); 175 1.1 tron 176 1.1 tron /* perform the update, if necessary */ 177 1.1 tron if ( mod ) { 178 1.1 tron Operation op2 = *op; 179 1.1 tron SlapReply r2 = { REP_RESULT }; 180 1.1 tron slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 181 1.2 christos LDAPControl c, *ca[2]; 182 1.2 christos lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private; 183 1.1 tron 184 1.1 tron /* This is a DSA-specific opattr, it never gets replicated. */ 185 1.1 tron op2.o_tag = LDAP_REQ_MODIFY; 186 1.1 tron op2.o_callback = &cb; 187 1.1 tron op2.orm_modlist = mod; 188 1.2 christos op2.orm_no_opattrs = 0; 189 1.1 tron op2.o_dn = op->o_bd->be_rootdn; 190 1.1 tron op2.o_ndn = op->o_bd->be_rootndn; 191 1.2 christos 192 1.2 christos /* 193 1.2 christos * Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy 194 1.2 christos * 195 1.2 christos * If this server is a shadow and forward_updates is true, 196 1.2 christos * use the frontend to perform this modify. That will trigger 197 1.2 christos * the update referral, which can then be forwarded by the 198 1.2 christos * chain overlay. Obviously the updateref and chain overlay 199 1.2 christos * must be configured appropriately for this to be useful. 200 1.2 christos */ 201 1.2 christos if ( SLAP_SHADOW( op->o_bd ) && lbi->forward_updates ) { 202 1.2 christos op2.o_bd = frontendDB; 203 1.2 christos 204 1.2 christos /* Must use Relax control since these are no-user-mod */ 205 1.2 christos op2.o_relax = SLAP_CONTROL_CRITICAL; 206 1.2 christos op2.o_ctrls = ca; 207 1.2 christos ca[0] = &c; 208 1.2 christos ca[1] = NULL; 209 1.2 christos BER_BVZERO( &c.ldctl_value ); 210 1.2 christos c.ldctl_iscritical = 1; 211 1.2 christos c.ldctl_oid = LDAP_CONTROL_RELAX; 212 1.2 christos } else { 213 1.2 christos /* If not forwarding, don't update opattrs and don't replicate */ 214 1.2 christos if ( SLAP_SINGLE_SHADOW( op->o_bd )) { 215 1.2 christos op2.orm_no_opattrs = 1; 216 1.2 christos op2.o_dont_replicate = 1; 217 1.2 christos } 218 1.2 christos /* TODO: not sure what this does in slapo-ppolicy */ 219 1.2 christos /* 220 1.2 christos op2.o_bd->bd_info = (BackendInfo *)on->on_info; 221 1.2 christos */ 222 1.2 christos } 223 1.2 christos 224 1.2 christos rc = op2.o_bd->be_modify( &op2, &r2 ); 225 1.1 tron slap_mods_free( mod, 1 ); 226 1.1 tron } 227 1.1 tron 228 1.1 tron op->o_bd->bd_info = bi; 229 1.1 tron return SLAP_CB_CONTINUE; 230 1.1 tron } 231 1.1 tron 232 1.1 tron static int 233 1.1 tron lastbind_bind( Operation *op, SlapReply *rs ) 234 1.1 tron { 235 1.1 tron slap_callback *cb; 236 1.1 tron slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 237 1.1 tron 238 1.1 tron /* setup a callback to intercept result of this bind operation 239 1.1 tron * and pass along the lastbind_info struct */ 240 1.1 tron cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx ); 241 1.1 tron cb->sc_response = lastbind_bind_response; 242 1.1 tron cb->sc_next = op->o_callback->sc_next; 243 1.1 tron cb->sc_private = on->on_bi.bi_private; 244 1.1 tron op->o_callback->sc_next = cb; 245 1.1 tron 246 1.1 tron return SLAP_CB_CONTINUE; 247 1.1 tron } 248 1.1 tron 249 1.1 tron static int 250 1.1 tron lastbind_db_init( 251 1.1 tron BackendDB *be, 252 1.1 tron ConfigReply *cr 253 1.1 tron ) 254 1.1 tron { 255 1.1 tron slap_overinst *on = (slap_overinst *) be->bd_info; 256 1.1 tron 257 1.1 tron /* initialize private structure to store configuration */ 258 1.1 tron on->on_bi.bi_private = ch_calloc( 1, sizeof(lastbind_info) ); 259 1.1 tron 260 1.1 tron return 0; 261 1.1 tron } 262 1.1 tron 263 1.1 tron static int 264 1.1 tron lastbind_db_close( 265 1.1 tron BackendDB *be, 266 1.1 tron ConfigReply *cr 267 1.1 tron ) 268 1.1 tron { 269 1.1 tron slap_overinst *on = (slap_overinst *) be->bd_info; 270 1.1 tron lastbind_info *lbi = (lastbind_info *) on->on_bi.bi_private; 271 1.1 tron 272 1.1 tron /* free private structure to store configuration */ 273 1.1 tron free( lbi ); 274 1.1 tron 275 1.1 tron return 0; 276 1.1 tron } 277 1.1 tron 278 1.1 tron static slap_overinst lastbind; 279 1.1 tron 280 1.1 tron int lastbind_initialize() 281 1.1 tron { 282 1.1 tron int i, code; 283 1.1 tron 284 1.1 tron /* register operational schema for this overlay (authTimestamp attribute) */ 285 1.1 tron for (i=0; lastBind_OpSchema[i].def; i++) { 286 1.1 tron code = register_at( lastBind_OpSchema[i].def, lastBind_OpSchema[i].ad, 0 ); 287 1.1 tron if ( code ) { 288 1.1 tron Debug( LDAP_DEBUG_ANY, 289 1.2 christos "lastbind_initialize: register_at failed\n" ); 290 1.1 tron return code; 291 1.1 tron } 292 1.1 tron } 293 1.1 tron 294 1.1 tron ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE; 295 1.1 tron 296 1.1 tron lastbind.on_bi.bi_type = "lastbind"; 297 1.2 christos lastbind.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; 298 1.1 tron lastbind.on_bi.bi_db_init = lastbind_db_init; 299 1.1 tron lastbind.on_bi.bi_db_close = lastbind_db_close; 300 1.1 tron lastbind.on_bi.bi_op_bind = lastbind_bind; 301 1.1 tron 302 1.1 tron /* register configuration directives */ 303 1.1 tron lastbind.on_bi.bi_cf_ocs = lastbindocs; 304 1.1 tron code = config_register_schema( lastbindcfg, lastbindocs ); 305 1.1 tron if ( code ) return code; 306 1.1 tron 307 1.1 tron return overlay_register( &lastbind ); 308 1.1 tron } 309 1.1 tron 310 1.1 tron #if SLAPD_OVER_LASTBIND == SLAPD_MOD_DYNAMIC 311 1.1 tron int init_module(int argc, char *argv[]) { 312 1.1 tron return lastbind_initialize(); 313 1.1 tron } 314 1.1 tron #endif 315 1.1 tron 316 1.1 tron #endif /* defined(SLAPD_OVER_LASTBIND) */ 317