lastbind.c 8.61 KB
Newer Older
1
/* lastbind.c - Record timestamp of the last successful bind to entries */
Howard Chu's avatar
Howard Chu committed
2
3
/* $OpenLDAP$ */
/*
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 * 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"

/*
Howard Chu's avatar
Howard Chu committed
22
 * This file implements an overlay that stores the timestamp of the
23
 * last successful bind operation in a directory entry.
Howard Chu's avatar
Howard Chu committed
24
 *
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 * 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"

41
/* Per-instance configuration information */
42
typedef struct lastbind_info {
Howard Chu's avatar
Howard Chu committed
43
	/* precision to update timestamp in authTimestamp attribute */
44
	int timestamp_precision;
45
	int forward_updates;	/* use frontend for authTimestamp updates */
46
47
} lastbind_info;

48
49
/* Operational attributes */
static AttributeDescription *ad_authTimestamp;
50

51
52
53
/* This is the definition used by ISODE, as supplied to us in
 * ITS#6238 Followup #9
 */
54
55
56
57
static struct schema_info {
	char *def;
	AttributeDescription **ad;
} lastBind_OpSchema[] = {
58
59
60
	{	"( 1.3.6.1.4.1.453.16.2.188 "
		"NAME 'authTimestamp' "
		"DESC 'last successful authentication using any method/mech' "
61
62
63
		"EQUALITY generalizedTimeMatch "
		"ORDERING generalizedTimeOrderingMatch "
		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
64
65
		"SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
		&ad_authTimestamp},
66
67
68
	{ NULL, NULL }
};

69
/* configuration attribute and objectclass */
70
71
72
73
static ConfigTable lastbindcfg[] = {
	{ "lastbind-precision", "seconds", 2, 2, 0,
	  ARG_INT|ARG_OFFSET,
	  (void *)offsetof(lastbind_info, timestamp_precision),
Howard Chu's avatar
Howard Chu committed
74
	  "( OLcfgCtAt:5.1 "
75
	  "NAME 'olcLastBindPrecision' "
Howard Chu's avatar
Howard Chu committed
76
	  "DESC 'Precision of authTimestamp attribute' "
77
	  "EQUALITY integerMatch "
78
	  "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
79
80
81
82
83
	{ "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' "
84
	  "EQUALITY booleanMatch "
85
	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
86
87
88
89
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs lastbindocs[] = {
Howard Chu's avatar
Howard Chu committed
90
	{ "( OLcfgCtOc:5.1 "
91
92
93
	  "NAME 'olcLastBindConfig' "
	  "DESC 'Last Bind configuration' "
	  "SUP olcOverlayConfig "
94
	  "MAY ( olcLastBindPrecision $ olcLastBindForwardUpdates) )",
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
	  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;

Howard Chu's avatar
Howard Chu committed
121
122
123
124
	/* we're only interested if the bind was successful */
	if ( rs->sr_err != LDAP_SUCCESS )
		return SLAP_CB_CONTINUE;

125
126
127
128
129
130
131
	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;
	}

Howard Chu's avatar
Howard Chu committed
132
	{
133
134
135
136
137
138
139
140
		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;

Howard Chu's avatar
Howard Chu committed
141
		/* get the current time */
142
143
		now = slap_get_time();

Howard Chu's avatar
Howard Chu committed
144
		/* get authTimestamp attribute, if it exists */
145
		if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL) {
146
147
148
			bindtime = parse_time( a->a_nvals[0].bv_val );

			if (bindtime != (time_t)-1) {
Howard Chu's avatar
Howard Chu committed
149
150
				/* if the recorded bind time is within our precision, we're done
				 * it doesn't need to be updated (save a write for nothing) */
151
152
153
154
155
				if ((now - bindtime) < lbi->timestamp_precision) {
					goto done;
				}
			}
		}
Howard Chu's avatar
Howard Chu committed
156

Howard Chu's avatar
Howard Chu committed
157
		/* update the authTimestamp in the user's entry with the current time */
158
159
160
161
162
163
164
		timestamp.bv_val = nowstr;
		timestamp.bv_len = sizeof(nowstr);
		slap_timestamp( &now, &timestamp );

		m = ch_calloc( sizeof(Modifications), 1 );
		m->sml_op = LDAP_MOD_REPLACE;
		m->sml_flags = 0;
165
166
		m->sml_type = ad_authTimestamp->ad_cname;
		m->sml_desc = ad_authTimestamp;
167
168
169
170
171
172
173
174
175
176
177
178
179
		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], &timestamp );
		ber_dupbv( &m->sml_nvalues[0], &timestamp );
		m->sml_next = mod;
		mod = m;
	}

done:
	be_entry_release_r( op, e );

Howard Chu's avatar
Howard Chu committed
180
	/* perform the update, if necessary */
181
182
183
184
	if ( mod ) {
		Operation op2 = *op;
		SlapReply r2 = { REP_RESULT };
		slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
185
186
		LDAPControl c, *ca[2];
		lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private;
187

188
		/* This is a DSA-specific opattr, it never gets replicated. */
189
190
191
		op2.o_tag = LDAP_REQ_MODIFY;
		op2.o_callback = &cb;
		op2.orm_modlist = mod;
192
		op2.orm_no_opattrs = 0;
193
194
		op2.o_dn = op->o_bd->be_rootdn;
		op2.o_ndn = op->o_bd->be_rootndn;
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

		/*
		 * 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;
			*/
		}

228
229
230
231
232
233
234
235
236
237
238
239
240
241
		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;

Howard Chu's avatar
Howard Chu committed
242
243
	/* setup a callback to intercept result of this bind operation
	 * and pass along the lastbind_info struct */
244
245
246
247
248
249
250
251
252
253
254
	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(
Howard Chu's avatar
Howard Chu committed
255
256
	BackendDB *be,
	ConfigReply *cr
257
258
)
{
Howard Chu's avatar
Howard Chu committed
259
260
	slap_overinst *on = (slap_overinst *) be->bd_info;

Howard Chu's avatar
Howard Chu committed
261
	/* initialize private structure to store configuration */
262
263
264
265
266
267
268
269
270
271
272
273
274
275
	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;

Howard Chu's avatar
Howard Chu committed
276
	/* free private structure to store configuration */
277
278
279
280
281
282
283
284
285
286
287
	free( lbi );

	return 0;
}

static slap_overinst lastbind;

int lastbind_initialize()
{
	int i, code;

Howard Chu's avatar
Howard Chu committed
288
	/* register operational schema for this overlay (authTimestamp attribute) */
289
290
291
292
	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,
293
				"lastbind_initialize: register_at failed\n" );
294
295
296
297
			return code;
		}
	}

298
299
	ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;

300
	lastbind.on_bi.bi_type = "lastbind";
301
	lastbind.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
302
303
304
305
	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;

Howard Chu's avatar
Howard Chu committed
306
	/* register configuration directives */
307
308
309
310
311
312
313
314
315
316
317
318
319
320
	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) */