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