lastbind.c 8.22 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
 * Optimization: to avoid performing a write on each bind,
26
27
28
 * a precision for this timestamp may be configured on the database,
 * causing it to only be updated if it is older than a given number
 * of seconds.
29
30
31
32
33
34
35
36
37
38
39
 */

#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>
40
#include "slap-config.h"
41

42
/* Per-instance configuration information */
43
typedef struct lastbind_info {
44
	int forward_updates;	/* use frontend for authTimestamp updates */
45
46
} lastbind_info;

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

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

68
/* configuration attribute and objectclass */
69
static ConfigTable lastbindcfg[] = {
70
71
72
73
74
	{ "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' "
75
	  "EQUALITY booleanMatch "
76
	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
77
78
79
80
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs lastbindocs[] = {
Howard Chu's avatar
Howard Chu committed
81
	{ "( OLcfgCtOc:5.1 "
82
83
84
	  "NAME 'olcLastBindConfig' "
	  "DESC 'Last Bind configuration' "
	  "SUP olcOverlayConfig "
85
	  "MAY ( olcLastBindForwardUpdates) )",
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
	  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
112
113
114
115
	/* we're only interested if the bind was successful */
	if ( rs->sr_err != LDAP_SUCCESS )
		return SLAP_CB_CONTINUE;

116
117
118
119
120
121
122
	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
123
	{
124
125
126
127
128
129
130
131
		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
132
		/* get the current time */
133
134
		now = slap_get_time();

Howard Chu's avatar
Howard Chu committed
135
		/* get authTimestamp attribute, if it exists */
136
		if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL) {
137
138
139
			bindtime = parse_time( a->a_nvals[0].bv_val );

			if (bindtime != (time_t)-1) {
Howard Chu's avatar
Howard Chu committed
140
141
				/* if the recorded bind time is within our precision, we're done
				 * it doesn't need to be updated (save a write for nothing) */
142
				if ((now - bindtime) < op->o_bd->be_lastbind_precision) {
143
144
145
146
					goto done;
				}
			}
		}
Howard Chu's avatar
Howard Chu committed
147

Howard Chu's avatar
Howard Chu committed
148
		/* update the authTimestamp in the user's entry with the current time */
149
150
151
152
153
154
155
		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;
156
157
		m->sml_type = ad_authTimestamp->ad_cname;
		m->sml_desc = ad_authTimestamp;
158
159
160
161
162
163
164
165
166
167
168
169
170
		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
171
	/* perform the update, if necessary */
172
173
174
175
	if ( mod ) {
		Operation op2 = *op;
		SlapReply r2 = { REP_RESULT };
		slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
176
177
		LDAPControl c, *ca[2];
		lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private;
178

179
		/* This is a DSA-specific opattr, it never gets replicated. */
180
181
182
		op2.o_tag = LDAP_REQ_MODIFY;
		op2.o_callback = &cb;
		op2.orm_modlist = mod;
183
		op2.orm_no_opattrs = 0;
184
185
		op2.o_dn = op->o_bd->be_rootdn;
		op2.o_ndn = op->o_bd->be_rootndn;
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

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

219
		rc = op2.o_bd->be_modify( &op2, &r2 );
220
221
222
223
224
225
226
227
228
229
230
231
232
		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
233
234
	/* setup a callback to intercept result of this bind operation
	 * and pass along the lastbind_info struct */
235
236
237
238
239
240
241
242
243
244
245
	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
246
247
	BackendDB *be,
	ConfigReply *cr
248
249
)
{
Howard Chu's avatar
Howard Chu committed
250
251
	slap_overinst *on = (slap_overinst *) be->bd_info;

Howard Chu's avatar
Howard Chu committed
252
	/* initialize private structure to store configuration */
253
254
255
256
257
258
259
260
261
262
263
264
265
266
	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
267
	/* free private structure to store configuration */
268
269
270
271
272
273
274
275
276
277
278
	free( lbi );

	return 0;
}

static slap_overinst lastbind;

int lastbind_initialize()
{
	int i, code;

Howard Chu's avatar
Howard Chu committed
279
	/* register operational schema for this overlay (authTimestamp attribute) */
280
281
282
283
	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,
284
				"lastbind_initialize: register_at failed\n" );
285
286
287
288
			return code;
		}
	}

289
290
	ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;

291
	lastbind.on_bi.bi_type = "lastbind";
292
	lastbind.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
293
294
295
296
	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
297
	/* register configuration directives */
298
299
300
301
302
303
304
305
306
307
308
309
310
311
	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) */