accesslog.c revision 1.1.1.2
1/*	$NetBSD: accesslog.c,v 1.1.1.2 2010/03/08 02:14:20 lukem Exp $	*/
2
3/* accesslog.c - log operations for audit/history purposes */
4/* OpenLDAP: pkg/ldap/servers/slapd/overlays/accesslog.c,v 1.37.2.25 2009/11/24 05:50:11 quanah Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2005-2009 The OpenLDAP Foundation.
8 * Portions copyright 2004-2005 Symas Corporation.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted only as authorized by the OpenLDAP
13 * Public License.
14 *
15 * A copy of this license is available in the file LICENSE in the
16 * top-level directory of the distribution or, alternatively, at
17 * <http://www.OpenLDAP.org/license.html>.
18 */
19/* ACKNOWLEDGEMENTS:
20 * This work was initially developed by Howard Chu for inclusion in
21 * OpenLDAP Software.
22 */
23
24#include "portable.h"
25
26#ifdef SLAPD_OVER_ACCESSLOG
27
28#include <stdio.h>
29
30#include <ac/string.h>
31#include <ac/ctype.h>
32
33#include "slap.h"
34#include "config.h"
35#include "lutil.h"
36#include "ldap_rq.h"
37
38#define LOG_OP_ADD	0x001
39#define LOG_OP_DELETE	0x002
40#define	LOG_OP_MODIFY	0x004
41#define LOG_OP_MODRDN	0x008
42#define	LOG_OP_COMPARE	0x010
43#define	LOG_OP_SEARCH	0x020
44#define LOG_OP_BIND	0x040
45#define LOG_OP_UNBIND	0x080
46#define	LOG_OP_ABANDON	0x100
47#define	LOG_OP_EXTENDED	0x200
48#define LOG_OP_UNKNOWN	0x400
49
50#define	LOG_OP_WRITES	(LOG_OP_ADD|LOG_OP_DELETE|LOG_OP_MODIFY|LOG_OP_MODRDN)
51#define	LOG_OP_READS	(LOG_OP_COMPARE|LOG_OP_SEARCH)
52#define	LOG_OP_SESSION	(LOG_OP_BIND|LOG_OP_UNBIND|LOG_OP_ABANDON)
53#define LOG_OP_ALL		(LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \
54	LOG_OP_EXTENDED|LOG_OP_UNKNOWN)
55
56typedef struct log_attr {
57	struct log_attr *next;
58	AttributeDescription *attr;
59} log_attr;
60
61typedef struct log_info {
62	BackendDB *li_db;
63	struct berval li_db_suffix;
64	slap_mask_t li_ops;
65	int li_age;
66	int li_cycle;
67	struct re_s *li_task;
68	Filter *li_oldf;
69	Entry *li_old;
70	log_attr *li_oldattrs;
71	int li_success;
72	ldap_pvt_thread_rmutex_t li_op_rmutex;
73	ldap_pvt_thread_mutex_t li_log_mutex;
74} log_info;
75
76static ConfigDriver log_cf_gen;
77
78enum {
79	LOG_DB = 1,
80	LOG_OPS,
81	LOG_PURGE,
82	LOG_SUCCESS,
83	LOG_OLD,
84	LOG_OLDATTR
85};
86
87static ConfigTable log_cfats[] = {
88	{ "logdb", "suffix", 2, 2, 0, ARG_DN|ARG_MAGIC|LOG_DB,
89		log_cf_gen, "( OLcfgOvAt:4.1 NAME 'olcAccessLogDB' "
90			"DESC 'Suffix of database for log content' "
91			"SUP distinguishedName SINGLE-VALUE )", NULL, NULL },
92	{ "logops", "op|writes|reads|session|all", 2, 0, 0,
93		ARG_MAGIC|LOG_OPS,
94		log_cf_gen, "( OLcfgOvAt:4.2 NAME 'olcAccessLogOps' "
95			"DESC 'Operation types to log' "
96			"EQUALITY caseIgnoreMatch "
97			"SYNTAX OMsDirectoryString )", NULL, NULL },
98	{ "logpurge", "age> <interval", 3, 3, 0, ARG_MAGIC|LOG_PURGE,
99		log_cf_gen, "( OLcfgOvAt:4.3 NAME 'olcAccessLogPurge' "
100			"DESC 'Log cleanup parameters' "
101			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
102	{ "logsuccess", NULL, 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|LOG_SUCCESS,
103		log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' "
104			"DESC 'Log successful ops only' "
105			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
106	{ "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD,
107		log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' "
108			"DESC 'Log old values when modifying entries matching the filter' "
109			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
110	{ "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR,
111		log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' "
112			"DESC 'Log old values of these attributes even if unmodified' "
113			"EQUALITY caseIgnoreMatch "
114			"SYNTAX OMsDirectoryString )", NULL, NULL },
115	{ NULL }
116};
117
118static ConfigOCs log_cfocs[] = {
119	{ "( OLcfgOvOc:4.1 "
120		"NAME 'olcAccessLogConfig' "
121		"DESC 'Access log configuration' "
122		"SUP olcOverlayConfig "
123		"MUST olcAccessLogDB "
124		"MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ "
125			"olcAccessLogOld $ olcAccessLogOldAttr ) )",
126			Cft_Overlay, log_cfats },
127	{ NULL }
128};
129
130static slap_verbmasks logops[] = {
131	{ BER_BVC("all"),		LOG_OP_ALL },
132	{ BER_BVC("writes"),	LOG_OP_WRITES },
133	{ BER_BVC("session"),	LOG_OP_SESSION },
134	{ BER_BVC("reads"),		LOG_OP_READS },
135	{ BER_BVC("add"),		LOG_OP_ADD },
136	{ BER_BVC("delete"),	LOG_OP_DELETE },
137	{ BER_BVC("modify"),	LOG_OP_MODIFY },
138	{ BER_BVC("modrdn"),	LOG_OP_MODRDN },
139	{ BER_BVC("compare"),	LOG_OP_COMPARE },
140	{ BER_BVC("search"),	LOG_OP_SEARCH },
141	{ BER_BVC("bind"),		LOG_OP_BIND },
142	{ BER_BVC("unbind"),	LOG_OP_UNBIND },
143	{ BER_BVC("abandon"),	LOG_OP_ABANDON },
144	{ BER_BVC("extended"),	LOG_OP_EXTENDED },
145	{ BER_BVC("unknown"),	LOG_OP_UNKNOWN },
146	{ BER_BVNULL, 0 }
147};
148
149/* Start with "add" in logops */
150#define EN_OFFSET	4
151
152enum {
153	LOG_EN_ADD = 0,
154	LOG_EN_DELETE,
155	LOG_EN_MODIFY,
156	LOG_EN_MODRDN,
157	LOG_EN_COMPARE,
158	LOG_EN_SEARCH,
159	LOG_EN_BIND,
160	LOG_EN_UNBIND,
161	LOG_EN_ABANDON,
162	LOG_EN_EXTENDED,
163	LOG_EN_UNKNOWN,
164	LOG_EN__COUNT
165};
166
167static ObjectClass *log_ocs[LOG_EN__COUNT], *log_container,
168	*log_oc_read, *log_oc_write;
169
170#define LOG_SCHEMA_ROOT	"1.3.6.1.4.1.4203.666.11.5"
171
172#define LOG_SCHEMA_AT LOG_SCHEMA_ROOT ".1"
173#define LOG_SCHEMA_OC LOG_SCHEMA_ROOT ".2"
174#define LOG_SCHEMA_SYN LOG_SCHEMA_ROOT ".3"
175
176static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType,
177	*ad_reqSession, *ad_reqResult, *ad_reqAuthzID, *ad_reqControls,
178	*ad_reqRespControls, *ad_reqMethod, *ad_reqAssertion, *ad_reqNewRDN,
179	*ad_reqNewSuperior, *ad_reqDeleteOldRDN, *ad_reqMod,
180	*ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries,
181	*ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData,
182	*ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases,
183	*ad_reqReferral, *ad_reqOld, *ad_auditContext;
184
185static int
186logSchemaControlValidate(
187	Syntax		*syntax,
188	struct berval	*val );
189
190char	*mrControl[] = {
191	"objectIdentifierFirstComponentMatch",
192	NULL
193};
194
195static struct {
196	char			*oid;
197	slap_syntax_defs_rec	syn;
198	char			**mrs;
199} lsyntaxes[] = {
200	{ LOG_SCHEMA_SYN ".1" ,
201		{ "( " LOG_SCHEMA_SYN ".1 DESC 'Control' )",
202			SLAP_SYNTAX_HIDE,
203			NULL,
204			logSchemaControlValidate,
205			NULL },
206		mrControl },
207	{ NULL }
208};
209
210static struct {
211	char *at;
212	AttributeDescription **ad;
213} lattrs[] = {
214	{ "( " LOG_SCHEMA_AT ".1 NAME 'reqDN' "
215		"DESC 'Target DN of request' "
216		"EQUALITY distinguishedNameMatch "
217		"SYNTAX OMsDN "
218		"SINGLE-VALUE )", &ad_reqDN },
219	{ "( " LOG_SCHEMA_AT ".2 NAME 'reqStart' "
220		"DESC 'Start time of request' "
221		"EQUALITY generalizedTimeMatch "
222		"ORDERING generalizedTimeOrderingMatch "
223		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
224		"SINGLE-VALUE )", &ad_reqStart },
225	{ "( " LOG_SCHEMA_AT ".3 NAME 'reqEnd' "
226		"DESC 'End time of request' "
227		"EQUALITY generalizedTimeMatch "
228		"ORDERING generalizedTimeOrderingMatch "
229		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
230		"SINGLE-VALUE )", &ad_reqEnd },
231	{ "( " LOG_SCHEMA_AT ".4 NAME 'reqType' "
232		"DESC 'Type of request' "
233		"EQUALITY caseIgnoreMatch "
234		"SYNTAX OMsDirectoryString "
235		"SINGLE-VALUE )", &ad_reqType },
236	{ "( " LOG_SCHEMA_AT ".5 NAME 'reqSession' "
237		"DESC 'Session ID of request' "
238		"EQUALITY caseIgnoreMatch "
239		"SYNTAX OMsDirectoryString "
240		"SINGLE-VALUE )", &ad_reqSession },
241	{ "( " LOG_SCHEMA_AT ".6 NAME 'reqAuthzID' "
242		"DESC 'Authorization ID of requestor' "
243		"EQUALITY distinguishedNameMatch "
244		"SYNTAX OMsDN "
245		"SINGLE-VALUE )", &ad_reqAuthzID },
246	{ "( " LOG_SCHEMA_AT ".7 NAME 'reqResult' "
247		"DESC 'Result code of request' "
248		"EQUALITY integerMatch "
249		"ORDERING integerOrderingMatch "
250		"SYNTAX OMsInteger "
251		"SINGLE-VALUE )", &ad_reqResult },
252	{ "( " LOG_SCHEMA_AT ".8 NAME 'reqMessage' "
253		"DESC 'Error text of request' "
254		"EQUALITY caseIgnoreMatch "
255		"SUBSTR caseIgnoreSubstringsMatch "
256		"SYNTAX OMsDirectoryString "
257		"SINGLE-VALUE )", &ad_reqMessage },
258	{ "( " LOG_SCHEMA_AT ".9 NAME 'reqReferral' "
259		"DESC 'Referrals returned for request' "
260		"SUP labeledURI )", &ad_reqReferral },
261	{ "( " LOG_SCHEMA_AT ".10 NAME 'reqControls' "
262		"DESC 'Request controls' "
263		"EQUALITY objectIdentifierFirstComponentMatch "
264		"SYNTAX " LOG_SCHEMA_SYN ".1 "
265		"X-ORDERED 'VALUES' )", &ad_reqControls },
266	{ "( " LOG_SCHEMA_AT ".11 NAME 'reqRespControls' "
267		"DESC 'Response controls of request' "
268		"EQUALITY objectIdentifierFirstComponentMatch "
269		"SYNTAX " LOG_SCHEMA_SYN ".1 "
270		"X-ORDERED 'VALUES' )", &ad_reqRespControls },
271	{ "( " LOG_SCHEMA_AT ".12 NAME 'reqId' "
272		"DESC 'ID of Request to Abandon' "
273		"EQUALITY integerMatch "
274		"ORDERING integerOrderingMatch "
275		"SYNTAX OMsInteger "
276		"SINGLE-VALUE )", &ad_reqId },
277	{ "( " LOG_SCHEMA_AT ".13 NAME 'reqVersion' "
278		"DESC 'Protocol version of Bind request' "
279		"EQUALITY integerMatch "
280		"ORDERING integerOrderingMatch "
281		"SYNTAX OMsInteger "
282		"SINGLE-VALUE )", &ad_reqVersion },
283	{ "( " LOG_SCHEMA_AT ".14 NAME 'reqMethod' "
284		"DESC 'Bind method of request' "
285		"EQUALITY caseIgnoreMatch "
286		"SYNTAX OMsDirectoryString "
287		"SINGLE-VALUE )", &ad_reqMethod },
288	{ "( " LOG_SCHEMA_AT ".15 NAME 'reqAssertion' "
289		"DESC 'Compare Assertion of request' "
290		"SYNTAX OMsDirectoryString "
291		"SINGLE-VALUE )", &ad_reqAssertion },
292	{ "( " LOG_SCHEMA_AT ".16 NAME 'reqMod' "
293		"DESC 'Modifications of request' "
294		"EQUALITY octetStringMatch "
295		"SUBSTR octetStringSubstringsMatch "
296		"SYNTAX OMsOctetString )", &ad_reqMod },
297	{ "( " LOG_SCHEMA_AT ".17 NAME 'reqOld' "
298		"DESC 'Old values of entry before request completed' "
299		"EQUALITY octetStringMatch "
300		"SUBSTR octetStringSubstringsMatch "
301		"SYNTAX OMsOctetString )", &ad_reqOld },
302	{ "( " LOG_SCHEMA_AT ".18 NAME 'reqNewRDN' "
303		"DESC 'New RDN of request' "
304		"EQUALITY distinguishedNameMatch "
305		"SYNTAX OMsDN "
306		"SINGLE-VALUE )", &ad_reqNewRDN },
307	{ "( " LOG_SCHEMA_AT ".19 NAME 'reqDeleteOldRDN' "
308		"DESC 'Delete old RDN' "
309		"EQUALITY booleanMatch "
310		"SYNTAX OMsBoolean "
311		"SINGLE-VALUE )", &ad_reqDeleteOldRDN },
312	{ "( " LOG_SCHEMA_AT ".20 NAME 'reqNewSuperior' "
313		"DESC 'New superior DN of request' "
314		"EQUALITY distinguishedNameMatch "
315		"SYNTAX OMsDN "
316		"SINGLE-VALUE )", &ad_reqNewSuperior },
317	{ "( " LOG_SCHEMA_AT ".21 NAME 'reqScope' "
318		"DESC 'Scope of request' "
319		"EQUALITY caseIgnoreMatch "
320		"SYNTAX OMsDirectoryString "
321		"SINGLE-VALUE )", &ad_reqScope },
322	{ "( " LOG_SCHEMA_AT ".22 NAME 'reqDerefAliases' "
323		"DESC 'Disposition of Aliases in request' "
324		"EQUALITY caseIgnoreMatch "
325		"SYNTAX OMsDirectoryString "
326		"SINGLE-VALUE )", &ad_reqDerefAliases },
327	{ "( " LOG_SCHEMA_AT ".23 NAME 'reqAttrsOnly' "
328		"DESC 'Attributes and values of request' "
329		"EQUALITY booleanMatch "
330		"SYNTAX OMsBoolean "
331		"SINGLE-VALUE )", &ad_reqAttrsOnly },
332	{ "( " LOG_SCHEMA_AT ".24 NAME 'reqFilter' "
333		"DESC 'Filter of request' "
334		"EQUALITY caseIgnoreMatch "
335		"SUBSTR caseIgnoreSubstringsMatch "
336		"SYNTAX OMsDirectoryString "
337		"SINGLE-VALUE )", &ad_reqFilter },
338	{ "( " LOG_SCHEMA_AT ".25 NAME 'reqAttr' "
339		"DESC 'Attributes of request' "
340		"EQUALITY caseIgnoreMatch "
341		"SYNTAX OMsDirectoryString )", &ad_reqAttr },
342	{ "( " LOG_SCHEMA_AT ".26 NAME 'reqSizeLimit' "
343		"DESC 'Size limit of request' "
344		"EQUALITY integerMatch "
345		"ORDERING integerOrderingMatch "
346		"SYNTAX OMsInteger "
347		"SINGLE-VALUE )", &ad_reqSizeLimit },
348	{ "( " LOG_SCHEMA_AT ".27 NAME 'reqTimeLimit' "
349		"DESC 'Time limit of request' "
350		"EQUALITY integerMatch "
351		"ORDERING integerOrderingMatch "
352		"SYNTAX OMsInteger "
353		"SINGLE-VALUE )", &ad_reqTimeLimit },
354	{ "( " LOG_SCHEMA_AT ".28 NAME 'reqEntries' "
355		"DESC 'Number of entries returned' "
356		"EQUALITY integerMatch "
357		"ORDERING integerOrderingMatch "
358		"SYNTAX OMsInteger "
359		"SINGLE-VALUE )", &ad_reqEntries },
360	{ "( " LOG_SCHEMA_AT ".29 NAME 'reqData' "
361		"DESC 'Data of extended request' "
362		"EQUALITY octetStringMatch "
363		"SUBSTR octetStringSubstringsMatch "
364		"SYNTAX OMsOctetString "
365		"SINGLE-VALUE )", &ad_reqData },
366
367	/*
368	 * from <draft-chu-ldap-logschema-01.txt>:
369	 *
370
371   ( LOG_SCHEMA_AT .30 NAME 'auditContext'
372   DESC 'DN of auditContainer'
373   EQUALITY distinguishedNameMatch
374   SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
375   SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
376
377	 * - removed EQUALITY matchingRule
378	 * - changed directoryOperation in dSAOperation
379	 */
380	{ "( " LOG_SCHEMA_AT ".30 NAME 'auditContext' "
381		"DESC 'DN of auditContainer' "
382		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
383		"SINGLE-VALUE "
384		"NO-USER-MODIFICATION "
385		"USAGE dSAOperation )", &ad_auditContext },
386	{ NULL, NULL }
387};
388
389static struct {
390	char *ot;
391	ObjectClass **oc;
392} locs[] = {
393	{ "( " LOG_SCHEMA_OC ".0 NAME 'auditContainer' "
394		"DESC 'AuditLog container' "
395		"SUP top STRUCTURAL "
396		"MAY ( cn $ reqStart $ reqEnd ) )", &log_container },
397	{ "( " LOG_SCHEMA_OC ".1 NAME 'auditObject' "
398		"DESC 'OpenLDAP request auditing' "
399		"SUP top STRUCTURAL "
400		"MUST ( reqStart $ reqType $ reqSession ) "
401		"MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $ reqEnd $ "
402			"reqResult $ reqMessage $ reqReferral ) )",
403				&log_ocs[LOG_EN_UNBIND] },
404	{ "( " LOG_SCHEMA_OC ".2 NAME 'auditReadObject' "
405		"DESC 'OpenLDAP read request record' "
406		"SUP auditObject STRUCTURAL )", &log_oc_read },
407	{ "( " LOG_SCHEMA_OC ".3 NAME 'auditWriteObject' "
408		"DESC 'OpenLDAP write request record' "
409		"SUP auditObject STRUCTURAL )", &log_oc_write },
410	{ "( " LOG_SCHEMA_OC ".4 NAME 'auditAbandon' "
411		"DESC 'Abandon operation' "
412		"SUP auditObject STRUCTURAL "
413		"MUST reqId )", &log_ocs[LOG_EN_ABANDON] },
414	{ "( " LOG_SCHEMA_OC ".5 NAME 'auditAdd' "
415		"DESC 'Add operation' "
416		"SUP auditWriteObject STRUCTURAL "
417		"MUST reqMod )", &log_ocs[LOG_EN_ADD] },
418	{ "( " LOG_SCHEMA_OC ".6 NAME 'auditBind' "
419		"DESC 'Bind operation' "
420		"SUP auditObject STRUCTURAL "
421		"MUST ( reqVersion $ reqMethod ) )", &log_ocs[LOG_EN_BIND] },
422	{ "( " LOG_SCHEMA_OC ".7 NAME 'auditCompare' "
423		"DESC 'Compare operation' "
424		"SUP auditReadObject STRUCTURAL "
425		"MUST reqAssertion )", &log_ocs[LOG_EN_COMPARE] },
426	{ "( " LOG_SCHEMA_OC ".8 NAME 'auditDelete' "
427		"DESC 'Delete operation' "
428		"SUP auditWriteObject STRUCTURAL "
429		"MAY reqOld )", &log_ocs[LOG_EN_DELETE] },
430	{ "( " LOG_SCHEMA_OC ".9 NAME 'auditModify' "
431		"DESC 'Modify operation' "
432		"SUP auditWriteObject STRUCTURAL "
433		"MAY reqOld MUST reqMod )", &log_ocs[LOG_EN_MODIFY] },
434	{ "( " LOG_SCHEMA_OC ".10 NAME 'auditModRDN' "
435		"DESC 'ModRDN operation' "
436		"SUP auditWriteObject STRUCTURAL "
437		"MUST ( reqNewRDN $ reqDeleteOldRDN ) "
438		"MAY ( reqNewSuperior $ reqMod $ reqOld ) )", &log_ocs[LOG_EN_MODRDN] },
439	{ "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' "
440		"DESC 'Search operation' "
441		"SUP auditReadObject STRUCTURAL "
442		"MUST ( reqScope $ reqDerefAliases $ reqAttrsonly ) "
443		"MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $ "
444			"reqTimeLimit ) )", &log_ocs[LOG_EN_SEARCH] },
445	{ "( " LOG_SCHEMA_OC ".12 NAME 'auditExtended' "
446		"DESC 'Extended operation' "
447		"SUP auditObject STRUCTURAL "
448		"MAY reqData )", &log_ocs[LOG_EN_EXTENDED] },
449	{ NULL, NULL }
450};
451
452#define	RDNEQ	"reqStart="
453
454/* Our time intervals are of the form [ddd+]hh:mm[:ss]
455 * If a field is present, it must be two digits. (Except for
456 * days, which can be arbitrary width.)
457 */
458static int
459log_age_parse(char *agestr)
460{
461	int t1, t2;
462	int gotdays = 0;
463	char *endptr;
464
465	t1 = strtol( agestr, &endptr, 10 );
466	/* Is there a days delimiter? */
467	if ( *endptr == '+' ) {
468		/* 32 bit time only covers about 68 years */
469		if ( t1 < 0 || t1 > 25000 )
470			return -1;
471		t1 *= 24;
472		gotdays = 1;
473		agestr = endptr + 1;
474	} else {
475		if ( agestr[2] != ':' ) {
476			/* No valid delimiter found, fail */
477			return -1;
478		}
479		t1 *= 60;
480		agestr += 3;
481	}
482
483	t2 = atoi( agestr );
484	t1 += t2;
485
486	if ( agestr[2] ) {
487		/* if there's a delimiter, it can only be a colon */
488		if ( agestr[2] != ':' )
489			return -1;
490	} else {
491		/* If we're at the end of the string, and we started with days,
492		 * fail because we expected to find minutes too.
493		 */
494		return gotdays ? -1 : t1 * 60;
495	}
496
497	agestr += 3;
498	t2 = atoi( agestr );
499
500	/* last field can only be seconds */
501	if ( agestr[2] && ( agestr[2] != ':' || !gotdays ))
502		return -1;
503	t1 *= 60;
504	t1 += t2;
505
506	if ( agestr[2] ) {
507		agestr += 3;
508		if ( agestr[2] )
509			return -1;
510		t1 *= 60;
511		t1 += atoi( agestr );
512	} else if ( gotdays ) {
513		/* only got days+hh:mm */
514		t1 *= 60;
515	}
516	return t1;
517}
518
519static void
520log_age_unparse( int age, struct berval *agebv, size_t size )
521{
522	int dd, hh, mm, ss, len;
523	char *ptr;
524
525	assert( size > 0 );
526
527	ss = age % 60;
528	age /= 60;
529	mm = age % 60;
530	age /= 60;
531	hh = age % 24;
532	age /= 24;
533	dd = age;
534
535	ptr = agebv->bv_val;
536
537	if ( dd ) {
538		len = snprintf( ptr, size, "%d+", dd );
539		assert( len >= 0 && (unsigned) len < size );
540		size -= len;
541		ptr += len;
542	}
543	len = snprintf( ptr, size, "%02d:%02d", hh, mm );
544	assert( len >= 0 && (unsigned) len < size );
545	size -= len;
546	ptr += len;
547	if ( ss ) {
548		len = snprintf( ptr, size, ":%02d", ss );
549		assert( len >= 0 && (unsigned) len < size );
550		size -= len;
551		ptr += len;
552	}
553
554	agebv->bv_len = ptr - agebv->bv_val;
555}
556
557static slap_callback nullsc = { NULL, NULL, NULL, NULL };
558
559#define PURGE_INCREMENT	100
560
561typedef struct purge_data {
562	int slots;
563	int used;
564	BerVarray dn;
565	BerVarray ndn;
566	struct berval csn;	/* an arbitrary old CSN */
567} purge_data;
568
569static int
570log_old_lookup( Operation *op, SlapReply *rs )
571{
572	purge_data *pd = op->o_callback->sc_private;
573	Attribute *a;
574
575	if ( rs->sr_type != REP_SEARCH) return 0;
576
577	if ( slapd_shutdown ) return 0;
578
579	/* Remember max CSN: should always be the last entry
580	 * seen, since log entries are ordered chronologically...
581	 */
582	a = attr_find( rs->sr_entry->e_attrs,
583		slap_schema.si_ad_entryCSN );
584	if ( a ) {
585		ber_len_t len = a->a_nvals[0].bv_len;
586		/* Paranoid len check, normalized CSNs are always the same length */
587		if ( len > LDAP_LUTIL_CSNSTR_BUFSIZE )
588			len = LDAP_LUTIL_CSNSTR_BUFSIZE;
589		if ( memcmp( a->a_nvals[0].bv_val, pd->csn.bv_val, len ) > 0 ) {
590			AC_MEMCPY( pd->csn.bv_val, a->a_nvals[0].bv_val, len );
591			pd->csn.bv_len = len;
592		}
593	}
594	if ( pd->used >= pd->slots ) {
595		pd->slots += PURGE_INCREMENT;
596		pd->dn = ch_realloc( pd->dn, pd->slots * sizeof( struct berval ));
597		pd->ndn = ch_realloc( pd->ndn, pd->slots * sizeof( struct berval ));
598	}
599	ber_dupbv( &pd->dn[pd->used], &rs->sr_entry->e_name );
600	ber_dupbv( &pd->ndn[pd->used], &rs->sr_entry->e_nname );
601	pd->used++;
602	return 0;
603}
604
605/* Periodically search for old entries in the log database and delete them */
606static void *
607accesslog_purge( void *ctx, void *arg )
608{
609	struct re_s *rtask = arg;
610	struct log_info *li = rtask->arg;
611
612	Connection conn = {0};
613	OperationBuffer opbuf;
614	Operation *op;
615	SlapReply rs = {REP_RESULT};
616	slap_callback cb = { NULL, log_old_lookup, NULL, NULL };
617	Filter f;
618	AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
619	purge_data pd = {0};
620	char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
621	char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
622	time_t old = slap_get_time();
623
624	connection_fake_init( &conn, &opbuf, ctx );
625	op = &opbuf.ob_op;
626
627	f.f_choice = LDAP_FILTER_LE;
628	f.f_ava = &ava;
629	f.f_next = NULL;
630
631	ava.aa_desc = ad_reqStart;
632	ava.aa_value.bv_val = timebuf;
633	ava.aa_value.bv_len = sizeof(timebuf);
634
635	old -= li->li_age;
636	slap_timestamp( &old, &ava.aa_value );
637
638	op->o_tag = LDAP_REQ_SEARCH;
639	op->o_bd = li->li_db;
640	op->o_dn = li->li_db->be_rootdn;
641	op->o_ndn = li->li_db->be_rootndn;
642	op->o_req_dn = li->li_db->be_suffix[0];
643	op->o_req_ndn = li->li_db->be_nsuffix[0];
644	op->o_callback = &cb;
645	op->ors_scope = LDAP_SCOPE_ONELEVEL;
646	op->ors_deref = LDAP_DEREF_NEVER;
647	op->ors_tlimit = SLAP_NO_LIMIT;
648	op->ors_slimit = SLAP_NO_LIMIT;
649	op->ors_filter = &f;
650	filter2bv_x( op, &f, &op->ors_filterstr );
651	op->ors_attrs = slap_anlist_no_attrs;
652	op->ors_attrsonly = 1;
653
654	pd.csn.bv_len = sizeof( csnbuf );
655	pd.csn.bv_val = csnbuf;
656	csnbuf[0] = '\0';
657	cb.sc_private = &pd;
658
659	op->o_bd->be_search( op, &rs );
660	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
661
662	if ( pd.used ) {
663		int i;
664
665		/* delete the expired entries */
666		op->o_tag = LDAP_REQ_DELETE;
667		op->o_callback = &nullsc;
668		op->o_csn = pd.csn;
669
670		for (i=0; i<pd.used; i++) {
671			op->o_req_dn = pd.dn[i];
672			op->o_req_ndn = pd.ndn[i];
673			if ( !slapd_shutdown )
674				op->o_bd->be_delete( op, &rs );
675			ch_free( pd.ndn[i].bv_val );
676			ch_free( pd.dn[i].bv_val );
677		}
678		ch_free( pd.ndn );
679		ch_free( pd.dn );
680
681		{
682			Modifications mod;
683			struct berval bv[2];
684			/* update context's entryCSN to reflect oldest CSN */
685			mod.sml_numvals = 1;
686			mod.sml_values = bv;
687			bv[0] = pd.csn;
688			BER_BVZERO(&bv[1]);
689			mod.sml_nvalues = NULL;
690			mod.sml_desc = slap_schema.si_ad_entryCSN;
691			mod.sml_op = LDAP_MOD_REPLACE;
692			mod.sml_flags = SLAP_MOD_INTERNAL;
693			mod.sml_next = NULL;
694
695			op->o_tag = LDAP_REQ_MODIFY;
696			op->orm_modlist = &mod;
697			op->orm_no_opattrs = 1;
698			op->o_req_dn = li->li_db->be_suffix[0];
699			op->o_req_ndn = li->li_db->be_nsuffix[0];
700			op->o_no_schema_check = 1;
701			op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
702			op->o_bd->be_modify( op, &rs );
703			if ( mod.sml_next ) {
704				slap_mods_free( mod.sml_next, 1 );
705			}
706		}
707	}
708
709	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
710	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
711	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
712
713	return NULL;
714}
715
716static int
717log_cf_gen(ConfigArgs *c)
718{
719	slap_overinst *on = (slap_overinst *)c->bi;
720	struct log_info *li = on->on_bi.bi_private;
721	int rc = 0;
722	slap_mask_t tmask = 0;
723	char agebuf[2*STRLENOF("ddddd+hh:mm:ss  ")];
724	struct berval agebv, cyclebv;
725
726	switch( c->op ) {
727	case SLAP_CONFIG_EMIT:
728		switch( c->type ) {
729		case LOG_DB:
730			if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
731				value_add_one( &c->rvalue_vals, &li->li_db_suffix );
732				value_add_one( &c->rvalue_nvals, &li->li_db_suffix );
733			} else if ( li->li_db ) {
734				value_add_one( &c->rvalue_vals, li->li_db->be_suffix );
735				value_add_one( &c->rvalue_nvals, li->li_db->be_nsuffix );
736			} else {
737				snprintf( c->cr_msg, sizeof( c->cr_msg ),
738					"accesslog: \"logdb <suffix>\" must be specified" );
739				Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
740					c->log, c->cr_msg, c->value_dn.bv_val );
741				rc = 1;
742				break;
743			}
744			break;
745		case LOG_OPS:
746			rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals );
747			break;
748		case LOG_PURGE:
749			if ( !li->li_age ) {
750				rc = 1;
751				break;
752			}
753			agebv.bv_val = agebuf;
754			log_age_unparse( li->li_age, &agebv, sizeof( agebuf ) );
755			agebv.bv_val[agebv.bv_len] = ' ';
756			agebv.bv_len++;
757			cyclebv.bv_val = agebv.bv_val + agebv.bv_len;
758			log_age_unparse( li->li_cycle, &cyclebv, sizeof( agebuf ) - agebv.bv_len );
759			agebv.bv_len += cyclebv.bv_len;
760			value_add_one( &c->rvalue_vals, &agebv );
761			break;
762		case LOG_SUCCESS:
763			if ( li->li_success )
764				c->value_int = li->li_success;
765			else
766				rc = 1;
767			break;
768		case LOG_OLD:
769			if ( li->li_oldf ) {
770				filter2bv( li->li_oldf, &agebv );
771				ber_bvarray_add( &c->rvalue_vals, &agebv );
772			}
773			else
774				rc = 1;
775			break;
776		case LOG_OLDATTR:
777			if ( li->li_oldattrs ) {
778				log_attr *la;
779
780				for ( la = li->li_oldattrs; la; la=la->next )
781					value_add_one( &c->rvalue_vals, &la->attr->ad_cname );
782			}
783			else
784				rc = 1;
785			break;
786		}
787		break;
788	case LDAP_MOD_DELETE:
789		switch( c->type ) {
790		case LOG_DB:
791			/* noop. this should always be a valid backend. */
792			break;
793		case LOG_OPS:
794			if ( c->valx < 0 ) {
795				li->li_ops = 0;
796			} else {
797				rc = verbs_to_mask( 1, &c->line, logops, &tmask );
798				if ( rc == 0 )
799					li->li_ops &= ~tmask;
800			}
801			break;
802		case LOG_PURGE:
803			if ( li->li_task ) {
804				struct re_s *re = li->li_task;
805				li->li_task = NULL;
806				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
807				if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ))
808					ldap_pvt_runqueue_stoptask( &slapd_rq, re );
809				ldap_pvt_runqueue_remove( &slapd_rq, re );
810				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
811			}
812			li->li_age = 0;
813			li->li_cycle = 0;
814			break;
815		case LOG_SUCCESS:
816			li->li_success = 0;
817			break;
818		case LOG_OLD:
819			if ( li->li_oldf ) {
820				filter_free( li->li_oldf );
821				li->li_oldf = NULL;
822			}
823			break;
824		case LOG_OLDATTR:
825			if ( c->valx < 0 ) {
826				log_attr *la, *ln;
827
828				for ( la = li->li_oldattrs; la; la = ln ) {
829					ln = la->next;
830					ch_free( la );
831				}
832			} else {
833				log_attr *la = NULL, **lp;
834				int i;
835
836				for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) {
837					la = *lp;
838					lp = &la->next;
839				}
840				*lp = la->next;
841				ch_free( la );
842			}
843			break;
844		}
845		break;
846	default:
847		switch( c->type ) {
848		case LOG_DB:
849			if ( CONFIG_ONLINE_ADD( c )) {
850				li->li_db = select_backend( &c->value_ndn, 0 );
851				if ( !li->li_db ) {
852					snprintf( c->cr_msg, sizeof( c->cr_msg ),
853						"<%s> no matching backend found for suffix",
854						c->argv[0] );
855					Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
856						c->log, c->cr_msg, c->value_dn.bv_val );
857					rc = 1;
858				}
859				ch_free( c->value_ndn.bv_val );
860			} else {
861				li->li_db_suffix = c->value_ndn;
862			}
863			ch_free( c->value_dn.bv_val );
864			break;
865		case LOG_OPS:
866			rc = verbs_to_mask( c->argc, c->argv, logops, &tmask );
867			if ( rc == 0 )
868				li->li_ops |= tmask;
869			break;
870		case LOG_PURGE:
871			li->li_age = log_age_parse( c->argv[1] );
872			if ( li->li_age < 1 ) {
873				rc = 1;
874			} else {
875				li->li_cycle = log_age_parse( c->argv[2] );
876				if ( li->li_cycle < 1 ) {
877					rc = 1;
878				} else if ( slapMode & SLAP_SERVER_MODE ) {
879					struct re_s *re = li->li_task;
880					if ( re )
881						re->interval.tv_sec = li->li_cycle;
882					else {
883						ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
884						li->li_task = ldap_pvt_runqueue_insert( &slapd_rq,
885							li->li_cycle, accesslog_purge, li,
886							"accesslog_purge", li->li_db ?
887								li->li_db->be_suffix[0].bv_val :
888								c->be->be_suffix[0].bv_val );
889						ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
890					}
891				}
892			}
893			break;
894		case LOG_SUCCESS:
895			li->li_success = c->value_int;
896			break;
897		case LOG_OLD:
898			li->li_oldf = str2filter( c->argv[1] );
899			if ( !li->li_oldf ) {
900				snprintf( c->cr_msg, sizeof( c->cr_msg ), "bad filter!" );
901				rc = 1;
902			}
903			break;
904		case LOG_OLDATTR: {
905			int i;
906			AttributeDescription *ad;
907			const char *text;
908
909			for ( i=1; i< c->argc; i++ ) {
910				ad = NULL;
911				if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) {
912					log_attr *la = ch_malloc( sizeof( log_attr ));
913					la->attr = ad;
914					la->next = li->li_oldattrs;
915					li->li_oldattrs = la;
916				} else {
917					snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s",
918						c->argv[0], c->argv[i], text );
919					Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
920						"%s: %s\n", c->log, c->cr_msg, 0 );
921					rc = ARG_BAD_CONF;
922					break;
923				}
924			}
925			}
926			break;
927		}
928		break;
929	}
930	return rc;
931}
932
933static int
934logSchemaControlValidate(
935	Syntax		*syntax,
936	struct berval	*valp )
937{
938	struct berval	val, bv;
939	ber_len_t		i;
940	int		rc = LDAP_SUCCESS;
941
942	assert( valp != NULL );
943
944	val = *valp;
945
946	/* check minimal size */
947	if ( val.bv_len < STRLENOF( "{*}" ) ) {
948		return LDAP_INVALID_SYNTAX;
949	}
950
951	val.bv_len--;
952
953	/* check SEQUENCE boundaries */
954	if ( val.bv_val[ 0 ] != '{' /*}*/ ||
955		val.bv_val[ val.bv_len ] != /*{*/ '}' )
956	{
957		return LDAP_INVALID_SYNTAX;
958	}
959
960	/* extract and check OID */
961	for ( i = 1; i < val.bv_len; i++ ) {
962		if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
963			break;
964		}
965	}
966
967	bv.bv_val = &val.bv_val[ i ];
968
969	for ( i++; i < val.bv_len; i++ ) {
970		if ( ASCII_SPACE( val.bv_val[ i ] ) )
971		{
972			break;
973		}
974	}
975
976	bv.bv_len = &val.bv_val[ i ] - bv.bv_val;
977
978	rc = numericoidValidate( NULL, &bv );
979	if ( rc != LDAP_SUCCESS ) {
980		return rc;
981	}
982
983	if ( i == val.bv_len ) {
984		return LDAP_SUCCESS;
985	}
986
987	if ( val.bv_val[ i ] != ' ' ) {
988		return LDAP_INVALID_SYNTAX;
989	}
990
991	for ( i++; i < val.bv_len; i++ ) {
992		if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
993			break;
994		}
995	}
996
997	if ( i == val.bv_len ) {
998		return LDAP_SUCCESS;
999	}
1000
1001	/* extract and check criticality */
1002	if ( strncasecmp( &val.bv_val[ i ], "criticality ", STRLENOF( "criticality " ) ) == 0 )
1003	{
1004		i += STRLENOF( "criticality " );
1005		for ( ; i < val.bv_len; i++ ) {
1006			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
1007				break;
1008			}
1009		}
1010
1011		if ( i == val.bv_len ) {
1012			return LDAP_INVALID_SYNTAX;
1013		}
1014
1015		bv.bv_val = &val.bv_val[ i ];
1016
1017		for ( ; i < val.bv_len; i++ ) {
1018			if ( ASCII_SPACE( val.bv_val[ i ] ) ) {
1019				break;
1020			}
1021		}
1022
1023		bv.bv_len = &val.bv_val[ i ] - bv.bv_val;
1024
1025		if ( !bvmatch( &bv, &slap_true_bv ) && !bvmatch( &bv, &slap_false_bv ) )
1026		{
1027			return LDAP_INVALID_SYNTAX;
1028		}
1029
1030		if ( i == val.bv_len ) {
1031			return LDAP_SUCCESS;
1032		}
1033
1034		if ( val.bv_val[ i ] != ' ' ) {
1035			return LDAP_INVALID_SYNTAX;
1036		}
1037
1038		for ( i++; i < val.bv_len; i++ ) {
1039			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
1040				break;
1041			}
1042		}
1043
1044		if ( i == val.bv_len ) {
1045			return LDAP_SUCCESS;
1046		}
1047	}
1048
1049	/* extract and check controlValue */
1050	if ( strncasecmp( &val.bv_val[ i ], "controlValue ", STRLENOF( "controlValue " ) ) == 0 )
1051	{
1052		i += STRLENOF( "controlValue " );
1053		for ( ; i < val.bv_len; i++ ) {
1054			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
1055				break;
1056			}
1057		}
1058
1059		if ( i == val.bv_len ) {
1060			return LDAP_INVALID_SYNTAX;
1061		}
1062
1063		if ( val.bv_val[ i ] != '"' ) {
1064			return LDAP_INVALID_SYNTAX;
1065		}
1066
1067		for ( ; i < val.bv_len; i++ ) {
1068			if ( val.bv_val[ i ] == '"' ) {
1069				break;
1070			}
1071
1072			if ( !ASCII_HEX( val.bv_val[ i ] ) ) {
1073				return LDAP_INVALID_SYNTAX;
1074			}
1075		}
1076
1077		if ( val.bv_val[ i ] != '"' ) {
1078			return LDAP_INVALID_SYNTAX;
1079		}
1080
1081		for ( ; i < val.bv_len; i++ ) {
1082			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
1083				break;
1084			}
1085		}
1086
1087		if ( i == val.bv_len ) {
1088			return LDAP_SUCCESS;
1089		}
1090	}
1091
1092	return LDAP_INVALID_SYNTAX;
1093}
1094
1095static int
1096accesslog_ctrls(
1097	LDAPControl **ctrls,
1098	BerVarray *valsp,
1099	BerVarray *nvalsp,
1100	void *memctx )
1101{
1102	long		i, rc = 0;
1103
1104	assert( valsp != NULL );
1105	assert( ctrls != NULL );
1106
1107	*valsp = NULL;
1108	*nvalsp = NULL;
1109
1110	for ( i = 0; ctrls[ i ] != NULL; i++ ) {
1111		struct berval	idx,
1112				oid,
1113				noid,
1114				bv;
1115		char		*ptr,
1116				buf[ 32 ];
1117
1118		if ( ctrls[ i ]->ldctl_oid == NULL ) {
1119			return LDAP_PROTOCOL_ERROR;
1120		}
1121
1122		idx.bv_len = snprintf( buf, sizeof( buf ), "{%ld}", i );
1123		idx.bv_val = buf;
1124
1125		ber_str2bv( ctrls[ i ]->ldctl_oid, 0, 0, &oid );
1126		noid.bv_len = idx.bv_len + oid.bv_len;
1127		ptr = noid.bv_val = ber_memalloc_x( noid.bv_len + 1, memctx );
1128		ptr = lutil_strcopy( ptr, idx.bv_val );
1129		ptr = lutil_strcopy( ptr, oid.bv_val );
1130
1131		bv.bv_len = idx.bv_len + STRLENOF( "{}" ) + oid.bv_len;
1132
1133		if ( ctrls[ i ]->ldctl_iscritical ) {
1134			bv.bv_len += STRLENOF( " criticality TRUE" );
1135		}
1136
1137		if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
1138			bv.bv_len += STRLENOF( " controlValue \"\"" )
1139				+ 2 * ctrls[ i ]->ldctl_value.bv_len;
1140		}
1141
1142		ptr = bv.bv_val = ber_memalloc_x( bv.bv_len + 1, memctx );
1143		if ( ptr == NULL ) {
1144			ber_bvarray_free( *valsp );
1145			*valsp = NULL;
1146			ber_bvarray_free( *nvalsp );
1147			*nvalsp = NULL;
1148			return LDAP_OTHER;
1149		}
1150
1151		ptr = lutil_strcopy( ptr, idx.bv_val );
1152
1153		*ptr++ = '{' /*}*/ ;
1154		ptr = lutil_strcopy( ptr, oid.bv_val );
1155
1156		if ( ctrls[ i ]->ldctl_iscritical ) {
1157			ptr = lutil_strcopy( ptr, " criticality TRUE" );
1158		}
1159
1160		if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
1161			ber_len_t	j;
1162
1163			ptr = lutil_strcopy( ptr, " controlValue \"" );
1164			for ( j = 0; j < ctrls[ i ]->ldctl_value.bv_len; j++ )
1165			{
1166				unsigned char	o;
1167
1168				o = ( ( ctrls[ i ]->ldctl_value.bv_val[ j ] >> 4 ) & 0xF );
1169				if ( o < 10 ) {
1170					*ptr++ = '0' + o;
1171
1172				} else {
1173					*ptr++ = 'A' + o;
1174				}
1175
1176				o = ( ctrls[ i ]->ldctl_value.bv_val[ j ] & 0xF );
1177				if ( o < 10 ) {
1178					*ptr++ = '0' + o;
1179
1180				} else {
1181					*ptr++ = 'A' + o;
1182				}
1183			}
1184
1185			*ptr++ = '"';
1186		}
1187
1188		*ptr++ = '}';
1189		*ptr = '\0';
1190
1191		ber_bvarray_add_x( valsp, &bv, memctx );
1192		ber_bvarray_add_x( nvalsp, &noid, memctx );
1193	}
1194
1195	return rc;
1196
1197}
1198
1199static Entry *accesslog_entry( Operation *op, SlapReply *rs, int logop,
1200	Operation *op2 ) {
1201	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1202	log_info *li = on->on_bi.bi_private;
1203
1204	char rdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
1205	char nrdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
1206
1207	struct berval rdn, nrdn, timestamp, ntimestamp, bv;
1208	slap_verbmasks *lo = logops+logop+EN_OFFSET;
1209
1210	Entry *e = entry_alloc();
1211
1212	strcpy( rdnbuf, RDNEQ );
1213	rdn.bv_val = rdnbuf;
1214	strcpy( nrdnbuf, RDNEQ );
1215	nrdn.bv_val = nrdnbuf;
1216
1217	timestamp.bv_val = rdnbuf+STRLENOF(RDNEQ);
1218	timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
1219	slap_timestamp( &op->o_time, &timestamp );
1220	snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op->o_tincr );
1221	timestamp.bv_len += STRLENOF(".123456");
1222
1223	rdn.bv_len = STRLENOF(RDNEQ)+timestamp.bv_len;
1224	ad_reqStart->ad_type->sat_equality->smr_normalize(
1225		SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, ad_reqStart->ad_type->sat_syntax,
1226		ad_reqStart->ad_type->sat_equality, &timestamp, &ntimestamp,
1227		op->o_tmpmemctx );
1228
1229	strcpy( nrdn.bv_val + STRLENOF(RDNEQ), ntimestamp.bv_val );
1230	nrdn.bv_len = STRLENOF(RDNEQ)+ntimestamp.bv_len;
1231	build_new_dn( &e->e_name, li->li_db->be_suffix, &rdn, NULL );
1232	build_new_dn( &e->e_nname, li->li_db->be_nsuffix, &nrdn, NULL );
1233
1234	attr_merge_one( e, slap_schema.si_ad_objectClass,
1235		&log_ocs[logop]->soc_cname, NULL );
1236	attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
1237		&log_ocs[logop]->soc_cname, NULL );
1238	attr_merge_one( e, ad_reqStart, &timestamp, &ntimestamp );
1239	op->o_tmpfree( ntimestamp.bv_val, op->o_tmpmemctx );
1240
1241	slap_op_time( &op2->o_time, &op2->o_tincr );
1242
1243	timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
1244	slap_timestamp( &op2->o_time, &timestamp );
1245	snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op2->o_tincr );
1246	timestamp.bv_len += STRLENOF(".123456");
1247
1248	attr_merge_normalize_one( e, ad_reqEnd, &timestamp, op->o_tmpmemctx );
1249
1250	/* Exops have OID appended */
1251	if ( logop == LOG_EN_EXTENDED ) {
1252		bv.bv_len = lo->word.bv_len + op->ore_reqoid.bv_len + 2;
1253		bv.bv_val = ch_malloc( bv.bv_len + 1 );
1254		AC_MEMCPY( bv.bv_val, lo->word.bv_val, lo->word.bv_len );
1255		bv.bv_val[lo->word.bv_len] = '{';
1256		AC_MEMCPY( bv.bv_val+lo->word.bv_len+1, op->ore_reqoid.bv_val,
1257			op->ore_reqoid.bv_len );
1258		bv.bv_val[bv.bv_len-1] = '}';
1259		bv.bv_val[bv.bv_len] = '\0';
1260		attr_merge_one( e, ad_reqType, &bv, NULL );
1261	} else {
1262		attr_merge_one( e, ad_reqType, &lo->word, NULL );
1263	}
1264
1265	rdn.bv_len = snprintf( rdn.bv_val, sizeof( rdnbuf ), "%lu", op->o_connid );
1266	if ( rdn.bv_len >= 0 || rdn.bv_len < sizeof( rdnbuf ) ) {
1267		attr_merge_one( e, ad_reqSession, &rdn, NULL );
1268	} /* else? */
1269
1270	if ( BER_BVISNULL( &op->o_dn ) ) {
1271		attr_merge_one( e, ad_reqAuthzID, (struct berval *)&slap_empty_bv,
1272			(struct berval *)&slap_empty_bv );
1273	} else {
1274		attr_merge_one( e, ad_reqAuthzID, &op->o_dn, &op->o_ndn );
1275	}
1276
1277	/* FIXME: need to add reqControls and reqRespControls */
1278	if ( op->o_ctrls ) {
1279		BerVarray	vals = NULL,
1280				nvals = NULL;
1281
1282		if ( accesslog_ctrls( op->o_ctrls, &vals, &nvals,
1283			op->o_tmpmemctx ) == LDAP_SUCCESS && vals )
1284		{
1285			attr_merge( e, ad_reqControls, vals, nvals );
1286			ber_bvarray_free_x( vals, op->o_tmpmemctx );
1287			ber_bvarray_free_x( nvals, op->o_tmpmemctx );
1288		}
1289	}
1290
1291	if ( rs->sr_ctrls ) {
1292		BerVarray	vals = NULL,
1293				nvals = NULL;
1294
1295		if ( accesslog_ctrls( rs->sr_ctrls, &vals, &nvals,
1296			op->o_tmpmemctx ) == LDAP_SUCCESS && vals )
1297		{
1298			attr_merge( e, ad_reqRespControls, vals, nvals );
1299			ber_bvarray_free_x( vals, op->o_tmpmemctx );
1300			ber_bvarray_free_x( nvals, op->o_tmpmemctx );
1301		}
1302
1303	}
1304
1305	return e;
1306}
1307
1308static struct berval scopes[] = {
1309	BER_BVC("base"),
1310	BER_BVC("one"),
1311	BER_BVC("sub"),
1312	BER_BVC("subord")
1313};
1314
1315static struct berval derefs[] = {
1316	BER_BVC("never"),
1317	BER_BVC("searching"),
1318	BER_BVC("finding"),
1319	BER_BVC("always")
1320};
1321
1322static struct berval simple = BER_BVC("SIMPLE");
1323
1324static void accesslog_val2val(AttributeDescription *ad, struct berval *val,
1325	char c_op, struct berval *dst) {
1326	char *ptr;
1327
1328	dst->bv_len = ad->ad_cname.bv_len + val->bv_len + 2;
1329	if ( c_op ) dst->bv_len++;
1330
1331	dst->bv_val = ch_malloc( dst->bv_len+1 );
1332
1333	ptr = lutil_strcopy( dst->bv_val, ad->ad_cname.bv_val );
1334	*ptr++ = ':';
1335	if ( c_op )
1336		*ptr++ = c_op;
1337	*ptr++ = ' ';
1338	AC_MEMCPY( ptr, val->bv_val, val->bv_len );
1339	dst->bv_val[dst->bv_len] = '\0';
1340}
1341
1342static int accesslog_response(Operation *op, SlapReply *rs) {
1343	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1344	log_info *li = on->on_bi.bi_private;
1345	Attribute *a, *last_attr;
1346	Modifications *m;
1347	struct berval *b;
1348	int i;
1349	int logop;
1350	slap_verbmasks *lo;
1351	Entry *e = NULL, *old = NULL;
1352	char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE+8];
1353	struct berval bv;
1354	char *ptr;
1355	BerVarray vals;
1356	Operation op2 = {0};
1357	SlapReply rs2 = {REP_RESULT};
1358
1359	if ( rs->sr_type != REP_RESULT && rs->sr_type != REP_EXTENDED )
1360		return SLAP_CB_CONTINUE;
1361
1362	switch ( op->o_tag ) {
1363	case LDAP_REQ_ADD:		logop = LOG_EN_ADD; break;
1364	case LDAP_REQ_DELETE:	logop = LOG_EN_DELETE; break;
1365	case LDAP_REQ_MODIFY:	logop = LOG_EN_MODIFY; break;
1366	case LDAP_REQ_MODRDN:	logop = LOG_EN_MODRDN; break;
1367	case LDAP_REQ_COMPARE:	logop = LOG_EN_COMPARE; break;
1368	case LDAP_REQ_SEARCH:	logop = LOG_EN_SEARCH; break;
1369	case LDAP_REQ_BIND:		logop = LOG_EN_BIND; break;
1370	case LDAP_REQ_EXTENDED:	logop = LOG_EN_EXTENDED; break;
1371	default:	/* unknown operation type */
1372		logop = LOG_EN_UNKNOWN; break;
1373	}	/* Unbind and Abandon never reach here */
1374
1375	lo = logops+logop+EN_OFFSET;
1376	if ( !( li->li_ops & lo->mask ))
1377		return SLAP_CB_CONTINUE;
1378
1379	if ( lo->mask & LOG_OP_WRITES ) {
1380		slap_callback *cb;
1381		ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
1382		old = li->li_old;
1383		li->li_old = NULL;
1384		/* Disarm mod_cleanup */
1385		for ( cb = op->o_callback; cb; cb = cb->sc_next ) {
1386			if ( cb->sc_private == (void *)on ) {
1387				cb->sc_private = NULL;
1388				break;
1389			}
1390		}
1391		ldap_pvt_thread_rmutex_unlock( &li->li_op_rmutex, op->o_tid );
1392	}
1393
1394	if ( li->li_success && rs->sr_err != LDAP_SUCCESS )
1395		goto done;
1396
1397	e = accesslog_entry( op, rs, logop, &op2 );
1398
1399	attr_merge_one( e, ad_reqDN, &op->o_req_dn, &op->o_req_ndn );
1400
1401	if ( rs->sr_text ) {
1402		ber_str2bv( rs->sr_text, 0, 0, &bv );
1403		attr_merge_one( e, ad_reqMessage, &bv, NULL );
1404	}
1405	bv.bv_len = snprintf( timebuf, sizeof( timebuf ), "%d", rs->sr_err );
1406	if ( bv.bv_len < sizeof( timebuf ) ) {
1407		bv.bv_val = timebuf;
1408		attr_merge_one( e, ad_reqResult, &bv, NULL );
1409	}
1410
1411	last_attr = attr_find( e->e_attrs, ad_reqResult );
1412
1413	switch( logop ) {
1414	case LOG_EN_ADD:
1415	case LOG_EN_DELETE: {
1416		char c_op;
1417		Entry *e2;
1418
1419		if ( logop == LOG_EN_ADD ) {
1420			e2 = op->ora_e;
1421			c_op = '+';
1422		} else {
1423			if ( !old )
1424				break;
1425			e2 = old;
1426			c_op = 0;
1427		}
1428		/* count all the vals */
1429		i = 0;
1430		for ( a=e2->e_attrs; a; a=a->a_next ) {
1431			i += a->a_numvals;
1432		}
1433		vals = ch_malloc( (i+1) * sizeof( struct berval ));
1434		i = 0;
1435		for ( a=e2->e_attrs; a; a=a->a_next ) {
1436			if ( a->a_vals ) {
1437				for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
1438					accesslog_val2val( a->a_desc, b, c_op, &vals[i] );
1439				}
1440			}
1441		}
1442		vals[i].bv_val = NULL;
1443		vals[i].bv_len = 0;
1444		a = attr_alloc( logop == LOG_EN_ADD ? ad_reqMod : ad_reqOld );
1445		a->a_numvals = i;
1446		a->a_vals = vals;
1447		a->a_nvals = vals;
1448		last_attr->a_next = a;
1449		break;
1450	}
1451
1452	case LOG_EN_MODRDN:
1453	case LOG_EN_MODIFY:
1454		/* count all the mods */
1455		i = 0;
1456		for ( m = op->orm_modlist; m; m = m->sml_next ) {
1457			if ( m->sml_values ) {
1458				i += m->sml_numvals;
1459			} else if ( m->sml_op == LDAP_MOD_DELETE ||
1460				m->sml_op == LDAP_MOD_REPLACE )
1461			{
1462				i++;
1463			}
1464		}
1465		vals = ch_malloc( (i+1) * sizeof( struct berval ));
1466		i = 0;
1467
1468		/* init flags on old entry */
1469		if ( old ) {
1470			for ( a = old->e_attrs; a; a = a->a_next ) {
1471				log_attr *la;
1472				a->a_flags = 0;
1473
1474				/* look for attrs that are always logged */
1475				for ( la = li->li_oldattrs; la; la = la->next ) {
1476					if ( a->a_desc == la->attr ) {
1477						a->a_flags = 1;
1478					}
1479				}
1480			}
1481		}
1482
1483		for ( m = op->orm_modlist; m; m = m->sml_next ) {
1484			/* Mark this attribute as modified */
1485			if ( old ) {
1486				a = attr_find( old->e_attrs, m->sml_desc );
1487				if ( a ) {
1488					a->a_flags = 1;
1489				}
1490			}
1491
1492			/* don't log the RDN mods; they're explicitly logged later */
1493			if ( logop == LOG_EN_MODRDN &&
1494			 	( m->sml_op == SLAP_MOD_SOFTADD ||
1495				  m->sml_op == LDAP_MOD_DELETE ) )
1496			{
1497				continue;
1498			}
1499
1500			if ( m->sml_values ) {
1501				for ( b = m->sml_values; !BER_BVISNULL( b ); b++, i++ ) {
1502					char c_op;
1503
1504					switch ( m->sml_op ) {
1505					case LDAP_MOD_ADD: c_op = '+'; break;
1506					case LDAP_MOD_DELETE:	c_op = '-'; break;
1507					case LDAP_MOD_REPLACE:	c_op = '='; break;
1508					case LDAP_MOD_INCREMENT:	c_op = '#'; break;
1509
1510					/* unknown op. there shouldn't be any of these. we
1511					 * don't know what to do with it, but we shouldn't just
1512					 * ignore it.
1513					 */
1514					default: c_op = '?'; break;
1515					}
1516					accesslog_val2val( m->sml_desc, b, c_op, &vals[i] );
1517				}
1518			} else if ( m->sml_op == LDAP_MOD_DELETE ||
1519				m->sml_op == LDAP_MOD_REPLACE )
1520			{
1521				vals[i].bv_len = m->sml_desc->ad_cname.bv_len + 2;
1522				vals[i].bv_val = ch_malloc( vals[i].bv_len + 1 );
1523				ptr = lutil_strcopy( vals[i].bv_val,
1524					m->sml_desc->ad_cname.bv_val );
1525				*ptr++ = ':';
1526				if ( m->sml_op == LDAP_MOD_DELETE ) {
1527					*ptr++ = '-';
1528				} else {
1529					*ptr++ = '=';
1530				}
1531				*ptr = '\0';
1532				i++;
1533			}
1534		}
1535
1536		if ( i > 0 ) {
1537			BER_BVZERO( &vals[i] );
1538			a = attr_alloc( ad_reqMod );
1539			a->a_numvals = i;
1540			a->a_vals = vals;
1541			a->a_nvals = vals;
1542			last_attr->a_next = a;
1543			last_attr = a;
1544
1545		} else {
1546			ch_free( vals );
1547		}
1548
1549		if ( old ) {
1550			/* count all the vals */
1551			i = 0;
1552			for ( a = old->e_attrs; a != NULL; a = a->a_next ) {
1553				if ( a->a_vals && a->a_flags ) {
1554					i += a->a_numvals;
1555				}
1556			}
1557			if ( i ) {
1558				vals = ch_malloc( (i + 1) * sizeof( struct berval ) );
1559				i = 0;
1560				for ( a=old->e_attrs; a; a=a->a_next ) {
1561					if ( a->a_vals && a->a_flags ) {
1562						for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
1563							accesslog_val2val( a->a_desc, b, 0, &vals[i] );
1564						}
1565					}
1566				}
1567				vals[i].bv_val = NULL;
1568				vals[i].bv_len = 0;
1569				a = attr_alloc( ad_reqOld );
1570				a->a_numvals = i;
1571				a->a_vals = vals;
1572				a->a_nvals = vals;
1573				last_attr->a_next = a;
1574			}
1575		}
1576		if ( logop == LOG_EN_MODIFY ) {
1577			break;
1578		}
1579
1580		/* Now log the actual modRDN info */
1581		attr_merge_one( e, ad_reqNewRDN, &op->orr_newrdn, &op->orr_nnewrdn );
1582		attr_merge_one( e, ad_reqDeleteOldRDN, op->orr_deleteoldrdn ?
1583			(struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
1584			NULL );
1585		if ( op->orr_newSup ) {
1586			attr_merge_one( e, ad_reqNewSuperior, op->orr_newSup, op->orr_nnewSup );
1587		}
1588		break;
1589
1590	case LOG_EN_COMPARE:
1591		bv.bv_len = op->orc_ava->aa_desc->ad_cname.bv_len + 1 +
1592			op->orc_ava->aa_value.bv_len;
1593		bv.bv_val = op->o_tmpalloc( bv.bv_len+1, op->o_tmpmemctx );
1594		ptr = lutil_strcopy( bv.bv_val, op->orc_ava->aa_desc->ad_cname.bv_val );
1595		*ptr++ = '=';
1596		AC_MEMCPY( ptr, op->orc_ava->aa_value.bv_val, op->orc_ava->aa_value.bv_len );
1597		bv.bv_val[bv.bv_len] = '\0';
1598		attr_merge_one( e, ad_reqAssertion, &bv, NULL );
1599		op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
1600		break;
1601
1602	case LOG_EN_SEARCH:
1603		attr_merge_one( e, ad_reqScope, &scopes[op->ors_scope], NULL );
1604		attr_merge_one( e, ad_reqDerefAliases, &derefs[op->ors_deref], NULL );
1605		attr_merge_one( e, ad_reqAttrsOnly, op->ors_attrsonly ?
1606			(struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
1607			NULL );
1608		if ( !BER_BVISEMPTY( &op->ors_filterstr ))
1609			attr_merge_one( e, ad_reqFilter, &op->ors_filterstr, NULL );
1610		if ( op->ors_attrs ) {
1611			/* count them */
1612			for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++)
1613				;
1614			vals = op->o_tmpalloc( (i+1) * sizeof(struct berval),
1615				op->o_tmpmemctx );
1616			for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++)
1617				vals[i] = op->ors_attrs[i].an_name;
1618			vals[i].bv_val = NULL;
1619			vals[i].bv_len = 0;
1620			attr_merge( e, ad_reqAttr, vals, NULL );
1621			op->o_tmpfree( vals, op->o_tmpmemctx );
1622		}
1623		bv.bv_val = timebuf;
1624		bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", rs->sr_nentries );
1625		if ( bv.bv_len < sizeof( timebuf ) ) {
1626			attr_merge_one( e, ad_reqEntries, &bv, NULL );
1627		} /* else? */
1628
1629		bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_tlimit );
1630		if ( bv.bv_len < sizeof( timebuf ) ) {
1631			attr_merge_one( e, ad_reqTimeLimit, &bv, NULL );
1632		} /* else? */
1633
1634		bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_slimit );
1635		if ( bv.bv_len < sizeof( timebuf ) ) {
1636			attr_merge_one( e, ad_reqSizeLimit, &bv, NULL );
1637		} /* else? */
1638		break;
1639
1640	case LOG_EN_BIND:
1641		bv.bv_val = timebuf;
1642		bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->o_protocol );
1643		if ( bv.bv_len < sizeof( timebuf ) ) {
1644			attr_merge_one( e, ad_reqVersion, &bv, NULL );
1645		} /* else? */
1646		if ( op->orb_method == LDAP_AUTH_SIMPLE ) {
1647			attr_merge_one( e, ad_reqMethod, &simple, NULL );
1648		} else {
1649			bv.bv_len = STRLENOF("SASL()") + op->orb_mech.bv_len;
1650			bv.bv_val = op->o_tmpalloc( bv.bv_len + 1, op->o_tmpmemctx );
1651			ptr = lutil_strcopy( bv.bv_val, "SASL(" );
1652			ptr = lutil_strcopy( ptr, op->orb_mech.bv_val );
1653			*ptr++ = ')';
1654			*ptr = '\0';
1655			attr_merge_one( e, ad_reqMethod, &bv, NULL );
1656			op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
1657		}
1658
1659		break;
1660
1661	case LOG_EN_EXTENDED:
1662		if ( op->ore_reqdata ) {
1663			attr_merge_one( e, ad_reqData, op->ore_reqdata, NULL );
1664		}
1665		break;
1666
1667	case LOG_EN_UNKNOWN:
1668		/* we don't know its parameters, don't add any */
1669		break;
1670	}
1671
1672	op2.o_hdr = op->o_hdr;
1673	op2.o_tag = LDAP_REQ_ADD;
1674	op2.o_bd = li->li_db;
1675	op2.o_dn = li->li_db->be_rootdn;
1676	op2.o_ndn = li->li_db->be_rootndn;
1677	op2.o_req_dn = e->e_name;
1678	op2.o_req_ndn = e->e_nname;
1679	op2.ora_e = e;
1680	op2.o_callback = &nullsc;
1681
1682	if (( lo->mask & LOG_OP_WRITES ) && !BER_BVISEMPTY( &op->o_csn )) {
1683		slap_queue_csn( &op2, &op->o_csn );
1684	}
1685
1686	op2.o_bd->be_add( &op2, &rs2 );
1687	if ( e == op2.ora_e ) entry_free( e );
1688	e = NULL;
1689
1690done:
1691	if ( lo->mask & LOG_OP_WRITES )
1692		ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
1693	if ( old ) entry_free( old );
1694	return SLAP_CB_CONTINUE;
1695}
1696
1697/* Since Bind success is sent by the frontend, it won't normally enter
1698 * the overlay response callback. Add another callback to make sure it
1699 * gets here.
1700 */
1701static int
1702accesslog_bind_resp( Operation *op, SlapReply *rs )
1703{
1704	BackendDB *be, db;
1705	int rc;
1706	slap_callback *sc;
1707
1708	be = op->o_bd;
1709	db = *be;
1710	op->o_bd = &db;
1711	db.bd_info = op->o_callback->sc_private;
1712	rc = accesslog_response( op, rs );
1713	op->o_bd = be;
1714	sc = op->o_callback;
1715	op->o_callback = sc->sc_next;
1716	op->o_tmpfree( sc, op->o_tmpmemctx );
1717	return rc;
1718}
1719
1720static int
1721accesslog_op_bind( Operation *op, SlapReply *rs )
1722{
1723	slap_callback *sc;
1724
1725	sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
1726	sc->sc_response = accesslog_bind_resp;
1727	sc->sc_private = op->o_bd->bd_info;
1728
1729	if ( op->o_callback ) {
1730		sc->sc_next = op->o_callback->sc_next;
1731		op->o_callback->sc_next = sc;
1732	} else {
1733		op->o_callback = sc;
1734	}
1735	return SLAP_CB_CONTINUE;
1736}
1737
1738static int
1739accesslog_mod_cleanup( Operation *op, SlapReply *rs )
1740{
1741	slap_callback *sc = op->o_callback;
1742	slap_overinst *on = sc->sc_private;
1743	op->o_callback = sc->sc_next;
1744
1745	op->o_tmpfree( sc, op->o_tmpmemctx );
1746
1747	if ( on ) {
1748		BackendInfo *bi = op->o_bd->bd_info;
1749		op->o_bd->bd_info = (BackendInfo *)on;
1750		accesslog_response( op, rs );
1751		op->o_bd->bd_info = bi;
1752	}
1753	return 0;
1754}
1755
1756static int
1757accesslog_op_mod( Operation *op, SlapReply *rs )
1758{
1759	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1760	log_info *li = on->on_bi.bi_private;
1761
1762	if ( li->li_ops & LOG_OP_WRITES ) {
1763		slap_callback *cb = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx ), *cb2;
1764		cb->sc_cleanup = accesslog_mod_cleanup;
1765		cb->sc_response = NULL;
1766		cb->sc_private = on;
1767		cb->sc_next = NULL;
1768		for ( cb2 = op->o_callback; cb2->sc_next; cb2 = cb2->sc_next );
1769		cb2->sc_next = cb;
1770
1771		ldap_pvt_thread_rmutex_lock( &li->li_op_rmutex, op->o_tid );
1772		if ( li->li_oldf && ( op->o_tag == LDAP_REQ_DELETE ||
1773			op->o_tag == LDAP_REQ_MODIFY ||
1774			( op->o_tag == LDAP_REQ_MODRDN && li->li_oldattrs ))) {
1775			int rc;
1776			Entry *e;
1777
1778			op->o_bd->bd_info = (BackendInfo *)on->on_info;
1779			rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1780			if ( e ) {
1781				if ( test_filter( op, e, li->li_oldf ) == LDAP_COMPARE_TRUE )
1782					li->li_old = entry_dup( e );
1783				be_entry_release_rw( op, e, 0 );
1784			}
1785			op->o_bd->bd_info = (BackendInfo *)on;
1786		}
1787	}
1788	return SLAP_CB_CONTINUE;
1789}
1790
1791/* unbinds are broadcast to all backends; we only log it if this
1792 * backend was used for the original bind.
1793 */
1794static int
1795accesslog_unbind( Operation *op, SlapReply *rs )
1796{
1797	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1798	if ( op->o_conn->c_authz_backend == on->on_info->oi_origdb ) {
1799		log_info *li = on->on_bi.bi_private;
1800		Operation op2 = {0};
1801		void *cids[SLAP_MAX_CIDS];
1802		SlapReply rs2 = {REP_RESULT};
1803		Entry *e;
1804
1805		if ( !( li->li_ops & LOG_OP_UNBIND ))
1806			return SLAP_CB_CONTINUE;
1807
1808		e = accesslog_entry( op, rs, LOG_EN_UNBIND, &op2 );
1809		op2.o_hdr = op->o_hdr;
1810		op2.o_tag = LDAP_REQ_ADD;
1811		op2.o_bd = li->li_db;
1812		op2.o_dn = li->li_db->be_rootdn;
1813		op2.o_ndn = li->li_db->be_rootndn;
1814		op2.o_req_dn = e->e_name;
1815		op2.o_req_ndn = e->e_nname;
1816		op2.ora_e = e;
1817		op2.o_callback = &nullsc;
1818		op2.o_controls = cids;
1819		memset(cids, 0, sizeof( cids ));
1820
1821		op2.o_bd->be_add( &op2, &rs2 );
1822		if ( e == op2.ora_e )
1823			entry_free( e );
1824	}
1825	return SLAP_CB_CONTINUE;
1826}
1827
1828static int
1829accesslog_abandon( Operation *op, SlapReply *rs )
1830{
1831	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1832	log_info *li = on->on_bi.bi_private;
1833	Operation op2 = {0};
1834	void *cids[SLAP_MAX_CIDS];
1835	SlapReply rs2 = {REP_RESULT};
1836	Entry *e;
1837	char buf[64];
1838	struct berval bv;
1839
1840	if ( !op->o_time || !( li->li_ops & LOG_OP_ABANDON ))
1841		return SLAP_CB_CONTINUE;
1842
1843	e = accesslog_entry( op, rs, LOG_EN_ABANDON, &op2 );
1844	bv.bv_val = buf;
1845	bv.bv_len = snprintf( buf, sizeof( buf ), "%d", op->orn_msgid );
1846	if ( bv.bv_len < sizeof( buf ) ) {
1847		attr_merge_one( e, ad_reqId, &bv, NULL );
1848	} /* else? */
1849
1850	op2.o_hdr = op->o_hdr;
1851	op2.o_tag = LDAP_REQ_ADD;
1852	op2.o_bd = li->li_db;
1853	op2.o_dn = li->li_db->be_rootdn;
1854	op2.o_ndn = li->li_db->be_rootndn;
1855	op2.o_req_dn = e->e_name;
1856	op2.o_req_ndn = e->e_nname;
1857	op2.ora_e = e;
1858	op2.o_callback = &nullsc;
1859	op2.o_controls = cids;
1860	memset(cids, 0, sizeof( cids ));
1861
1862	op2.o_bd->be_add( &op2, &rs2 );
1863	if ( e == op2.ora_e )
1864		entry_free( e );
1865
1866	return SLAP_CB_CONTINUE;
1867}
1868
1869static int
1870accesslog_operational( Operation *op, SlapReply *rs )
1871{
1872	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1873	log_info *li = on->on_bi.bi_private;
1874
1875	if ( op->o_sync != SLAP_CONTROL_NONE )
1876		return SLAP_CB_CONTINUE;
1877
1878	if ( rs->sr_entry != NULL
1879		&& dn_match( &op->o_bd->be_nsuffix[0], &rs->sr_entry->e_nname ) )
1880	{
1881		Attribute	**ap;
1882
1883		for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
1884			/* just count */ ;
1885
1886		if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
1887				ad_inlist( ad_auditContext, rs->sr_attrs ) )
1888		{
1889			*ap = attr_alloc( ad_auditContext );
1890			attr_valadd( *ap,
1891				&li->li_db->be_suffix[0],
1892				&li->li_db->be_nsuffix[0], 1 );
1893		}
1894	}
1895
1896	return SLAP_CB_CONTINUE;
1897}
1898
1899static slap_overinst accesslog;
1900
1901static int
1902accesslog_db_init(
1903	BackendDB *be,
1904	ConfigReply *cr
1905)
1906{
1907	slap_overinst *on = (slap_overinst *)be->bd_info;
1908	log_info *li = ch_calloc(1, sizeof(log_info));
1909
1910	on->on_bi.bi_private = li;
1911	ldap_pvt_thread_rmutex_init( &li->li_op_rmutex );
1912	ldap_pvt_thread_mutex_init( &li->li_log_mutex );
1913	return 0;
1914}
1915
1916static int
1917accesslog_db_destroy(
1918	BackendDB *be,
1919	ConfigReply *cr
1920)
1921{
1922	slap_overinst *on = (slap_overinst *)be->bd_info;
1923	log_info *li = on->on_bi.bi_private;
1924	log_attr *la;
1925
1926	if ( li->li_oldf )
1927		filter_free( li->li_oldf );
1928	for ( la=li->li_oldattrs; la; la=li->li_oldattrs ) {
1929		li->li_oldattrs = la->next;
1930		ch_free( la );
1931	}
1932	ldap_pvt_thread_mutex_destroy( &li->li_log_mutex );
1933	ldap_pvt_thread_rmutex_destroy( &li->li_op_rmutex );
1934	free( li );
1935	return LDAP_SUCCESS;
1936}
1937
1938/* Create the logdb's root entry if it's missing */
1939static void *
1940accesslog_db_root(
1941	void *ctx,
1942	void *arg )
1943{
1944	struct re_s *rtask = arg;
1945	slap_overinst *on = rtask->arg;
1946	log_info *li = on->on_bi.bi_private;
1947
1948	Connection conn = {0};
1949	OperationBuffer opbuf;
1950	Operation *op;
1951
1952	Entry *e;
1953	int rc;
1954
1955	connection_fake_init( &conn, &opbuf, ctx );
1956	op = &opbuf.ob_op;
1957	op->o_bd = li->li_db;
1958	op->o_dn = li->li_db->be_rootdn;
1959	op->o_ndn = li->li_db->be_rootndn;
1960	rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e );
1961
1962	if ( e ) {
1963		be_entry_release_rw( op, e, 0 );
1964
1965	} else {
1966		SlapReply rs = {REP_RESULT};
1967		struct berval rdn, nrdn, attr;
1968		char *ptr;
1969		AttributeDescription *ad = NULL;
1970		const char *text = NULL;
1971		Entry *e_ctx;
1972
1973		e = entry_alloc();
1974		ber_dupbv( &e->e_name, li->li_db->be_suffix );
1975		ber_dupbv( &e->e_nname, li->li_db->be_nsuffix );
1976
1977		attr_merge_one( e, slap_schema.si_ad_objectClass,
1978			&log_container->soc_cname, NULL );
1979
1980		dnRdn( &e->e_name, &rdn );
1981		dnRdn( &e->e_nname, &nrdn );
1982		ptr = ber_bvchr( &rdn, '=' );
1983
1984		assert( ptr != NULL );
1985
1986		attr.bv_val = rdn.bv_val;
1987		attr.bv_len = ptr - rdn.bv_val;
1988
1989		slap_bv2ad( &attr, &ad, &text );
1990
1991		rdn.bv_val = ptr+1;
1992		rdn.bv_len -= attr.bv_len + 1;
1993		ptr = ber_bvchr( &nrdn, '=' );
1994		nrdn.bv_len -= ptr - nrdn.bv_val + 1;
1995		nrdn.bv_val = ptr+1;
1996		attr_merge_one( e, ad, &rdn, &nrdn );
1997
1998		/* Get contextCSN from main DB */
1999		op->o_bd = on->on_info->oi_origdb;
2000		rc = be_entry_get_rw( op, op->o_bd->be_nsuffix, NULL,
2001			slap_schema.si_ad_contextCSN, 0, &e_ctx );
2002
2003		if ( e_ctx ) {
2004			Attribute *a;
2005
2006			a = attr_find( e_ctx->e_attrs, slap_schema.si_ad_contextCSN );
2007			if ( a ) {
2008				/* FIXME: contextCSN could have multiple values!
2009				 * should select the one with the server's SID */
2010				attr_merge_one( e, slap_schema.si_ad_entryCSN,
2011					&a->a_vals[0], &a->a_nvals[0] );
2012				attr_merge( e, a->a_desc, a->a_vals, a->a_nvals );
2013			}
2014			be_entry_release_rw( op, e_ctx, 0 );
2015		}
2016		op->o_bd = li->li_db;
2017
2018		op->ora_e = e;
2019		op->o_req_dn = e->e_name;
2020		op->o_req_ndn = e->e_nname;
2021		op->o_callback = &nullsc;
2022		SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
2023		rc = op->o_bd->be_add( op, &rs );
2024		SLAP_DBFLAGS( op->o_bd ) ^= SLAP_DBFLAG_NOLASTMOD;
2025		if ( e == op->ora_e )
2026			entry_free( e );
2027	}
2028	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
2029	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
2030	ldap_pvt_runqueue_remove( &slapd_rq, rtask );
2031	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
2032
2033	return NULL;
2034}
2035
2036static int
2037accesslog_db_open(
2038	BackendDB *be,
2039	ConfigReply *cr
2040)
2041{
2042	slap_overinst *on = (slap_overinst *)be->bd_info;
2043	log_info *li = on->on_bi.bi_private;
2044
2045
2046	if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
2047		li->li_db = select_backend( &li->li_db_suffix, 0 );
2048		ch_free( li->li_db_suffix.bv_val );
2049		BER_BVZERO( &li->li_db_suffix );
2050	}
2051	if ( li->li_db == NULL ) {
2052		Debug( LDAP_DEBUG_ANY,
2053			"accesslog: \"logdb <suffix>\" missing or invalid.\n",
2054			0, 0, 0 );
2055		return 1;
2056	}
2057
2058	if ( slapMode & SLAP_TOOL_MODE )
2059		return 0;
2060
2061	if ( BER_BVISEMPTY( &li->li_db->be_rootndn )) {
2062		ber_dupbv( &li->li_db->be_rootdn, li->li_db->be_suffix );
2063		ber_dupbv( &li->li_db->be_rootndn, li->li_db->be_nsuffix );
2064	}
2065
2066	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
2067	ldap_pvt_runqueue_insert( &slapd_rq, 3600, accesslog_db_root, on,
2068		"accesslog_db_root", li->li_db->be_suffix[0].bv_val );
2069	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
2070
2071	return 0;
2072}
2073
2074int accesslog_initialize()
2075{
2076	int i, rc;
2077
2078	accesslog.on_bi.bi_type = "accesslog";
2079	accesslog.on_bi.bi_db_init = accesslog_db_init;
2080	accesslog.on_bi.bi_db_destroy = accesslog_db_destroy;
2081	accesslog.on_bi.bi_db_open = accesslog_db_open;
2082
2083	accesslog.on_bi.bi_op_add = accesslog_op_mod;
2084	accesslog.on_bi.bi_op_bind = accesslog_op_bind;
2085	accesslog.on_bi.bi_op_delete = accesslog_op_mod;
2086	accesslog.on_bi.bi_op_modify = accesslog_op_mod;
2087	accesslog.on_bi.bi_op_modrdn = accesslog_op_mod;
2088	accesslog.on_bi.bi_op_unbind = accesslog_unbind;
2089	accesslog.on_bi.bi_op_abandon = accesslog_abandon;
2090	accesslog.on_bi.bi_operational = accesslog_operational;
2091	accesslog.on_response = accesslog_response;
2092
2093	accesslog.on_bi.bi_cf_ocs = log_cfocs;
2094
2095	nullsc.sc_response = slap_null_cb;
2096
2097	rc = config_register_schema( log_cfats, log_cfocs );
2098	if ( rc ) return rc;
2099
2100	/* log schema integration */
2101	for ( i=0; lsyntaxes[i].oid; i++ ) {
2102		int code;
2103
2104		code = register_syntax( &lsyntaxes[ i ].syn );
2105		if ( code != 0 ) {
2106			Debug( LDAP_DEBUG_ANY,
2107				"accesslog_init: register_syntax failed\n",
2108				0, 0, 0 );
2109			return code;
2110		}
2111
2112		if ( lsyntaxes[i].mrs != NULL ) {
2113			code = mr_make_syntax_compat_with_mrs(
2114				lsyntaxes[i].oid, lsyntaxes[i].mrs );
2115			if ( code < 0 ) {
2116				Debug( LDAP_DEBUG_ANY,
2117					"accesslog_init: "
2118					"mr_make_syntax_compat_with_mrs "
2119					"failed\n",
2120					0, 0, 0 );
2121				return code;
2122			}
2123		}
2124	}
2125
2126	for ( i=0; lattrs[i].at; i++ ) {
2127		int code;
2128
2129		code = register_at( lattrs[i].at, lattrs[i].ad, 0 );
2130		if ( code ) {
2131			Debug( LDAP_DEBUG_ANY,
2132				"accesslog_init: register_at failed\n",
2133				0, 0, 0 );
2134			return -1;
2135		}
2136#ifndef LDAP_DEVEL
2137		(*lattrs[i].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
2138#endif
2139	}
2140
2141	for ( i=0; locs[i].ot; i++ ) {
2142		int code;
2143
2144		code = register_oc( locs[i].ot, locs[i].oc, 0 );
2145		if ( code ) {
2146			Debug( LDAP_DEBUG_ANY,
2147				"accesslog_init: register_oc failed\n",
2148				0, 0, 0 );
2149			return -1;
2150		}
2151#ifndef LDAP_DEVEL
2152		(*locs[i].oc)->soc_flags |= SLAP_OC_HIDE;
2153#endif
2154	}
2155
2156	return overlay_register(&accesslog);
2157}
2158
2159#if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC
2160int
2161init_module( int argc, char *argv[] )
2162{
2163	return accesslog_initialize();
2164}
2165#endif
2166
2167#endif /* SLAPD_OVER_ACCESSLOG */
2168