accesslog.c 68.8 KB
Newer Older
Howard Chu's avatar
Howard Chu committed
1
2
3
4
/* accesslog.c - log operations for audit/history purposes */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2005-2020 The OpenLDAP Foundation.
Howard Chu's avatar
Howard Chu committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * Portions copyright 2004-2005 Symas Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Howard Chu for inclusion in
 * OpenLDAP Software.
 */

#include "portable.h"

#ifdef SLAPD_OVER_ACCESSLOG

#include <stdio.h>

#include <ac/string.h>
#include <ac/ctype.h>

#include "slap.h"
#include "config.h"
#include "lutil.h"
#include "ldap_rq.h"

#define LOG_OP_ADD	0x001
#define LOG_OP_DELETE	0x002
#define	LOG_OP_MODIFY	0x004
39
#define LOG_OP_MODRDN	0x008
Howard Chu's avatar
Howard Chu committed
40
41
42
43
44
45
#define	LOG_OP_COMPARE	0x010
#define	LOG_OP_SEARCH	0x020
#define LOG_OP_BIND	0x040
#define LOG_OP_UNBIND	0x080
#define	LOG_OP_ABANDON	0x100
#define	LOG_OP_EXTENDED	0x200
46
#define LOG_OP_UNKNOWN	0x400
Howard Chu's avatar
Howard Chu committed
47

48
#define	LOG_OP_WRITES	(LOG_OP_ADD|LOG_OP_DELETE|LOG_OP_MODIFY|LOG_OP_MODRDN)
Howard Chu's avatar
Howard Chu committed
49
50
#define	LOG_OP_READS	(LOG_OP_COMPARE|LOG_OP_SEARCH)
#define	LOG_OP_SESSION	(LOG_OP_BIND|LOG_OP_UNBIND|LOG_OP_ABANDON)
51
52
#define LOG_OP_ALL		(LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \
	LOG_OP_EXTENDED|LOG_OP_UNKNOWN)
Howard Chu's avatar
Howard Chu committed
53

54
55
56
57
58
typedef struct log_attr {
	struct log_attr *next;
	AttributeDescription *attr;
} log_attr;

59
60
61
62
63
64
65
typedef struct log_base {
	struct log_base *lb_next;
	slap_mask_t lb_ops;
	struct berval lb_base;
	struct berval lb_line;
} log_base;

Howard Chu's avatar
Howard Chu committed
66
67
typedef struct log_info {
	BackendDB *li_db;
68
	struct berval li_db_suffix;
Howard Chu's avatar
Howard Chu committed
69
70
71
72
	slap_mask_t li_ops;
	int li_age;
	int li_cycle;
	struct re_s *li_task;
Howard Chu's avatar
Howard Chu committed
73
	Filter *li_oldf;
Howard Chu's avatar
Howard Chu committed
74
	Entry *li_old;
75
	log_attr *li_oldattrs;
76
	struct berval li_uuid;
77
	int li_success;
78
	log_base *li_bases;
79
80
	BerVarray li_mincsn;
	int *li_sids, li_numcsns;
81
	ldap_pvt_thread_mutex_t li_op_rmutex;
Howard Chu's avatar
Howard Chu committed
82
	ldap_pvt_thread_mutex_t li_log_mutex;
Howard Chu's avatar
Howard Chu committed
83
84
85
86
87
88
89
} log_info;

static ConfigDriver log_cf_gen;

enum {
	LOG_DB = 1,
	LOG_OPS,
90
	LOG_PURGE,
Howard Chu's avatar
Howard Chu committed
91
	LOG_SUCCESS,
92
	LOG_OLD,
93
94
	LOG_OLDATTR,
	LOG_BASE
Howard Chu's avatar
Howard Chu committed
95
96
97
98
99
100
};

static ConfigTable log_cfats[] = {
	{ "logdb", "suffix", 2, 2, 0, ARG_DN|ARG_MAGIC|LOG_DB,
		log_cf_gen, "( OLcfgOvAt:4.1 NAME 'olcAccessLogDB' "
			"DESC 'Suffix of database for log content' "
101
			"EQUALITY distinguishedNameMatch "
Howard Chu's avatar
Howard Chu committed
102
103
104
105
106
107
108
109
110
111
			"SUP distinguishedName SINGLE-VALUE )", NULL, NULL },
	{ "logops", "op|writes|reads|session|all", 2, 0, 0,
		ARG_MAGIC|LOG_OPS,
		log_cf_gen, "( OLcfgOvAt:4.2 NAME 'olcAccessLogOps' "
			"DESC 'Operation types to log' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString )", NULL, NULL },
	{ "logpurge", "age> <interval", 3, 3, 0, ARG_MAGIC|LOG_PURGE,
		log_cf_gen, "( OLcfgOvAt:4.3 NAME 'olcAccessLogPurge' "
			"DESC 'Log cleanup parameters' "
112
			"EQUALITY caseIgnoreMatch "
Howard Chu's avatar
Howard Chu committed
113
			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
114
115
116
	{ "logsuccess", NULL, 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|LOG_SUCCESS,
		log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' "
			"DESC 'Log successful ops only' "
117
			"EQUALITY booleanMatch "
118
			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
Howard Chu's avatar
Howard Chu committed
119
120
	{ "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD,
		log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' "
Howard Chu's avatar
Howard Chu committed
121
			"DESC 'Log old values when modifying entries matching the filter' "
122
			"EQUALITY caseExactMatch "
Howard Chu's avatar
Howard Chu committed
123
			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
124
125
126
	{ "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR,
		log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' "
			"DESC 'Log old values of these attributes even if unmodified' "
127
			"EQUALITY caseIgnoreMatch "
128
			"SYNTAX OMsDirectoryString )", NULL, NULL },
129
130
131
132
133
134
	{ "logbase", "op|writes|reads|session|all< <baseDN", 3, 3, 0,
		ARG_MAGIC|LOG_BASE,
		log_cf_gen, "( OLcfgOvAt:4.7 NAME 'olcAccessLogBase' "
			"DESC 'Operation types to log under a specific branch' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString )", NULL, NULL },
Howard Chu's avatar
Howard Chu committed
135
136
137
138
139
140
141
142
143
	{ NULL }
};

static ConfigOCs log_cfocs[] = {
	{ "( OLcfgOvOc:4.1 "
		"NAME 'olcAccessLogConfig' "
		"DESC 'Access log configuration' "
		"SUP olcOverlayConfig "
		"MUST olcAccessLogDB "
Howard Chu's avatar
Howard Chu committed
144
		"MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ "
145
			"olcAccessLogOld $ olcAccessLogOldAttr $ olcAccessLogBase ) )",
Howard Chu's avatar
Howard Chu committed
146
147
148
149
150
151
152
153
154
155
156
157
			Cft_Overlay, log_cfats },
	{ NULL }
};

static slap_verbmasks logops[] = {
	{ BER_BVC("all"),		LOG_OP_ALL },
	{ BER_BVC("writes"),	LOG_OP_WRITES },
	{ BER_BVC("session"),	LOG_OP_SESSION },
	{ BER_BVC("reads"),		LOG_OP_READS },
	{ BER_BVC("add"),		LOG_OP_ADD },
	{ BER_BVC("delete"),	LOG_OP_DELETE },
	{ BER_BVC("modify"),	LOG_OP_MODIFY },
158
	{ BER_BVC("modrdn"),	LOG_OP_MODRDN },
Howard Chu's avatar
Howard Chu committed
159
160
161
162
163
164
	{ BER_BVC("compare"),	LOG_OP_COMPARE },
	{ BER_BVC("search"),	LOG_OP_SEARCH },
	{ BER_BVC("bind"),		LOG_OP_BIND },
	{ BER_BVC("unbind"),	LOG_OP_UNBIND },
	{ BER_BVC("abandon"),	LOG_OP_ABANDON },
	{ BER_BVC("extended"),	LOG_OP_EXTENDED },
165
	{ BER_BVC("unknown"),	LOG_OP_UNKNOWN },
Howard Chu's avatar
Howard Chu committed
166
167
168
169
170
171
172
173
174
175
	{ BER_BVNULL, 0 }
};

/* Start with "add" in logops */
#define EN_OFFSET	4

enum {
	LOG_EN_ADD = 0,
	LOG_EN_DELETE,
	LOG_EN_MODIFY,
176
	LOG_EN_MODRDN,
Howard Chu's avatar
Howard Chu committed
177
178
179
180
181
182
	LOG_EN_COMPARE,
	LOG_EN_SEARCH,
	LOG_EN_BIND,
	LOG_EN_UNBIND,
	LOG_EN_ABANDON,
	LOG_EN_EXTENDED,
183
	LOG_EN_UNKNOWN,
Howard Chu's avatar
Howard Chu committed
184
185
186
	LOG_EN__COUNT
};

Howard Chu's avatar
Howard Chu committed
187
188
static ObjectClass *log_ocs[LOG_EN__COUNT], *log_container,
	*log_oc_read, *log_oc_write;
Howard Chu's avatar
Howard Chu committed
189
190
191
192
193

#define LOG_SCHEMA_ROOT	"1.3.6.1.4.1.4203.666.11.5"

#define LOG_SCHEMA_AT LOG_SCHEMA_ROOT ".1"
#define LOG_SCHEMA_OC LOG_SCHEMA_ROOT ".2"
194
#define LOG_SCHEMA_SYN LOG_SCHEMA_ROOT ".3"
Howard Chu's avatar
Howard Chu committed
195
196
197
198
199
200
201

static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType,
	*ad_reqSession, *ad_reqResult, *ad_reqAuthzID, *ad_reqControls,
	*ad_reqRespControls, *ad_reqMethod, *ad_reqAssertion, *ad_reqNewRDN,
	*ad_reqNewSuperior, *ad_reqDeleteOldRDN, *ad_reqMod,
	*ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries,
	*ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData,
Howard Chu's avatar
Howard Chu committed
202
	*ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases,
203
204
	*ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID,
	*ad_minCSN;
Howard Chu's avatar
Howard Chu committed
205

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
static int
logSchemaControlValidate(
	Syntax		*syntax,
	struct berval	*val );

char	*mrControl[] = {
	"objectIdentifierFirstComponentMatch",
	NULL
};

static struct {
	char			*oid;
	slap_syntax_defs_rec	syn;
	char			**mrs;
} lsyntaxes[] = {
	{ LOG_SCHEMA_SYN ".1" ,
		{ "( " LOG_SCHEMA_SYN ".1 DESC 'Control' )",
			SLAP_SYNTAX_HIDE,
			NULL,
			logSchemaControlValidate,
			NULL },
		mrControl },
	{ NULL }
};

Howard Chu's avatar
Howard Chu committed
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
static struct {
	char *at;
	AttributeDescription **ad;
} lattrs[] = {
	{ "( " LOG_SCHEMA_AT ".1 NAME 'reqDN' "
		"DESC 'Target DN of request' "
		"EQUALITY distinguishedNameMatch "
		"SYNTAX OMsDN "
		"SINGLE-VALUE )", &ad_reqDN },
	{ "( " LOG_SCHEMA_AT ".2 NAME 'reqStart' "
		"DESC 'Start time of request' "
		"EQUALITY generalizedTimeMatch "
		"ORDERING generalizedTimeOrderingMatch "
		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
		"SINGLE-VALUE )", &ad_reqStart },
	{ "( " LOG_SCHEMA_AT ".3 NAME 'reqEnd' "
		"DESC 'End time of request' "
		"EQUALITY generalizedTimeMatch "
		"ORDERING generalizedTimeOrderingMatch "
		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
		"SINGLE-VALUE )", &ad_reqEnd },
	{ "( " LOG_SCHEMA_AT ".4 NAME 'reqType' "
		"DESC 'Type of request' "
		"EQUALITY caseIgnoreMatch "
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqType },
	{ "( " LOG_SCHEMA_AT ".5 NAME 'reqSession' "
		"DESC 'Session ID of request' "
		"EQUALITY caseIgnoreMatch "
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqSession },
Howard Chu's avatar
Howard Chu committed
262
263
264
265
266
267
	{ "( " LOG_SCHEMA_AT ".6 NAME 'reqAuthzID' "
		"DESC 'Authorization ID of requestor' "
		"EQUALITY distinguishedNameMatch "
		"SYNTAX OMsDN "
		"SINGLE-VALUE )", &ad_reqAuthzID },
	{ "( " LOG_SCHEMA_AT ".7 NAME 'reqResult' "
Howard Chu's avatar
Howard Chu committed
268
269
		"DESC 'Result code of request' "
		"EQUALITY integerMatch "
Howard Chu's avatar
Howard Chu committed
270
		"ORDERING integerOrderingMatch "
Howard Chu's avatar
Howard Chu committed
271
272
		"SYNTAX OMsInteger "
		"SINGLE-VALUE )", &ad_reqResult },
Howard Chu's avatar
Howard Chu committed
273
274
275
276
277
278
279
280
281
282
	{ "( " LOG_SCHEMA_AT ".8 NAME 'reqMessage' "
		"DESC 'Error text of request' "
		"EQUALITY caseIgnoreMatch "
		"SUBSTR caseIgnoreSubstringsMatch "
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqMessage },
	{ "( " LOG_SCHEMA_AT ".9 NAME 'reqReferral' "
		"DESC 'Referrals returned for request' "
		"SUP labeledURI )", &ad_reqReferral },
	{ "( " LOG_SCHEMA_AT ".10 NAME 'reqControls' "
Howard Chu's avatar
Howard Chu committed
283
		"DESC 'Request controls' "
284
285
		"EQUALITY objectIdentifierFirstComponentMatch "
		"SYNTAX " LOG_SCHEMA_SYN ".1 "
286
		"X-ORDERED 'VALUES' )", &ad_reqControls },
Howard Chu's avatar
Howard Chu committed
287
	{ "( " LOG_SCHEMA_AT ".11 NAME 'reqRespControls' "
Howard Chu's avatar
Howard Chu committed
288
		"DESC 'Response controls of request' "
289
290
		"EQUALITY objectIdentifierFirstComponentMatch "
		"SYNTAX " LOG_SCHEMA_SYN ".1 "
291
		"X-ORDERED 'VALUES' )", &ad_reqRespControls },
Howard Chu's avatar
Howard Chu committed
292
293
294
295
296
297
298
299
300
301
302
303
304
	{ "( " LOG_SCHEMA_AT ".12 NAME 'reqId' "
		"DESC 'ID of Request to Abandon' "
		"EQUALITY integerMatch "
		"ORDERING integerOrderingMatch "
		"SYNTAX OMsInteger "
		"SINGLE-VALUE )", &ad_reqId },
	{ "( " LOG_SCHEMA_AT ".13 NAME 'reqVersion' "
		"DESC 'Protocol version of Bind request' "
		"EQUALITY integerMatch "
		"ORDERING integerOrderingMatch "
		"SYNTAX OMsInteger "
		"SINGLE-VALUE )", &ad_reqVersion },
	{ "( " LOG_SCHEMA_AT ".14 NAME 'reqMethod' "
Howard Chu's avatar
Howard Chu committed
305
306
307
308
		"DESC 'Bind method of request' "
		"EQUALITY caseIgnoreMatch "
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqMethod },
Howard Chu's avatar
Howard Chu committed
309
	{ "( " LOG_SCHEMA_AT ".15 NAME 'reqAssertion' "
Howard Chu's avatar
Howard Chu committed
310
311
312
		"DESC 'Compare Assertion of request' "
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqAssertion },
Howard Chu's avatar
Howard Chu committed
313
314
	{ "( " LOG_SCHEMA_AT ".16 NAME 'reqMod' "
		"DESC 'Modifications of request' "
315
316
317
		"EQUALITY octetStringMatch "
		"SUBSTR octetStringSubstringsMatch "
		"SYNTAX OMsOctetString )", &ad_reqMod },
Howard Chu's avatar
Howard Chu committed
318
319
	{ "( " LOG_SCHEMA_AT ".17 NAME 'reqOld' "
		"DESC 'Old values of entry before request completed' "
320
321
322
		"EQUALITY octetStringMatch "
		"SUBSTR octetStringSubstringsMatch "
		"SYNTAX OMsOctetString )", &ad_reqOld },
Howard Chu's avatar
Howard Chu committed
323
	{ "( " LOG_SCHEMA_AT ".18 NAME 'reqNewRDN' "
Howard Chu's avatar
Howard Chu committed
324
325
326
327
		"DESC 'New RDN of request' "
		"EQUALITY distinguishedNameMatch "
		"SYNTAX OMsDN "
		"SINGLE-VALUE )", &ad_reqNewRDN },
Howard Chu's avatar
Howard Chu committed
328
	{ "( " LOG_SCHEMA_AT ".19 NAME 'reqDeleteOldRDN' "
Howard Chu's avatar
Howard Chu committed
329
		"DESC 'Delete old RDN' "
Howard Chu's avatar
Howard Chu committed
330
		"EQUALITY booleanMatch "
Howard Chu's avatar
Howard Chu committed
331
332
		"SYNTAX OMsBoolean "
		"SINGLE-VALUE )", &ad_reqDeleteOldRDN },
Howard Chu's avatar
Howard Chu committed
333
334
335
336
337
338
	{ "( " LOG_SCHEMA_AT ".20 NAME 'reqNewSuperior' "
		"DESC 'New superior DN of request' "
		"EQUALITY distinguishedNameMatch "
		"SYNTAX OMsDN "
		"SINGLE-VALUE )", &ad_reqNewSuperior },
	{ "( " LOG_SCHEMA_AT ".21 NAME 'reqScope' "
Howard Chu's avatar
Howard Chu committed
339
		"DESC 'Scope of request' "
Howard Chu's avatar
Howard Chu committed
340
		"EQUALITY caseIgnoreMatch "
Howard Chu's avatar
Howard Chu committed
341
342
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqScope },
Howard Chu's avatar
Howard Chu committed
343
344
345
346
347
348
349
350
351
352
353
	{ "( " LOG_SCHEMA_AT ".22 NAME 'reqDerefAliases' "
		"DESC 'Disposition of Aliases in request' "
		"EQUALITY caseIgnoreMatch "
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqDerefAliases },
	{ "( " LOG_SCHEMA_AT ".23 NAME 'reqAttrsOnly' "
		"DESC 'Attributes and values of request' "
		"EQUALITY booleanMatch "
		"SYNTAX OMsBoolean "
		"SINGLE-VALUE )", &ad_reqAttrsOnly },
	{ "( " LOG_SCHEMA_AT ".24 NAME 'reqFilter' "
Howard Chu's avatar
Howard Chu committed
354
		"DESC 'Filter of request' "
Howard Chu's avatar
Howard Chu committed
355
356
		"EQUALITY caseIgnoreMatch "
		"SUBSTR caseIgnoreSubstringsMatch "
Howard Chu's avatar
Howard Chu committed
357
358
		"SYNTAX OMsDirectoryString "
		"SINGLE-VALUE )", &ad_reqFilter },
Howard Chu's avatar
Howard Chu committed
359
	{ "( " LOG_SCHEMA_AT ".25 NAME 'reqAttr' "
Howard Chu's avatar
Howard Chu committed
360
		"DESC 'Attributes of request' "
Howard Chu's avatar
Howard Chu committed
361
		"EQUALITY caseIgnoreMatch "
Howard Chu's avatar
Howard Chu committed
362
		"SYNTAX OMsDirectoryString )", &ad_reqAttr },
Howard Chu's avatar
Howard Chu committed
363
	{ "( " LOG_SCHEMA_AT ".26 NAME 'reqSizeLimit' "
Howard Chu's avatar
Howard Chu committed
364
		"DESC 'Size limit of request' "
Howard Chu's avatar
Howard Chu committed
365
366
		"EQUALITY integerMatch "
		"ORDERING integerOrderingMatch "
Howard Chu's avatar
Howard Chu committed
367
368
		"SYNTAX OMsInteger "
		"SINGLE-VALUE )", &ad_reqSizeLimit },
Howard Chu's avatar
Howard Chu committed
369
	{ "( " LOG_SCHEMA_AT ".27 NAME 'reqTimeLimit' "
Howard Chu's avatar
Howard Chu committed
370
		"DESC 'Time limit of request' "
Howard Chu's avatar
Howard Chu committed
371
372
		"EQUALITY integerMatch "
		"ORDERING integerOrderingMatch "
Howard Chu's avatar
Howard Chu committed
373
374
		"SYNTAX OMsInteger "
		"SINGLE-VALUE )", &ad_reqTimeLimit },
Howard Chu's avatar
Howard Chu committed
375
376
377
378
379
380
381
	{ "( " LOG_SCHEMA_AT ".28 NAME 'reqEntries' "
		"DESC 'Number of entries returned' "
		"EQUALITY integerMatch "
		"ORDERING integerOrderingMatch "
		"SYNTAX OMsInteger "
		"SINGLE-VALUE )", &ad_reqEntries },
	{ "( " LOG_SCHEMA_AT ".29 NAME 'reqData' "
Howard Chu's avatar
Howard Chu committed
382
		"DESC 'Data of extended request' "
Howard Chu's avatar
Howard Chu committed
383
384
		"EQUALITY octetStringMatch "
		"SUBSTR octetStringSubstringsMatch "
Howard Chu's avatar
Howard Chu committed
385
386
		"SYNTAX OMsOctetString "
		"SINGLE-VALUE )", &ad_reqData },
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

	/*
	 * from <draft-chu-ldap-logschema-01.txt>:
	 *

   ( LOG_SCHEMA_AT .30 NAME 'auditContext'
   DESC 'DN of auditContainer'
   EQUALITY distinguishedNameMatch
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
   SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )

	 * - removed EQUALITY matchingRule
	 * - changed directoryOperation in dSAOperation
	 */
	{ "( " LOG_SCHEMA_AT ".30 NAME 'auditContext' "
		"DESC 'DN of auditContainer' "
		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
		"SINGLE-VALUE "
		"NO-USER-MODIFICATION "
		"USAGE dSAOperation )", &ad_auditContext },
407
408
409
410
411
412
413

	/*
	 * ITS#6656
	 */
	{ "( " LOG_SCHEMA_AT ".31 NAME 'reqEntryUUID' "
		"DESC 'UUID of entry' "
		"EQUALITY UUIDMatch "
414
		"ORDERING UUIDOrderingMatch "
415
416
		"SYNTAX 1.3.6.1.1.16.1 "
		"SINGLE-VALUE )", &ad_reqEntryUUID },
417
418
419
420
421
422
423
424
425
426
427

	/*
	 * ITS#8486
	 */
	{ "( " LOG_SCHEMA_AT ".32 NAME 'minCSN' "
		"DESC 'CSN set that the logs are recorded from' "
		"EQUALITY CSNMatch "
		"ORDERING CSNOrderingMatch "
		"SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
		"NO-USER-MODIFICATION "
		"USAGE dSAOperation )", &ad_minCSN },
Howard Chu's avatar
Howard Chu committed
428
429
430
431
432
433
434
435
	{ NULL, NULL }
};

static struct {
	char *ot;
	ObjectClass **oc;
} locs[] = {
	{ "( " LOG_SCHEMA_OC ".0 NAME 'auditContainer' "
Howard Chu's avatar
Howard Chu committed
436
		"DESC 'AuditLog container' "
Howard Chu's avatar
Howard Chu committed
437
		"SUP top STRUCTURAL "
Howard Chu's avatar
Howard Chu committed
438
		"MAY ( cn $ reqStart $ reqEnd ) )", &log_container },
Howard Chu's avatar
Howard Chu committed
439
440
441
442
443
	{ "( " LOG_SCHEMA_OC ".1 NAME 'auditObject' "
		"DESC 'OpenLDAP request auditing' "
		"SUP top STRUCTURAL "
		"MUST ( reqStart $ reqType $ reqSession ) "
		"MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $ reqEnd $ "
444
			"reqResult $ reqMessage $ reqReferral $ reqEntryUUID ) )",
Howard Chu's avatar
Howard Chu committed
445
				&log_ocs[LOG_EN_UNBIND] },
Howard Chu's avatar
Howard Chu committed
446
447
	{ "( " LOG_SCHEMA_OC ".2 NAME 'auditReadObject' "
		"DESC 'OpenLDAP read request record' "
Howard Chu's avatar
Howard Chu committed
448
		"SUP auditObject STRUCTURAL )", &log_oc_read },
Howard Chu's avatar
Howard Chu committed
449
450
	{ "( " LOG_SCHEMA_OC ".3 NAME 'auditWriteObject' "
		"DESC 'OpenLDAP write request record' "
Howard Chu's avatar
Howard Chu committed
451
		"SUP auditObject STRUCTURAL )", &log_oc_write },
Howard Chu's avatar
Howard Chu committed
452
453
	{ "( " LOG_SCHEMA_OC ".4 NAME 'auditAbandon' "
		"DESC 'Abandon operation' "
Howard Chu's avatar
Howard Chu committed
454
		"SUP auditObject STRUCTURAL "
Howard Chu's avatar
Howard Chu committed
455
456
457
458
459
460
461
		"MUST reqId )", &log_ocs[LOG_EN_ABANDON] },
	{ "( " LOG_SCHEMA_OC ".5 NAME 'auditAdd' "
		"DESC 'Add operation' "
		"SUP auditWriteObject STRUCTURAL "
		"MUST reqMod )", &log_ocs[LOG_EN_ADD] },
	{ "( " LOG_SCHEMA_OC ".6 NAME 'auditBind' "
		"DESC 'Bind operation' "
Howard Chu's avatar
Howard Chu committed
462
		"SUP auditObject STRUCTURAL "
Howard Chu's avatar
Howard Chu committed
463
		"MUST ( reqVersion $ reqMethod ) )", &log_ocs[LOG_EN_BIND] },
Howard Chu's avatar
Howard Chu committed
464
465
466
467
	{ "( " LOG_SCHEMA_OC ".7 NAME 'auditCompare' "
		"DESC 'Compare operation' "
		"SUP auditReadObject STRUCTURAL "
		"MUST reqAssertion )", &log_ocs[LOG_EN_COMPARE] },
Howard Chu's avatar
Howard Chu committed
468
469
470
471
472
	{ "( " LOG_SCHEMA_OC ".8 NAME 'auditDelete' "
		"DESC 'Delete operation' "
		"SUP auditWriteObject STRUCTURAL "
		"MAY reqOld )", &log_ocs[LOG_EN_DELETE] },
	{ "( " LOG_SCHEMA_OC ".9 NAME 'auditModify' "
Howard Chu's avatar
Howard Chu committed
473
		"DESC 'Modify operation' "
Howard Chu's avatar
Howard Chu committed
474
		"SUP auditWriteObject STRUCTURAL "
Howard Chu's avatar
Howard Chu committed
475
		"MAY reqOld MUST reqMod )", &log_ocs[LOG_EN_MODIFY] },
Howard Chu's avatar
Howard Chu committed
476
	{ "( " LOG_SCHEMA_OC ".10 NAME 'auditModRDN' "
477
		"DESC 'ModRDN operation' "
Howard Chu's avatar
Howard Chu committed
478
479
		"SUP auditWriteObject STRUCTURAL "
		"MUST ( reqNewRDN $ reqDeleteOldRDN ) "
480
		"MAY ( reqNewSuperior $ reqMod $ reqOld ) )", &log_ocs[LOG_EN_MODRDN] },
Howard Chu's avatar
Howard Chu committed
481
	{ "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' "
Howard Chu's avatar
Howard Chu committed
482
483
		"DESC 'Search operation' "
		"SUP auditReadObject STRUCTURAL "
Howard Chu's avatar
Howard Chu committed
484
		"MUST ( reqScope $ reqDerefAliases $ reqAttrsonly ) "
Howard Chu's avatar
Howard Chu committed
485
486
		"MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $ "
			"reqTimeLimit ) )", &log_ocs[LOG_EN_SEARCH] },
Howard Chu's avatar
Howard Chu committed
487
	{ "( " LOG_SCHEMA_OC ".12 NAME 'auditExtended' "
Howard Chu's avatar
Howard Chu committed
488
		"DESC 'Extended operation' "
Howard Chu's avatar
Howard Chu committed
489
		"SUP auditObject STRUCTURAL "
Howard Chu's avatar
Howard Chu committed
490
491
492
493
494
495
		"MAY reqData )", &log_ocs[LOG_EN_EXTENDED] },
	{ NULL, NULL }
};

#define	RDNEQ	"reqStart="

496
497
498
/* Our time intervals are of the form [ddd+]hh:mm[:ss]
 * If a field is present, it must be two digits. (Except for
 * days, which can be arbitrary width.)
Howard Chu's avatar
Howard Chu committed
499
500
501
502
503
504
 */
static int
log_age_parse(char *agestr)
{
	int t1, t2;
	int gotdays = 0;
505
	char *endptr;
Howard Chu's avatar
Howard Chu committed
506

507
	t1 = strtol( agestr, &endptr, 10 );
Howard Chu's avatar
Howard Chu committed
508
	/* Is there a days delimiter? */
509
	if ( *endptr == '+' ) {
510
511
512
		/* 32 bit time only covers about 68 years */
		if ( t1 < 0 || t1 > 25000 )
			return -1;
Howard Chu's avatar
Howard Chu committed
513
514
		t1 *= 24;
		gotdays = 1;
Howard Chu's avatar
Howard Chu committed
515
516
517
518
519
520
521
522
		agestr = endptr + 1;
	} else {
		if ( agestr[2] != ':' ) {
			/* No valid delimiter found, fail */
			return -1;
		}
		t1 *= 60;
		agestr += 3;
Howard Chu's avatar
Howard Chu committed
523
524
525
526
527
	}

	t2 = atoi( agestr );
	t1 += t2;

Howard Chu's avatar
Howard Chu committed
528
529
530
531
532
533
534
535
536
537
	if ( agestr[2] ) {
		/* if there's a delimiter, it can only be a colon */
		if ( agestr[2] != ':' )
			return -1;
	} else {
		/* If we're at the end of the string, and we started with days,
		 * fail because we expected to find minutes too.
		 */
		return gotdays ? -1 : t1 * 60;
	}
Howard Chu's avatar
Howard Chu committed
538
539
540
541
542
543
544
545
546
547
548
549
550
551

	agestr += 3;
	t2 = atoi( agestr );

	/* last field can only be seconds */
	if ( agestr[2] && ( agestr[2] != ':' || !gotdays ))
		return -1;
	t1 *= 60;
	t1 += t2;

	if ( agestr[2] ) {
		agestr += 3;
		if ( agestr[2] )
			return -1;
Howard Chu's avatar
Howard Chu committed
552
		t1 *= 60;
Howard Chu's avatar
Howard Chu committed
553
		t1 += atoi( agestr );
Howard Chu's avatar
Howard Chu committed
554
555
556
	} else if ( gotdays ) {
		/* only got days+hh:mm */
		t1 *= 60;
Howard Chu's avatar
Howard Chu committed
557
558
559
560
561
	}
	return t1;
}

static void
562
log_age_unparse( int age, struct berval *agebv, size_t size )
Howard Chu's avatar
Howard Chu committed
563
{
564
	int dd, hh, mm, ss, len;
Howard Chu's avatar
Howard Chu committed
565
566
	char *ptr;

567
568
	assert( size > 0 );

Howard Chu's avatar
Howard Chu committed
569
570
571
572
	ss = age % 60;
	age /= 60;
	mm = age % 60;
	age /= 60;
Howard Chu's avatar
Howard Chu committed
573
	hh = age % 24;
Howard Chu's avatar
Howard Chu committed
574
575
576
577
578
	age /= 24;
	dd = age;

	ptr = agebv->bv_val;

579
580
	if ( dd ) {
		len = snprintf( ptr, size, "%d+", dd );
581
		assert( len >= 0 && (unsigned) len < size );
582
583
584
585
		size -= len;
		ptr += len;
	}
	len = snprintf( ptr, size, "%02d:%02d", hh, mm );
586
	assert( len >= 0 && (unsigned) len < size );
587
588
589
590
	size -= len;
	ptr += len;
	if ( ss ) {
		len = snprintf( ptr, size, ":%02d", ss );
591
		assert( len >= 0 && (unsigned) len < size );
592
593
594
		size -= len;
		ptr += len;
	}
Howard Chu's avatar
Howard Chu committed
595
596
597
598

	agebv->bv_len = ptr - agebv->bv_val;
}

599
static slap_callback nullsc;
Howard Chu's avatar
Howard Chu committed
600
601
602
603

#define PURGE_INCREMENT	100

typedef struct purge_data {
604
	struct log_info *li;
Howard Chu's avatar
Howard Chu committed
605
606
	int slots;
	int used;
607
	int mincsn_updated;
Howard Chu's avatar
Howard Chu committed
608
609
610
611
612
613
614
615
	BerVarray dn;
	BerVarray ndn;
} purge_data;

static int
log_old_lookup( Operation *op, SlapReply *rs )
{
	purge_data *pd = op->o_callback->sc_private;
616
	struct log_info *li = pd->li;
617
	Attribute *a;
Howard Chu's avatar
Howard Chu committed
618
619
620

	if ( rs->sr_type != REP_SEARCH) return 0;

Howard Chu's avatar
Howard Chu committed
621
622
	if ( slapd_shutdown ) return 0;

623
	/* Update minCSN */
624
625
	a = attr_find( rs->sr_entry->e_attrs,
		slap_schema.si_ad_entryCSN );
Howard Chu's avatar
Howard Chu committed
626
	if ( a ) {
Howard Chu's avatar
Howard Chu committed
627
		ber_len_t len = a->a_nvals[0].bv_len;
628
629
630
631
632
633
634
635
636
637
638
639
640
		int i, sid;

		/* Find the correct sid */
		sid = slap_parse_csn_sid( &a->a_nvals[0] );
		for ( i=0; i < li->li_numcsns; i++ ) {
			if ( sid <= li->li_sids[i] ) break;
		}
		if ( i >= li->li_numcsns || sid != li->li_sids[i] ) {
			Debug( LDAP_DEBUG_ANY, "log_old_lookup: "
					"csn=%s with sid not in minCSN set!\n",
					a->a_nvals[0].bv_val );
		}

Howard Chu's avatar
Howard Chu committed
641
		/* Paranoid len check, normalized CSNs are always the same length */
642
643
644
645
646
		if ( len > li->li_mincsn[i].bv_len )
			len = li->li_mincsn[i].bv_len;
		if ( ber_bvcmp( &li->li_mincsn[i], &a->a_nvals[0] ) < 0 ) {
			pd->mincsn_updated = 1;
			AC_MEMCPY( li->li_mincsn[i].bv_val, a->a_nvals[0].bv_val, len );
Howard Chu's avatar
Howard Chu committed
647
		}
648
	}
Howard Chu's avatar
Howard Chu committed
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
	if ( pd->used >= pd->slots ) {
		pd->slots += PURGE_INCREMENT;
		pd->dn = ch_realloc( pd->dn, pd->slots * sizeof( struct berval ));
		pd->ndn = ch_realloc( pd->ndn, pd->slots * sizeof( struct berval ));
	}
	ber_dupbv( &pd->dn[pd->used], &rs->sr_entry->e_name );
	ber_dupbv( &pd->ndn[pd->used], &rs->sr_entry->e_nname );
	pd->used++;
	return 0;
}

/* Periodically search for old entries in the log database and delete them */
static void *
accesslog_purge( void *ctx, void *arg )
{
	struct re_s *rtask = arg;
	struct log_info *li = rtask->arg;

	Connection conn = {0};
668
	OperationBuffer opbuf;
669
	Operation *op;
Howard Chu's avatar
Howard Chu committed
670
	SlapReply rs = {REP_RESULT};
671
	slap_callback cb = { NULL, log_old_lookup, NULL, NULL, NULL };
Howard Chu's avatar
Howard Chu committed
672
	Filter f;
673
	AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
674
	purge_data pd = { .li = li };
Howard Chu's avatar
Howard Chu committed
675
	char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
676
	char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
Howard Chu's avatar
Howard Chu committed
677
678
	time_t old = slap_get_time();

679
680
	connection_fake_init( &conn, &opbuf, ctx );
	op = &opbuf.ob_op;
Howard Chu's avatar
Howard Chu committed
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717

	f.f_choice = LDAP_FILTER_LE;
	f.f_ava = &ava;
	f.f_next = NULL;

	ava.aa_desc = ad_reqStart;
	ava.aa_value.bv_val = timebuf;
	ava.aa_value.bv_len = sizeof(timebuf);

	old -= li->li_age;
	slap_timestamp( &old, &ava.aa_value );

	op->o_tag = LDAP_REQ_SEARCH;
	op->o_bd = li->li_db;
	op->o_dn = li->li_db->be_rootdn;
	op->o_ndn = li->li_db->be_rootndn;
	op->o_req_dn = li->li_db->be_suffix[0];
	op->o_req_ndn = li->li_db->be_nsuffix[0];
	op->o_callback = &cb;
	op->ors_scope = LDAP_SCOPE_ONELEVEL;
	op->ors_deref = LDAP_DEREF_NEVER;
	op->ors_tlimit = SLAP_NO_LIMIT;
	op->ors_slimit = SLAP_NO_LIMIT;
	op->ors_filter = &f;
	filter2bv_x( op, &f, &op->ors_filterstr );
	op->ors_attrs = slap_anlist_no_attrs;
	op->ors_attrsonly = 1;
	
	cb.sc_private = &pd;

	op->o_bd->be_search( op, &rs );
	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );

	if ( pd.used ) {
		int i;

		op->o_callback = &nullsc;
718
		op->o_dont_replicate = 1;
Howard Chu's avatar
Howard Chu committed
719

720
		if ( pd.mincsn_updated ) {
721
			Modifications mod;
722
723
724
			/* update context's minCSN to reflect oldest CSN */
			mod.sml_numvals = li->li_numcsns;
			mod.sml_values = li->li_mincsn;
725
			mod.sml_nvalues = NULL;
726
			mod.sml_desc = ad_minCSN;
727
728
729
730
731
732
733
734
735
736
737
			mod.sml_op = LDAP_MOD_REPLACE;
			mod.sml_flags = SLAP_MOD_INTERNAL;
			mod.sml_next = NULL;

			op->o_tag = LDAP_REQ_MODIFY;
			op->orm_modlist = &mod;
			op->orm_no_opattrs = 1;
			op->o_req_dn = li->li_db->be_suffix[0];
			op->o_req_ndn = li->li_db->be_nsuffix[0];
			op->o_no_schema_check = 1;
			op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
738
739
740
741
742
			if ( !slapd_shutdown ) {
				Debug( LDAP_DEBUG_SYNC, "accesslog_purge: "
						"updating minCSN with %d values\n",
						li->li_numcsns );
				op->o_bd->be_modify( op, &rs );
743
744
			}
		}
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760

		/* delete the expired entries */
		op->o_tag = LDAP_REQ_DELETE;
		for (i=0; i<pd.used; i++) {
			op->o_req_dn = pd.dn[i];
			op->o_req_ndn = pd.ndn[i];
			if ( !slapd_shutdown ) {
				rs_reinit( &rs, REP_RESULT );
				op->o_bd->be_delete( op, &rs );
			}
			ch_free( pd.ndn[i].bv_val );
			ch_free( pd.dn[i].bv_val );
			ldap_pvt_thread_pool_pausecheck( &connection_pool );
		}
		ch_free( pd.ndn );
		ch_free( pd.dn );
Howard Chu's avatar
Howard Chu committed
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
	}

	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );

	return NULL;
}

static int
log_cf_gen(ConfigArgs *c)
{
	slap_overinst *on = (slap_overinst *)c->bi;
	struct log_info *li = on->on_bi.bi_private;
	int rc = 0;
	slap_mask_t tmask = 0;
Howard Chu's avatar
Howard Chu committed
777
	char agebuf[2*STRLENOF("ddddd+hh:mm:ss  ")];
Howard Chu's avatar
Howard Chu committed
778
779
780
781
782
783
	struct berval agebv, cyclebv;

	switch( c->op ) {
	case SLAP_CONFIG_EMIT:
		switch( c->type ) {
		case LOG_DB:
784
785
786
787
788
789
790
			if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
				value_add_one( &c->rvalue_vals, &li->li_db_suffix );
				value_add_one( &c->rvalue_nvals, &li->li_db_suffix );
			} else if ( li->li_db ) {
				value_add_one( &c->rvalue_vals, li->li_db->be_suffix );
				value_add_one( &c->rvalue_nvals, li->li_db->be_nsuffix );
			} else {
791
				snprintf( c->cr_msg, sizeof( c->cr_msg ),
Pierangelo Masarati's avatar
Pierangelo Masarati committed
792
793
					"accesslog: \"logdb <suffix>\" must be specified" );
				Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
794
					c->log, c->cr_msg, c->value_dn.bv_val );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
795
796
797
				rc = 1;
				break;
			}
Howard Chu's avatar
Howard Chu committed
798
799
800
801
802
			break;
		case LOG_OPS:
			rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals );
			break;
		case LOG_PURGE:
803
804
805
806
			if ( !li->li_age ) {
				rc = 1;
				break;
			}
Howard Chu's avatar
Howard Chu committed
807
			agebv.bv_val = agebuf;
808
			log_age_unparse( li->li_age, &agebv, sizeof( agebuf ) );
Howard Chu's avatar
Howard Chu committed
809
810
811
			agebv.bv_val[agebv.bv_len] = ' ';
			agebv.bv_len++;
			cyclebv.bv_val = agebv.bv_val + agebv.bv_len;
812
			log_age_unparse( li->li_cycle, &cyclebv, sizeof( agebuf ) - agebv.bv_len );
Howard Chu's avatar
Howard Chu committed
813
814
815
			agebv.bv_len += cyclebv.bv_len;
			value_add_one( &c->rvalue_vals, &agebv );
			break;
816
817
818
819
820
821
		case LOG_SUCCESS:
			if ( li->li_success )
				c->value_int = li->li_success;
			else
				rc = 1;
			break;
Howard Chu's avatar
Howard Chu committed
822
823
824
		case LOG_OLD:
			if ( li->li_oldf ) {
				filter2bv( li->li_oldf, &agebv );
Howard Chu's avatar
Howard Chu committed
825
				ber_bvarray_add( &c->rvalue_vals, &agebv );
Howard Chu's avatar
Howard Chu committed
826
827
828
829
			}
			else
				rc = 1;
			break;
830
831
832
833
834
835
836
837
838
839
		case LOG_OLDATTR:
			if ( li->li_oldattrs ) {
				log_attr *la;

				for ( la = li->li_oldattrs; la; la=la->next )
					value_add_one( &c->rvalue_vals, &la->attr->ad_cname );
			}
			else
				rc = 1;
			break;
840
841
842
843
844
845
846
847
848
849
		case LOG_BASE:
			if ( li->li_bases ) {
				log_base *lb;

				for ( lb = li->li_bases; lb; lb=lb->lb_next )
					value_add_one( &c->rvalue_vals, &lb->lb_line );
			}
			else
				rc = 1;
			break;
Howard Chu's avatar
Howard Chu committed
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
		}
		break;
	case LDAP_MOD_DELETE:
		switch( c->type ) {
		case LOG_DB:
			/* noop. this should always be a valid backend. */
			break;
		case LOG_OPS:
			if ( c->valx < 0 ) {
				li->li_ops = 0;
			} else {
				rc = verbs_to_mask( 1, &c->line, logops, &tmask );
				if ( rc == 0 )
					li->li_ops &= ~tmask;
			}
			break;
		case LOG_PURGE:
			if ( li->li_task ) {
				struct re_s *re = li->li_task;
				li->li_task = NULL;
870
				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
Howard Chu's avatar
Howard Chu committed
871
872
873
				if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ))
					ldap_pvt_runqueue_stoptask( &slapd_rq, re );
				ldap_pvt_runqueue_remove( &slapd_rq, re );
874
				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
Howard Chu's avatar
Howard Chu committed
875
876
877
878
			}
			li->li_age = 0;
			li->li_cycle = 0;
			break;
879
880
881
		case LOG_SUCCESS:
			li->li_success = 0;
			break;
Howard Chu's avatar
Howard Chu committed
882
883
884
885
		case LOG_OLD:
			if ( li->li_oldf ) {
				filter_free( li->li_oldf );
				li->li_oldf = NULL;
Howard Chu's avatar
Howard Chu committed
886
887
			}
			break;
888
889
890
891
892
893
894
895
896
		case LOG_OLDATTR:
			if ( c->valx < 0 ) {
				log_attr *la, *ln;

				for ( la = li->li_oldattrs; la; la = ln ) {
					ln = la->next;
					ch_free( la );
				}
			} else {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
897
				log_attr *la = NULL, **lp;
898
899
900
901
902
903
904
905
906
907
				int i;

				for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) {
					la = *lp;
					lp = &la->next;
				}
				*lp = la->next;
				ch_free( la );
			}
			break;
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
		case LOG_BASE:
			if ( c->valx < 0 ) {
				log_base *lb, *ln;

				for ( lb = li->li_bases; lb; lb = ln ) {
					ln = lb->lb_next;
					ch_free( lb );
				}
			} else {
				log_base *lb = NULL, **lp;
				int i;

				for ( lp = &li->li_bases, i=0; i < c->valx; i++ ) {
					lb = *lp;
					lp = &lb->lb_next;
				}
				*lp = lb->lb_next;
				ch_free( lb );
			}
			break;
Howard Chu's avatar
Howard Chu committed
928
929
930
931
932
		}
		break;
	default:
		switch( c->type ) {
		case LOG_DB:
933
			if ( CONFIG_ONLINE_ADD( c )) {
934
				li->li_db = select_backend( &c->value_ndn, 0 );
935
				if ( !li->li_db ) {
936
					snprintf( c->cr_msg, sizeof( c->cr_msg ),
937
938
939
						"<%s> no matching backend found for suffix",
						c->argv[0] );
					Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
940
						c->log, c->cr_msg, c->value_dn.bv_val );
941
942
943
944
945
					rc = 1;
				}
				ch_free( c->value_ndn.bv_val );
			} else {
				li->li_db_suffix = c->value_ndn;
Howard Chu's avatar
Howard Chu committed
946
947
948
949
950
951
952
953
954
955
			}
			ch_free( c->value_dn.bv_val );
			break;
		case LOG_OPS:
			rc = verbs_to_mask( c->argc, c->argv, logops, &tmask );
			if ( rc == 0 )
				li->li_ops |= tmask;
			break;
		case LOG_PURGE:
			li->li_age = log_age_parse( c->argv[1] );
956
			if ( li->li_age < 1 ) {
Howard Chu's avatar
Howard Chu committed
957
958
959
				rc = 1;
			} else {
				li->li_cycle = log_age_parse( c->argv[2] );
960
				if ( li->li_cycle < 1 ) {
Howard Chu's avatar
Howard Chu committed
961
962
963
964
965
					rc = 1;
				} else if ( slapMode & SLAP_SERVER_MODE ) {
					struct re_s *re = li->li_task;
					if ( re )
						re->interval.tv_sec = li->li_cycle;
966
967
					else {
						ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
Howard Chu's avatar
Howard Chu committed
968
969
970
971
972
						li->li_task = ldap_pvt_runqueue_insert( &slapd_rq,
							li->li_cycle, accesslog_purge, li,
							"accesslog_purge", li->li_db ?
								li->li_db->be_suffix[0].bv_val :
								c->be->be_suffix[0].bv_val );
973
974
						ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
					}
Howard Chu's avatar
Howard Chu committed
975
976
977
				}
			}
			break;
978
979
980
		case LOG_SUCCESS:
			li->li_success = c->value_int;
			break;
Howard Chu's avatar
Howard Chu committed
981
982
983
		case LOG_OLD:
			li->li_oldf = str2filter( c->argv[1] );
			if ( !li->li_oldf ) {
984
				snprintf( c->cr_msg, sizeof( c->cr_msg ), "bad filter!" );
Howard Chu's avatar
Howard Chu committed
985
986
				rc = 1;
			}
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
			break;
		case LOG_OLDATTR: {
			int i;
			AttributeDescription *ad;
			const char *text;

			for ( i=1; i< c->argc; i++ ) {
				ad = NULL;
				if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) {
					log_attr *la = ch_malloc( sizeof( log_attr ));
					la->attr = ad;
					la->next = li->li_oldattrs;
					li->li_oldattrs = la;
				} else {
1001
					snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s",
1002
1003
						c->argv[0], c->argv[i], text );
					Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
1004
						"%s: %s\n", c->log, c->cr_msg );
1005
1006
1007
1008
1009
1010
					rc = ARG_BAD_CONF;
					break;
				}
			}
			}
			break;
1011
		case LOG_BASE: {
Howard Chu's avatar
Howard Chu committed
1012
			slap_mask_t m = 0;
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
			rc = verbstring_to_mask( logops, c->argv[1], '|', &m );
			if ( rc == 0 ) {
				struct berval dn, ndn;
				ber_str2bv( c->argv[2], 0, 0, &dn );
				rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
				if ( rc == 0 ) {
					log_base *lb;
					struct berval mbv;
					char *ptr;
					mask_to_verbstring( logops, m, '|', &mbv );
					lb = ch_malloc( sizeof( log_base ) + mbv.bv_len + ndn.bv_len + 3 + 1 );
					lb->lb_line.bv_val = (char *)(lb + 1);
					lb->lb_line.bv_len = mbv.bv_len + ndn.bv_len + 3;
					ptr = lutil_strcopy( lb->lb_line.bv_val, mbv.bv_val );
					*ptr++ = ' ';
					*ptr++ = '"';
					lb->lb_base.bv_val = ptr;
					lb->lb_base.bv_len = ndn.bv_len;
					ptr = lutil_strcopy( ptr, ndn.bv_val );
					*ptr++ = '"';
					lb->lb_ops = m;
					lb->lb_next = li->li_bases;
					li->li_bases = lb;
				} else {
					snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid DN: %s",
						c->argv[0], c->argv[2] );
					Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
1040
						"%s: %s\n", c->log, c->cr_msg );
1041
1042
					rc = ARG_BAD_CONF;
				}
Howard Chu's avatar
Howard Chu committed
1043
			} else {
1044
1045
1046
				snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid ops: %s",
					c->argv[0], c->argv[1] );
				Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
1047
					"%s: %s\n", c->log, c->cr_msg );
1048
1049
1050
1051
				rc = ARG_BAD_CONF;
			}
			}
			break;
Howard Chu's avatar
Howard Chu committed
1052
1053
1054
1055
1056
1057
		}
		break;
	}
	return rc;
}

1058
static int
1059
1060
1061
logSchemaControlValidate(
	Syntax		*syntax,
	struct berval	*valp )
1062
{
1063
	struct berval	val, bv;
1064
	ber_len_t		i;
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
	int		rc = LDAP_SUCCESS;

	assert( valp != NULL );

	val = *valp;

	/* check minimal size */
	if ( val.bv_len < STRLENOF( "{*}" ) ) {
		return LDAP_INVALID_SYNTAX;
	}

	val.bv_len--;

	/* check SEQUENCE boundaries */
	if ( val.bv_val[ 0 ] != '{' /*}*/ ||
		val.bv_val[ val.bv_len ] != /*{*/ '}' )
	{
		return LDAP_INVALID_SYNTAX;
	}

	/* extract and check OID */
	for ( i = 1; i < val.bv_len; i++ ) {
		if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
			break;
		}
	}

	bv.bv_val = &val.bv_val[ i ];

	for ( i++; i < val.bv_len; i++ ) {
		if ( ASCII_SPACE( val.bv_val[ i ] ) )
		{
			break;
		}
	}

	bv.bv_len = &val.bv_val[ i ] - bv.bv_val;

	rc = numericoidValidate( NULL, &bv );
	if ( rc != LDAP_SUCCESS ) {
		return rc;
	}

	if ( i == val.bv_len ) {
		return LDAP_SUCCESS;
	}

	if ( val.bv_val[ i ] != ' ' ) {
		return LDAP_INVALID_SYNTAX;
	}

	for ( i++; i < val.bv_len; i++ ) {
		if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
			break;
		}
	}

	if ( i == val.bv_len ) {
		return LDAP_SUCCESS;
	}

	/* extract and check criticality */
	if ( strncasecmp( &val.bv_val[ i ], "criticality ", STRLENOF( "criticality " ) ) == 0 )
	{
		i += STRLENOF( "criticality " );
		for ( ; i < val.bv_len; i++ ) {
			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
				break;
			}
		}

		if ( i == val.bv_len ) {
			return LDAP_INVALID_SYNTAX;
		}

		bv.bv_val = &val.bv_val[ i ];

		for ( ; i < val.bv_len; i++ ) {
			if ( ASCII_SPACE( val.bv_val[ i ] ) ) {
				break;
			}
		}

		bv.bv_len = &val.bv_val[ i ] - bv.bv_val;

		if ( !bvmatch( &bv, &slap_true_bv ) && !bvmatch( &bv, &slap_false_bv ) ) 
		{
			return LDAP_INVALID_SYNTAX;
		}

		if ( i == val.bv_len ) {
			return LDAP_SUCCESS;
		}

		if ( val.bv_val[ i ] != ' ' ) {
			return LDAP_INVALID_SYNTAX;
		}

		for ( i++; i < val.bv_len; i++ ) {
			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
				break;
			}
		}

		if ( i == val.bv_len ) {
			return LDAP_SUCCESS;
		}
	}

	/* extract and check controlValue */
	if ( strncasecmp( &val.bv_val[ i ], "controlValue ", STRLENOF( "controlValue " ) ) == 0 )
	{
1177
1178
		ber_len_t valueStart, valueLen;

1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
		i += STRLENOF( "controlValue " );
		for ( ; i < val.bv_len; i++ ) {
			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
				break;
			}
		}

		if ( i == val.bv_len ) {
			return LDAP_INVALID_SYNTAX;
		}

		if ( val.bv_val[ i ] != '"' ) {
			return LDAP_INVALID_SYNTAX;
		}

1194
1195
1196
1197
		i++;
		valueStart = i;

		for ( ; i < val.bv_len; i++ ) {
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
			if ( val.bv_val[ i ] == '"' ) {
				break;
			}

			if ( !ASCII_HEX( val.bv_val[ i ] ) ) {
				return LDAP_INVALID_SYNTAX;
			}
		}

		if ( val.bv_val[ i ] != '"' ) {
			return LDAP_INVALID_SYNTAX;
		}

1211
1212
1213
1214
1215
		valueLen = i - valueStart;
		if ( (valueLen/2)*2 != valueLen ) {
			return LDAP_INVALID_SYNTAX;
		}

1216
		for ( i++; i < val.bv_len; i++ ) {
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
			if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
				break;
			}
		}

		if ( i == val.bv_len ) {
			return LDAP_SUCCESS;
		}
	}

	return LDAP_INVALID_SYNTAX;
}

static int
accesslog_ctrls(
	LDAPControl **ctrls,
	BerVarray *valsp,
	BerVarray *nvalsp,
	void *memctx )
{
	long		i, rc = 0;
1238
1239
1240
1241
1242

	assert( valsp != NULL );
	assert( ctrls != NULL );

	*valsp = NULL;
1243
	*nvalsp = NULL;
1244
1245

	for ( i = 0; ctrls[ i ] != NULL; i++ ) {
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
		struct berval	idx,
				oid,
				noid,
				bv;
		char		*ptr,
				buf[ 32 ];

		if ( ctrls[ i ]->ldctl_oid == NULL ) {
			return LDAP_PROTOCOL_ERROR;
		}
1256

1257
1258
		idx.bv_len = snprintf( buf, sizeof( buf ), "{%ld}", i );
		idx.bv_val = buf;
1259

1260
1261
1262
1263
1264
		ber_str2bv( ctrls[ i ]->ldctl_oid, 0, 0, &oid );
		noid.bv_len = idx.bv_len + oid.bv_len;
		ptr = noid.bv_val = ber_memalloc_x( noid.bv_len + 1, memctx );
		ptr = lutil_strcopy( ptr, idx.bv_val );
		ptr = lutil_strcopy( ptr, oid.bv_val );
1265

1266
1267
1268
1269
1270
		bv.bv_len = idx.bv_len + STRLENOF( "{}" ) + oid.bv_len;

		if ( ctrls[ i ]->ldctl_iscritical ) {
			bv.bv_len += STRLENOF( " criticality TRUE" );
		}
1271

1272
1273
1274
1275
		if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
			bv.bv_len += STRLENOF( " controlValue \"\"" )
				+ 2 * ctrls[ i ]->ldctl_value.bv_len;
		}
1276

1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
		ptr = bv.bv_val = ber_memalloc_x( bv.bv_len + 1, memctx );
		if ( ptr == NULL ) {
			ber_bvarray_free( *valsp );
			*valsp = NULL;
			ber_bvarray_free( *nvalsp );
			*nvalsp = NULL;
			return LDAP_OTHER;
		}

		ptr = lutil_strcopy( ptr, idx.bv_val );

		*ptr++ = '{' /*}*/ ;
		ptr = lutil_strcopy( ptr, oid.bv_val );

		if ( ctrls[ i ]->ldctl_iscritical ) {
			ptr = lutil_strcopy( ptr, " criticality TRUE" );
		}
		
		if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
1296
			ber_len_t	j;
1297
1298

			ptr = lutil_strcopy( ptr, " controlValue \"" );
1299
1300
1301
			for ( j = 0; j < ctrls[ i ]->ldctl_value.bv_len; j++ ) {
				*ptr++ = SLAP_ESCAPE_HI(ctrls[ i ]->ldctl_value.bv_val[ j ]);
				*ptr++ = SLAP_ESCAPE_LO(ctrls[ i ]->ldctl_value.bv_val[ j ]);
1302
			}
1303
1304

			*ptr++ = '"';
1305
1306
		}

1307
1308
1309
1310
1311
		*ptr++ = '}';
		*ptr = '\0';

		ber_bvarray_add_x( valsp, &bv, memctx );
		ber_bvarray_add_x( nvalsp, &noid, memctx );
1312
1313
1314
	}

	return rc;
1315
	
1316
1317
}

1318
1319
static Entry *accesslog_entry( Operation *op, SlapReply *rs,
	log_info *li, int logop, Operation *op2 ) {
Howard Chu's avatar
Howard Chu committed
1320
1321

	char rdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
1322
1323
	char nrdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];

Howard Chu's avatar
Howard Chu committed
1324
	struct berval rdn, nrdn, timestamp, ntimestamp, bv;
Howard Chu's avatar
Howard Chu committed
1325
1326
	slap_verbmasks *lo = logops+logop+EN_OFFSET;

1327
	Entry *e = entry_alloc();
Howard Chu's avatar
Howard Chu committed
1328
1329
1330

	strcpy( rdnbuf, RDNEQ );
	rdn.bv_val = rdnbuf;
1331
1332
	strcpy( nrdnbuf, RDNEQ );
	nrdn.bv_val = nrdnbuf;
Howard Chu's avatar
Howard Chu committed
1333
1334
1335
1336

	timestamp.bv_val = rdnbuf+STRLENOF(RDNEQ);
	timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
	slap_timestamp( &op->o_time, &timestamp );
1337
1338
	snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op->o_tincr );
	timestamp.bv_len += STRLENOF(".123456");
Howard Chu's avatar
Howard Chu committed
1339

Howard Chu's avatar
Howard Chu committed
1340
	rdn.bv_len = STRLENOF(RDNEQ)+timestamp.bv_len;
1341
1342
1343
1344
1345
1346
	ad_reqStart->ad_type->sat_equality->smr_normalize(
		SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, ad_reqStart->ad_type->sat_syntax,
		ad_reqStart->ad_type->sat_equality, &timestamp, &ntimestamp,
		op->o_tmpmemctx );

	strcpy( nrdn.bv_val + STRLENOF(RDNEQ), ntimestamp.bv_val );
Howard Chu's avatar
Howard Chu committed
1347
	nrdn.bv_len = STRLENOF(RDNEQ)+ntimestamp.bv_len;
Howard Chu's avatar
Howard Chu committed
1348
	build_new_dn( &e->e_name, li->li_db->be_suffix, &rdn, NULL );
Howard Chu's avatar
Howard Chu committed
1349
	build_new_dn( &e->e_nname, li->li_db->be_nsuffix, &nrdn, NULL );
Howard Chu's avatar
Howard Chu committed
1350
1351
1352
1353
1354

	attr_merge_one( e, slap_schema.si_ad_objectClass,
		&log_ocs[logop]->soc_cname, NULL );
	attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
		&log_ocs[logop]->soc_cname, NULL );
Howard Chu's avatar
Howard Chu committed
1355
	attr_merge_one( e, ad_reqStart, &timestamp, &ntimestamp );
1356
	op->o_tmpfree( ntimestamp.bv_val, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
1357

Howard Chu's avatar
Howard Chu committed
1358
1359
1360
1361
	slap_op_time( &op2->o_time, &op2->o_tincr );

	timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
	slap_timestamp( &op2->o_time, &timestamp );
1362
1363
	snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op2->o_tincr );
	timestamp.bv_len += STRLENOF(".123456");
Howard Chu's avatar
Howard Chu committed
1364
1365
1366

	attr_merge_normalize_one( e, ad_reqEnd, &timestamp, op->o_tmpmemctx );

Howard Chu's avatar
Howard Chu committed
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
	/* Exops have OID appended */
	if ( logop == LOG_EN_EXTENDED ) {
		bv.bv_len = lo->word.bv_len + op->ore_reqoid.bv_len + 2;
		bv.bv_val = ch_malloc( bv.bv_len + 1 );
		AC_MEMCPY( bv.bv_val, lo->word.bv_val, lo->word.bv_len );
		bv.bv_val[lo->word.bv_len] = '{';
		AC_MEMCPY( bv.bv_val+lo->word.bv_len+1, op->ore_reqoid.bv_val,
			op->ore_reqoid.bv_len );
		bv.bv_val[bv.bv_len-1] = '}';
		bv.bv_val[bv.bv_len] = '\0';
		attr_merge_one( e, ad_reqType, &bv, NULL );
	} else {
		attr_merge_one( e, ad_reqType, &lo->word, NULL );
	}

1382
	rdn.bv_len = s