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