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