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