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