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, ×tamp );
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, ×tamp, &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, ×tamp, &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, ×tamp );
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, ×tamp, 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