accesslog.c revision 1.1.1.2
1/* $NetBSD: accesslog.c,v 1.1.1.2 2010/03/08 02:14:20 lukem Exp $ */ 2 3/* accesslog.c - log operations for audit/history purposes */ 4/* OpenLDAP: pkg/ldap/servers/slapd/overlays/accesslog.c,v 1.37.2.25 2009/11/24 05:50:11 quanah Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2005-2009 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 "portable.h" 25 26#ifdef SLAPD_OVER_ACCESSLOG 27 28#include <stdio.h> 29 30#include <ac/string.h> 31#include <ac/ctype.h> 32 33#include "slap.h" 34#include "config.h" 35#include "lutil.h" 36#include "ldap_rq.h" 37 38#define LOG_OP_ADD 0x001 39#define LOG_OP_DELETE 0x002 40#define LOG_OP_MODIFY 0x004 41#define LOG_OP_MODRDN 0x008 42#define LOG_OP_COMPARE 0x010 43#define LOG_OP_SEARCH 0x020 44#define LOG_OP_BIND 0x040 45#define LOG_OP_UNBIND 0x080 46#define LOG_OP_ABANDON 0x100 47#define LOG_OP_EXTENDED 0x200 48#define LOG_OP_UNKNOWN 0x400 49 50#define LOG_OP_WRITES (LOG_OP_ADD|LOG_OP_DELETE|LOG_OP_MODIFY|LOG_OP_MODRDN) 51#define LOG_OP_READS (LOG_OP_COMPARE|LOG_OP_SEARCH) 52#define LOG_OP_SESSION (LOG_OP_BIND|LOG_OP_UNBIND|LOG_OP_ABANDON) 53#define LOG_OP_ALL (LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \ 54 LOG_OP_EXTENDED|LOG_OP_UNKNOWN) 55 56typedef struct log_attr { 57 struct log_attr *next; 58 AttributeDescription *attr; 59} log_attr; 60 61typedef struct log_info { 62 BackendDB *li_db; 63 struct berval li_db_suffix; 64 slap_mask_t li_ops; 65 int li_age; 66 int li_cycle; 67 struct re_s *li_task; 68 Filter *li_oldf; 69 Entry *li_old; 70 log_attr *li_oldattrs; 71 int li_success; 72 ldap_pvt_thread_rmutex_t li_op_rmutex; 73 ldap_pvt_thread_mutex_t li_log_mutex; 74} log_info; 75 76static ConfigDriver log_cf_gen; 77 78enum { 79 LOG_DB = 1, 80 LOG_OPS, 81 LOG_PURGE, 82 LOG_SUCCESS, 83 LOG_OLD, 84 LOG_OLDATTR 85}; 86 87static ConfigTable log_cfats[] = { 88 { "logdb", "suffix", 2, 2, 0, ARG_DN|ARG_MAGIC|LOG_DB, 89 log_cf_gen, "( OLcfgOvAt:4.1 NAME 'olcAccessLogDB' " 90 "DESC 'Suffix of database for log content' " 91 "SUP distinguishedName SINGLE-VALUE )", NULL, NULL }, 92 { "logops", "op|writes|reads|session|all", 2, 0, 0, 93 ARG_MAGIC|LOG_OPS, 94 log_cf_gen, "( OLcfgOvAt:4.2 NAME 'olcAccessLogOps' " 95 "DESC 'Operation types to log' " 96 "EQUALITY caseIgnoreMatch " 97 "SYNTAX OMsDirectoryString )", NULL, NULL }, 98 { "logpurge", "age> <interval", 3, 3, 0, ARG_MAGIC|LOG_PURGE, 99 log_cf_gen, "( OLcfgOvAt:4.3 NAME 'olcAccessLogPurge' " 100 "DESC 'Log cleanup parameters' " 101 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, 102 { "logsuccess", NULL, 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|LOG_SUCCESS, 103 log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' " 104 "DESC 'Log successful ops only' " 105 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 106 { "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD, 107 log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' " 108 "DESC 'Log old values when modifying entries matching the filter' " 109 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, 110 { "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR, 111 log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' " 112 "DESC 'Log old values of these attributes even if unmodified' " 113 "EQUALITY caseIgnoreMatch " 114 "SYNTAX OMsDirectoryString )", NULL, NULL }, 115 { NULL } 116}; 117 118static ConfigOCs log_cfocs[] = { 119 { "( OLcfgOvOc:4.1 " 120 "NAME 'olcAccessLogConfig' " 121 "DESC 'Access log configuration' " 122 "SUP olcOverlayConfig " 123 "MUST olcAccessLogDB " 124 "MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ " 125 "olcAccessLogOld $ olcAccessLogOldAttr ) )", 126 Cft_Overlay, log_cfats }, 127 { NULL } 128}; 129 130static slap_verbmasks logops[] = { 131 { BER_BVC("all"), LOG_OP_ALL }, 132 { BER_BVC("writes"), LOG_OP_WRITES }, 133 { BER_BVC("session"), LOG_OP_SESSION }, 134 { BER_BVC("reads"), LOG_OP_READS }, 135 { BER_BVC("add"), LOG_OP_ADD }, 136 { BER_BVC("delete"), LOG_OP_DELETE }, 137 { BER_BVC("modify"), LOG_OP_MODIFY }, 138 { BER_BVC("modrdn"), LOG_OP_MODRDN }, 139 { BER_BVC("compare"), LOG_OP_COMPARE }, 140 { BER_BVC("search"), LOG_OP_SEARCH }, 141 { BER_BVC("bind"), LOG_OP_BIND }, 142 { BER_BVC("unbind"), LOG_OP_UNBIND }, 143 { BER_BVC("abandon"), LOG_OP_ABANDON }, 144 { BER_BVC("extended"), LOG_OP_EXTENDED }, 145 { BER_BVC("unknown"), LOG_OP_UNKNOWN }, 146 { BER_BVNULL, 0 } 147}; 148 149/* Start with "add" in logops */ 150#define EN_OFFSET 4 151 152enum { 153 LOG_EN_ADD = 0, 154 LOG_EN_DELETE, 155 LOG_EN_MODIFY, 156 LOG_EN_MODRDN, 157 LOG_EN_COMPARE, 158 LOG_EN_SEARCH, 159 LOG_EN_BIND, 160 LOG_EN_UNBIND, 161 LOG_EN_ABANDON, 162 LOG_EN_EXTENDED, 163 LOG_EN_UNKNOWN, 164 LOG_EN__COUNT 165}; 166 167static ObjectClass *log_ocs[LOG_EN__COUNT], *log_container, 168 *log_oc_read, *log_oc_write; 169 170#define LOG_SCHEMA_ROOT "1.3.6.1.4.1.4203.666.11.5" 171 172#define LOG_SCHEMA_AT LOG_SCHEMA_ROOT ".1" 173#define LOG_SCHEMA_OC LOG_SCHEMA_ROOT ".2" 174#define LOG_SCHEMA_SYN LOG_SCHEMA_ROOT ".3" 175 176static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType, 177 *ad_reqSession, *ad_reqResult, *ad_reqAuthzID, *ad_reqControls, 178 *ad_reqRespControls, *ad_reqMethod, *ad_reqAssertion, *ad_reqNewRDN, 179 *ad_reqNewSuperior, *ad_reqDeleteOldRDN, *ad_reqMod, 180 *ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries, 181 *ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData, 182 *ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases, 183 *ad_reqReferral, *ad_reqOld, *ad_auditContext; 184 185static int 186logSchemaControlValidate( 187 Syntax *syntax, 188 struct berval *val ); 189 190char *mrControl[] = { 191 "objectIdentifierFirstComponentMatch", 192 NULL 193}; 194 195static struct { 196 char *oid; 197 slap_syntax_defs_rec syn; 198 char **mrs; 199} lsyntaxes[] = { 200 { LOG_SCHEMA_SYN ".1" , 201 { "( " LOG_SCHEMA_SYN ".1 DESC 'Control' )", 202 SLAP_SYNTAX_HIDE, 203 NULL, 204 logSchemaControlValidate, 205 NULL }, 206 mrControl }, 207 { NULL } 208}; 209 210static struct { 211 char *at; 212 AttributeDescription **ad; 213} lattrs[] = { 214 { "( " LOG_SCHEMA_AT ".1 NAME 'reqDN' " 215 "DESC 'Target DN of request' " 216 "EQUALITY distinguishedNameMatch " 217 "SYNTAX OMsDN " 218 "SINGLE-VALUE )", &ad_reqDN }, 219 { "( " LOG_SCHEMA_AT ".2 NAME 'reqStart' " 220 "DESC 'Start time of request' " 221 "EQUALITY generalizedTimeMatch " 222 "ORDERING generalizedTimeOrderingMatch " 223 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 224 "SINGLE-VALUE )", &ad_reqStart }, 225 { "( " LOG_SCHEMA_AT ".3 NAME 'reqEnd' " 226 "DESC 'End time of request' " 227 "EQUALITY generalizedTimeMatch " 228 "ORDERING generalizedTimeOrderingMatch " 229 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " 230 "SINGLE-VALUE )", &ad_reqEnd }, 231 { "( " LOG_SCHEMA_AT ".4 NAME 'reqType' " 232 "DESC 'Type of request' " 233 "EQUALITY caseIgnoreMatch " 234 "SYNTAX OMsDirectoryString " 235 "SINGLE-VALUE )", &ad_reqType }, 236 { "( " LOG_SCHEMA_AT ".5 NAME 'reqSession' " 237 "DESC 'Session ID of request' " 238 "EQUALITY caseIgnoreMatch " 239 "SYNTAX OMsDirectoryString " 240 "SINGLE-VALUE )", &ad_reqSession }, 241 { "( " LOG_SCHEMA_AT ".6 NAME 'reqAuthzID' " 242 "DESC 'Authorization ID of requestor' " 243 "EQUALITY distinguishedNameMatch " 244 "SYNTAX OMsDN " 245 "SINGLE-VALUE )", &ad_reqAuthzID }, 246 { "( " LOG_SCHEMA_AT ".7 NAME 'reqResult' " 247 "DESC 'Result code of request' " 248 "EQUALITY integerMatch " 249 "ORDERING integerOrderingMatch " 250 "SYNTAX OMsInteger " 251 "SINGLE-VALUE )", &ad_reqResult }, 252 { "( " LOG_SCHEMA_AT ".8 NAME 'reqMessage' " 253 "DESC 'Error text of request' " 254 "EQUALITY caseIgnoreMatch " 255 "SUBSTR caseIgnoreSubstringsMatch " 256 "SYNTAX OMsDirectoryString " 257 "SINGLE-VALUE )", &ad_reqMessage }, 258 { "( " LOG_SCHEMA_AT ".9 NAME 'reqReferral' " 259 "DESC 'Referrals returned for request' " 260 "SUP labeledURI )", &ad_reqReferral }, 261 { "( " LOG_SCHEMA_AT ".10 NAME 'reqControls' " 262 "DESC 'Request controls' " 263 "EQUALITY objectIdentifierFirstComponentMatch " 264 "SYNTAX " LOG_SCHEMA_SYN ".1 " 265 "X-ORDERED 'VALUES' )", &ad_reqControls }, 266 { "( " LOG_SCHEMA_AT ".11 NAME 'reqRespControls' " 267 "DESC 'Response controls of request' " 268 "EQUALITY objectIdentifierFirstComponentMatch " 269 "SYNTAX " LOG_SCHEMA_SYN ".1 " 270 "X-ORDERED 'VALUES' )", &ad_reqRespControls }, 271 { "( " LOG_SCHEMA_AT ".12 NAME 'reqId' " 272 "DESC 'ID of Request to Abandon' " 273 "EQUALITY integerMatch " 274 "ORDERING integerOrderingMatch " 275 "SYNTAX OMsInteger " 276 "SINGLE-VALUE )", &ad_reqId }, 277 { "( " LOG_SCHEMA_AT ".13 NAME 'reqVersion' " 278 "DESC 'Protocol version of Bind request' " 279 "EQUALITY integerMatch " 280 "ORDERING integerOrderingMatch " 281 "SYNTAX OMsInteger " 282 "SINGLE-VALUE )", &ad_reqVersion }, 283 { "( " LOG_SCHEMA_AT ".14 NAME 'reqMethod' " 284 "DESC 'Bind method of request' " 285 "EQUALITY caseIgnoreMatch " 286 "SYNTAX OMsDirectoryString " 287 "SINGLE-VALUE )", &ad_reqMethod }, 288 { "( " LOG_SCHEMA_AT ".15 NAME 'reqAssertion' " 289 "DESC 'Compare Assertion of request' " 290 "SYNTAX OMsDirectoryString " 291 "SINGLE-VALUE )", &ad_reqAssertion }, 292 { "( " LOG_SCHEMA_AT ".16 NAME 'reqMod' " 293 "DESC 'Modifications of request' " 294 "EQUALITY octetStringMatch " 295 "SUBSTR octetStringSubstringsMatch " 296 "SYNTAX OMsOctetString )", &ad_reqMod }, 297 { "( " LOG_SCHEMA_AT ".17 NAME 'reqOld' " 298 "DESC 'Old values of entry before request completed' " 299 "EQUALITY octetStringMatch " 300 "SUBSTR octetStringSubstringsMatch " 301 "SYNTAX OMsOctetString )", &ad_reqOld }, 302 { "( " LOG_SCHEMA_AT ".18 NAME 'reqNewRDN' " 303 "DESC 'New RDN of request' " 304 "EQUALITY distinguishedNameMatch " 305 "SYNTAX OMsDN " 306 "SINGLE-VALUE )", &ad_reqNewRDN }, 307 { "( " LOG_SCHEMA_AT ".19 NAME 'reqDeleteOldRDN' " 308 "DESC 'Delete old RDN' " 309 "EQUALITY booleanMatch " 310 "SYNTAX OMsBoolean " 311 "SINGLE-VALUE )", &ad_reqDeleteOldRDN }, 312 { "( " LOG_SCHEMA_AT ".20 NAME 'reqNewSuperior' " 313 "DESC 'New superior DN of request' " 314 "EQUALITY distinguishedNameMatch " 315 "SYNTAX OMsDN " 316 "SINGLE-VALUE )", &ad_reqNewSuperior }, 317 { "( " LOG_SCHEMA_AT ".21 NAME 'reqScope' " 318 "DESC 'Scope of request' " 319 "EQUALITY caseIgnoreMatch " 320 "SYNTAX OMsDirectoryString " 321 "SINGLE-VALUE )", &ad_reqScope }, 322 { "( " LOG_SCHEMA_AT ".22 NAME 'reqDerefAliases' " 323 "DESC 'Disposition of Aliases in request' " 324 "EQUALITY caseIgnoreMatch " 325 "SYNTAX OMsDirectoryString " 326 "SINGLE-VALUE )", &ad_reqDerefAliases }, 327 { "( " LOG_SCHEMA_AT ".23 NAME 'reqAttrsOnly' " 328 "DESC 'Attributes and values of request' " 329 "EQUALITY booleanMatch " 330 "SYNTAX OMsBoolean " 331 "SINGLE-VALUE )", &ad_reqAttrsOnly }, 332 { "( " LOG_SCHEMA_AT ".24 NAME 'reqFilter' " 333 "DESC 'Filter of request' " 334 "EQUALITY caseIgnoreMatch " 335 "SUBSTR caseIgnoreSubstringsMatch " 336 "SYNTAX OMsDirectoryString " 337 "SINGLE-VALUE )", &ad_reqFilter }, 338 { "( " LOG_SCHEMA_AT ".25 NAME 'reqAttr' " 339 "DESC 'Attributes of request' " 340 "EQUALITY caseIgnoreMatch " 341 "SYNTAX OMsDirectoryString )", &ad_reqAttr }, 342 { "( " LOG_SCHEMA_AT ".26 NAME 'reqSizeLimit' " 343 "DESC 'Size limit of request' " 344 "EQUALITY integerMatch " 345 "ORDERING integerOrderingMatch " 346 "SYNTAX OMsInteger " 347 "SINGLE-VALUE )", &ad_reqSizeLimit }, 348 { "( " LOG_SCHEMA_AT ".27 NAME 'reqTimeLimit' " 349 "DESC 'Time limit of request' " 350 "EQUALITY integerMatch " 351 "ORDERING integerOrderingMatch " 352 "SYNTAX OMsInteger " 353 "SINGLE-VALUE )", &ad_reqTimeLimit }, 354 { "( " LOG_SCHEMA_AT ".28 NAME 'reqEntries' " 355 "DESC 'Number of entries returned' " 356 "EQUALITY integerMatch " 357 "ORDERING integerOrderingMatch " 358 "SYNTAX OMsInteger " 359 "SINGLE-VALUE )", &ad_reqEntries }, 360 { "( " LOG_SCHEMA_AT ".29 NAME 'reqData' " 361 "DESC 'Data of extended request' " 362 "EQUALITY octetStringMatch " 363 "SUBSTR octetStringSubstringsMatch " 364 "SYNTAX OMsOctetString " 365 "SINGLE-VALUE )", &ad_reqData }, 366 367 /* 368 * from <draft-chu-ldap-logschema-01.txt>: 369 * 370 371 ( LOG_SCHEMA_AT .30 NAME 'auditContext' 372 DESC 'DN of auditContainer' 373 EQUALITY distinguishedNameMatch 374 SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 375 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation ) 376 377 * - removed EQUALITY matchingRule 378 * - changed directoryOperation in dSAOperation 379 */ 380 { "( " LOG_SCHEMA_AT ".30 NAME 'auditContext' " 381 "DESC 'DN of auditContainer' " 382 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " 383 "SINGLE-VALUE " 384 "NO-USER-MODIFICATION " 385 "USAGE dSAOperation )", &ad_auditContext }, 386 { NULL, NULL } 387}; 388 389static struct { 390 char *ot; 391 ObjectClass **oc; 392} locs[] = { 393 { "( " LOG_SCHEMA_OC ".0 NAME 'auditContainer' " 394 "DESC 'AuditLog container' " 395 "SUP top STRUCTURAL " 396 "MAY ( cn $ reqStart $ reqEnd ) )", &log_container }, 397 { "( " LOG_SCHEMA_OC ".1 NAME 'auditObject' " 398 "DESC 'OpenLDAP request auditing' " 399 "SUP top STRUCTURAL " 400 "MUST ( reqStart $ reqType $ reqSession ) " 401 "MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $ reqEnd $ " 402 "reqResult $ reqMessage $ reqReferral ) )", 403 &log_ocs[LOG_EN_UNBIND] }, 404 { "( " LOG_SCHEMA_OC ".2 NAME 'auditReadObject' " 405 "DESC 'OpenLDAP read request record' " 406 "SUP auditObject STRUCTURAL )", &log_oc_read }, 407 { "( " LOG_SCHEMA_OC ".3 NAME 'auditWriteObject' " 408 "DESC 'OpenLDAP write request record' " 409 "SUP auditObject STRUCTURAL )", &log_oc_write }, 410 { "( " LOG_SCHEMA_OC ".4 NAME 'auditAbandon' " 411 "DESC 'Abandon operation' " 412 "SUP auditObject STRUCTURAL " 413 "MUST reqId )", &log_ocs[LOG_EN_ABANDON] }, 414 { "( " LOG_SCHEMA_OC ".5 NAME 'auditAdd' " 415 "DESC 'Add operation' " 416 "SUP auditWriteObject STRUCTURAL " 417 "MUST reqMod )", &log_ocs[LOG_EN_ADD] }, 418 { "( " LOG_SCHEMA_OC ".6 NAME 'auditBind' " 419 "DESC 'Bind operation' " 420 "SUP auditObject STRUCTURAL " 421 "MUST ( reqVersion $ reqMethod ) )", &log_ocs[LOG_EN_BIND] }, 422 { "( " LOG_SCHEMA_OC ".7 NAME 'auditCompare' " 423 "DESC 'Compare operation' " 424 "SUP auditReadObject STRUCTURAL " 425 "MUST reqAssertion )", &log_ocs[LOG_EN_COMPARE] }, 426 { "( " LOG_SCHEMA_OC ".8 NAME 'auditDelete' " 427 "DESC 'Delete operation' " 428 "SUP auditWriteObject STRUCTURAL " 429 "MAY reqOld )", &log_ocs[LOG_EN_DELETE] }, 430 { "( " LOG_SCHEMA_OC ".9 NAME 'auditModify' " 431 "DESC 'Modify operation' " 432 "SUP auditWriteObject STRUCTURAL " 433 "MAY reqOld MUST reqMod )", &log_ocs[LOG_EN_MODIFY] }, 434 { "( " LOG_SCHEMA_OC ".10 NAME 'auditModRDN' " 435 "DESC 'ModRDN operation' " 436 "SUP auditWriteObject STRUCTURAL " 437 "MUST ( reqNewRDN $ reqDeleteOldRDN ) " 438 "MAY ( reqNewSuperior $ reqMod $ reqOld ) )", &log_ocs[LOG_EN_MODRDN] }, 439 { "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' " 440 "DESC 'Search operation' " 441 "SUP auditReadObject STRUCTURAL " 442 "MUST ( reqScope $ reqDerefAliases $ reqAttrsonly ) " 443 "MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $ " 444 "reqTimeLimit ) )", &log_ocs[LOG_EN_SEARCH] }, 445 { "( " LOG_SCHEMA_OC ".12 NAME 'auditExtended' " 446 "DESC 'Extended operation' " 447 "SUP auditObject STRUCTURAL " 448 "MAY reqData )", &log_ocs[LOG_EN_EXTENDED] }, 449 { NULL, NULL } 450}; 451 452#define RDNEQ "reqStart=" 453 454/* Our time intervals are of the form [ddd+]hh:mm[:ss] 455 * If a field is present, it must be two digits. (Except for 456 * days, which can be arbitrary width.) 457 */ 458static int 459log_age_parse(char *agestr) 460{ 461 int t1, t2; 462 int gotdays = 0; 463 char *endptr; 464 465 t1 = strtol( agestr, &endptr, 10 ); 466 /* Is there a days delimiter? */ 467 if ( *endptr == '+' ) { 468 /* 32 bit time only covers about 68 years */ 469 if ( t1 < 0 || t1 > 25000 ) 470 return -1; 471 t1 *= 24; 472 gotdays = 1; 473 agestr = endptr + 1; 474 } else { 475 if ( agestr[2] != ':' ) { 476 /* No valid delimiter found, fail */ 477 return -1; 478 } 479 t1 *= 60; 480 agestr += 3; 481 } 482 483 t2 = atoi( agestr ); 484 t1 += t2; 485 486 if ( agestr[2] ) { 487 /* if there's a delimiter, it can only be a colon */ 488 if ( agestr[2] != ':' ) 489 return -1; 490 } else { 491 /* If we're at the end of the string, and we started with days, 492 * fail because we expected to find minutes too. 493 */ 494 return gotdays ? -1 : t1 * 60; 495 } 496 497 agestr += 3; 498 t2 = atoi( agestr ); 499 500 /* last field can only be seconds */ 501 if ( agestr[2] && ( agestr[2] != ':' || !gotdays )) 502 return -1; 503 t1 *= 60; 504 t1 += t2; 505 506 if ( agestr[2] ) { 507 agestr += 3; 508 if ( agestr[2] ) 509 return -1; 510 t1 *= 60; 511 t1 += atoi( agestr ); 512 } else if ( gotdays ) { 513 /* only got days+hh:mm */ 514 t1 *= 60; 515 } 516 return t1; 517} 518 519static void 520log_age_unparse( int age, struct berval *agebv, size_t size ) 521{ 522 int dd, hh, mm, ss, len; 523 char *ptr; 524 525 assert( size > 0 ); 526 527 ss = age % 60; 528 age /= 60; 529 mm = age % 60; 530 age /= 60; 531 hh = age % 24; 532 age /= 24; 533 dd = age; 534 535 ptr = agebv->bv_val; 536 537 if ( dd ) { 538 len = snprintf( ptr, size, "%d+", dd ); 539 assert( len >= 0 && (unsigned) len < size ); 540 size -= len; 541 ptr += len; 542 } 543 len = snprintf( ptr, size, "%02d:%02d", hh, mm ); 544 assert( len >= 0 && (unsigned) len < size ); 545 size -= len; 546 ptr += len; 547 if ( ss ) { 548 len = snprintf( ptr, size, ":%02d", ss ); 549 assert( len >= 0 && (unsigned) len < size ); 550 size -= len; 551 ptr += len; 552 } 553 554 agebv->bv_len = ptr - agebv->bv_val; 555} 556 557static slap_callback nullsc = { NULL, NULL, NULL, NULL }; 558 559#define PURGE_INCREMENT 100 560 561typedef struct purge_data { 562 int slots; 563 int used; 564 BerVarray dn; 565 BerVarray ndn; 566 struct berval csn; /* an arbitrary old CSN */ 567} purge_data; 568 569static int 570log_old_lookup( Operation *op, SlapReply *rs ) 571{ 572 purge_data *pd = op->o_callback->sc_private; 573 Attribute *a; 574 575 if ( rs->sr_type != REP_SEARCH) return 0; 576 577 if ( slapd_shutdown ) return 0; 578 579 /* Remember max CSN: should always be the last entry 580 * seen, since log entries are ordered chronologically... 581 */ 582 a = attr_find( rs->sr_entry->e_attrs, 583 slap_schema.si_ad_entryCSN ); 584 if ( a ) { 585 ber_len_t len = a->a_nvals[0].bv_len; 586 /* Paranoid len check, normalized CSNs are always the same length */ 587 if ( len > LDAP_LUTIL_CSNSTR_BUFSIZE ) 588 len = LDAP_LUTIL_CSNSTR_BUFSIZE; 589 if ( memcmp( a->a_nvals[0].bv_val, pd->csn.bv_val, len ) > 0 ) { 590 AC_MEMCPY( pd->csn.bv_val, a->a_nvals[0].bv_val, len ); 591 pd->csn.bv_len = len; 592 } 593 } 594 if ( pd->used >= pd->slots ) { 595 pd->slots += PURGE_INCREMENT; 596 pd->dn = ch_realloc( pd->dn, pd->slots * sizeof( struct berval )); 597 pd->ndn = ch_realloc( pd->ndn, pd->slots * sizeof( struct berval )); 598 } 599 ber_dupbv( &pd->dn[pd->used], &rs->sr_entry->e_name ); 600 ber_dupbv( &pd->ndn[pd->used], &rs->sr_entry->e_nname ); 601 pd->used++; 602 return 0; 603} 604 605/* Periodically search for old entries in the log database and delete them */ 606static void * 607accesslog_purge( void *ctx, void *arg ) 608{ 609 struct re_s *rtask = arg; 610 struct log_info *li = rtask->arg; 611 612 Connection conn = {0}; 613 OperationBuffer opbuf; 614 Operation *op; 615 SlapReply rs = {REP_RESULT}; 616 slap_callback cb = { NULL, log_old_lookup, NULL, NULL }; 617 Filter f; 618 AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; 619 purge_data pd = {0}; 620 char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE]; 621 char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE]; 622 time_t old = slap_get_time(); 623 624 connection_fake_init( &conn, &opbuf, ctx ); 625 op = &opbuf.ob_op; 626 627 f.f_choice = LDAP_FILTER_LE; 628 f.f_ava = &ava; 629 f.f_next = NULL; 630 631 ava.aa_desc = ad_reqStart; 632 ava.aa_value.bv_val = timebuf; 633 ava.aa_value.bv_len = sizeof(timebuf); 634 635 old -= li->li_age; 636 slap_timestamp( &old, &ava.aa_value ); 637 638 op->o_tag = LDAP_REQ_SEARCH; 639 op->o_bd = li->li_db; 640 op->o_dn = li->li_db->be_rootdn; 641 op->o_ndn = li->li_db->be_rootndn; 642 op->o_req_dn = li->li_db->be_suffix[0]; 643 op->o_req_ndn = li->li_db->be_nsuffix[0]; 644 op->o_callback = &cb; 645 op->ors_scope = LDAP_SCOPE_ONELEVEL; 646 op->ors_deref = LDAP_DEREF_NEVER; 647 op->ors_tlimit = SLAP_NO_LIMIT; 648 op->ors_slimit = SLAP_NO_LIMIT; 649 op->ors_filter = &f; 650 filter2bv_x( op, &f, &op->ors_filterstr ); 651 op->ors_attrs = slap_anlist_no_attrs; 652 op->ors_attrsonly = 1; 653 654 pd.csn.bv_len = sizeof( csnbuf ); 655 pd.csn.bv_val = csnbuf; 656 csnbuf[0] = '\0'; 657 cb.sc_private = &pd; 658 659 op->o_bd->be_search( op, &rs ); 660 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 661 662 if ( pd.used ) { 663 int i; 664 665 /* delete the expired entries */ 666 op->o_tag = LDAP_REQ_DELETE; 667 op->o_callback = &nullsc; 668 op->o_csn = pd.csn; 669 670 for (i=0; i<pd.used; i++) { 671 op->o_req_dn = pd.dn[i]; 672 op->o_req_ndn = pd.ndn[i]; 673 if ( !slapd_shutdown ) 674 op->o_bd->be_delete( op, &rs ); 675 ch_free( pd.ndn[i].bv_val ); 676 ch_free( pd.dn[i].bv_val ); 677 } 678 ch_free( pd.ndn ); 679 ch_free( pd.dn ); 680 681 { 682 Modifications mod; 683 struct berval bv[2]; 684 /* update context's entryCSN to reflect oldest CSN */ 685 mod.sml_numvals = 1; 686 mod.sml_values = bv; 687 bv[0] = pd.csn; 688 BER_BVZERO(&bv[1]); 689 mod.sml_nvalues = NULL; 690 mod.sml_desc = slap_schema.si_ad_entryCSN; 691 mod.sml_op = LDAP_MOD_REPLACE; 692 mod.sml_flags = SLAP_MOD_INTERNAL; 693 mod.sml_next = NULL; 694 695 op->o_tag = LDAP_REQ_MODIFY; 696 op->orm_modlist = &mod; 697 op->orm_no_opattrs = 1; 698 op->o_req_dn = li->li_db->be_suffix[0]; 699 op->o_req_ndn = li->li_db->be_nsuffix[0]; 700 op->o_no_schema_check = 1; 701 op->o_managedsait = SLAP_CONTROL_NONCRITICAL; 702 op->o_bd->be_modify( op, &rs ); 703 if ( mod.sml_next ) { 704 slap_mods_free( mod.sml_next, 1 ); 705 } 706 } 707 } 708 709 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 710 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask ); 711 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 712 713 return NULL; 714} 715 716static int 717log_cf_gen(ConfigArgs *c) 718{ 719 slap_overinst *on = (slap_overinst *)c->bi; 720 struct log_info *li = on->on_bi.bi_private; 721 int rc = 0; 722 slap_mask_t tmask = 0; 723 char agebuf[2*STRLENOF("ddddd+hh:mm:ss ")]; 724 struct berval agebv, cyclebv; 725 726 switch( c->op ) { 727 case SLAP_CONFIG_EMIT: 728 switch( c->type ) { 729 case LOG_DB: 730 if ( !BER_BVISEMPTY( &li->li_db_suffix )) { 731 value_add_one( &c->rvalue_vals, &li->li_db_suffix ); 732 value_add_one( &c->rvalue_nvals, &li->li_db_suffix ); 733 } else if ( li->li_db ) { 734 value_add_one( &c->rvalue_vals, li->li_db->be_suffix ); 735 value_add_one( &c->rvalue_nvals, li->li_db->be_nsuffix ); 736 } else { 737 snprintf( c->cr_msg, sizeof( c->cr_msg ), 738 "accesslog: \"logdb <suffix>\" must be specified" ); 739 Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n", 740 c->log, c->cr_msg, c->value_dn.bv_val ); 741 rc = 1; 742 break; 743 } 744 break; 745 case LOG_OPS: 746 rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals ); 747 break; 748 case LOG_PURGE: 749 if ( !li->li_age ) { 750 rc = 1; 751 break; 752 } 753 agebv.bv_val = agebuf; 754 log_age_unparse( li->li_age, &agebv, sizeof( agebuf ) ); 755 agebv.bv_val[agebv.bv_len] = ' '; 756 agebv.bv_len++; 757 cyclebv.bv_val = agebv.bv_val + agebv.bv_len; 758 log_age_unparse( li->li_cycle, &cyclebv, sizeof( agebuf ) - agebv.bv_len ); 759 agebv.bv_len += cyclebv.bv_len; 760 value_add_one( &c->rvalue_vals, &agebv ); 761 break; 762 case LOG_SUCCESS: 763 if ( li->li_success ) 764 c->value_int = li->li_success; 765 else 766 rc = 1; 767 break; 768 case LOG_OLD: 769 if ( li->li_oldf ) { 770 filter2bv( li->li_oldf, &agebv ); 771 ber_bvarray_add( &c->rvalue_vals, &agebv ); 772 } 773 else 774 rc = 1; 775 break; 776 case LOG_OLDATTR: 777 if ( li->li_oldattrs ) { 778 log_attr *la; 779 780 for ( la = li->li_oldattrs; la; la=la->next ) 781 value_add_one( &c->rvalue_vals, &la->attr->ad_cname ); 782 } 783 else 784 rc = 1; 785 break; 786 } 787 break; 788 case LDAP_MOD_DELETE: 789 switch( c->type ) { 790 case LOG_DB: 791 /* noop. this should always be a valid backend. */ 792 break; 793 case LOG_OPS: 794 if ( c->valx < 0 ) { 795 li->li_ops = 0; 796 } else { 797 rc = verbs_to_mask( 1, &c->line, logops, &tmask ); 798 if ( rc == 0 ) 799 li->li_ops &= ~tmask; 800 } 801 break; 802 case LOG_PURGE: 803 if ( li->li_task ) { 804 struct re_s *re = li->li_task; 805 li->li_task = NULL; 806 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 807 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re )) 808 ldap_pvt_runqueue_stoptask( &slapd_rq, re ); 809 ldap_pvt_runqueue_remove( &slapd_rq, re ); 810 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 811 } 812 li->li_age = 0; 813 li->li_cycle = 0; 814 break; 815 case LOG_SUCCESS: 816 li->li_success = 0; 817 break; 818 case LOG_OLD: 819 if ( li->li_oldf ) { 820 filter_free( li->li_oldf ); 821 li->li_oldf = NULL; 822 } 823 break; 824 case LOG_OLDATTR: 825 if ( c->valx < 0 ) { 826 log_attr *la, *ln; 827 828 for ( la = li->li_oldattrs; la; la = ln ) { 829 ln = la->next; 830 ch_free( la ); 831 } 832 } else { 833 log_attr *la = NULL, **lp; 834 int i; 835 836 for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) { 837 la = *lp; 838 lp = &la->next; 839 } 840 *lp = la->next; 841 ch_free( la ); 842 } 843 break; 844 } 845 break; 846 default: 847 switch( c->type ) { 848 case LOG_DB: 849 if ( CONFIG_ONLINE_ADD( c )) { 850 li->li_db = select_backend( &c->value_ndn, 0 ); 851 if ( !li->li_db ) { 852 snprintf( c->cr_msg, sizeof( c->cr_msg ), 853 "<%s> no matching backend found for suffix", 854 c->argv[0] ); 855 Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n", 856 c->log, c->cr_msg, c->value_dn.bv_val ); 857 rc = 1; 858 } 859 ch_free( c->value_ndn.bv_val ); 860 } else { 861 li->li_db_suffix = c->value_ndn; 862 } 863 ch_free( c->value_dn.bv_val ); 864 break; 865 case LOG_OPS: 866 rc = verbs_to_mask( c->argc, c->argv, logops, &tmask ); 867 if ( rc == 0 ) 868 li->li_ops |= tmask; 869 break; 870 case LOG_PURGE: 871 li->li_age = log_age_parse( c->argv[1] ); 872 if ( li->li_age < 1 ) { 873 rc = 1; 874 } else { 875 li->li_cycle = log_age_parse( c->argv[2] ); 876 if ( li->li_cycle < 1 ) { 877 rc = 1; 878 } else if ( slapMode & SLAP_SERVER_MODE ) { 879 struct re_s *re = li->li_task; 880 if ( re ) 881 re->interval.tv_sec = li->li_cycle; 882 else { 883 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 884 li->li_task = ldap_pvt_runqueue_insert( &slapd_rq, 885 li->li_cycle, accesslog_purge, li, 886 "accesslog_purge", li->li_db ? 887 li->li_db->be_suffix[0].bv_val : 888 c->be->be_suffix[0].bv_val ); 889 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 890 } 891 } 892 } 893 break; 894 case LOG_SUCCESS: 895 li->li_success = c->value_int; 896 break; 897 case LOG_OLD: 898 li->li_oldf = str2filter( c->argv[1] ); 899 if ( !li->li_oldf ) { 900 snprintf( c->cr_msg, sizeof( c->cr_msg ), "bad filter!" ); 901 rc = 1; 902 } 903 break; 904 case LOG_OLDATTR: { 905 int i; 906 AttributeDescription *ad; 907 const char *text; 908 909 for ( i=1; i< c->argc; i++ ) { 910 ad = NULL; 911 if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) { 912 log_attr *la = ch_malloc( sizeof( log_attr )); 913 la->attr = ad; 914 la->next = li->li_oldattrs; 915 li->li_oldattrs = la; 916 } else { 917 snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s", 918 c->argv[0], c->argv[i], text ); 919 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 920 "%s: %s\n", c->log, c->cr_msg, 0 ); 921 rc = ARG_BAD_CONF; 922 break; 923 } 924 } 925 } 926 break; 927 } 928 break; 929 } 930 return rc; 931} 932 933static int 934logSchemaControlValidate( 935 Syntax *syntax, 936 struct berval *valp ) 937{ 938 struct berval val, bv; 939 ber_len_t i; 940 int rc = LDAP_SUCCESS; 941 942 assert( valp != NULL ); 943 944 val = *valp; 945 946 /* check minimal size */ 947 if ( val.bv_len < STRLENOF( "{*}" ) ) { 948 return LDAP_INVALID_SYNTAX; 949 } 950 951 val.bv_len--; 952 953 /* check SEQUENCE boundaries */ 954 if ( val.bv_val[ 0 ] != '{' /*}*/ || 955 val.bv_val[ val.bv_len ] != /*{*/ '}' ) 956 { 957 return LDAP_INVALID_SYNTAX; 958 } 959 960 /* extract and check OID */ 961 for ( i = 1; i < val.bv_len; i++ ) { 962 if ( !ASCII_SPACE( val.bv_val[ i ] ) ) { 963 break; 964 } 965 } 966 967 bv.bv_val = &val.bv_val[ i ]; 968 969 for ( i++; i < val.bv_len; i++ ) { 970 if ( ASCII_SPACE( val.bv_val[ i ] ) ) 971 { 972 break; 973 } 974 } 975 976 bv.bv_len = &val.bv_val[ i ] - bv.bv_val; 977 978 rc = numericoidValidate( NULL, &bv ); 979 if ( rc != LDAP_SUCCESS ) { 980 return rc; 981 } 982 983 if ( i == val.bv_len ) { 984 return LDAP_SUCCESS; 985 } 986 987 if ( val.bv_val[ i ] != ' ' ) { 988 return LDAP_INVALID_SYNTAX; 989 } 990 991 for ( i++; i < val.bv_len; i++ ) { 992 if ( !ASCII_SPACE( val.bv_val[ i ] ) ) { 993 break; 994 } 995 } 996 997 if ( i == val.bv_len ) { 998 return LDAP_SUCCESS; 999 } 1000 1001 /* extract and check criticality */ 1002 if ( strncasecmp( &val.bv_val[ i ], "criticality ", STRLENOF( "criticality " ) ) == 0 ) 1003 { 1004 i += STRLENOF( "criticality " ); 1005 for ( ; i < val.bv_len; i++ ) { 1006 if ( !ASCII_SPACE( val.bv_val[ i ] ) ) { 1007 break; 1008 } 1009 } 1010 1011 if ( i == val.bv_len ) { 1012 return LDAP_INVALID_SYNTAX; 1013 } 1014 1015 bv.bv_val = &val.bv_val[ i ]; 1016 1017 for ( ; i < val.bv_len; i++ ) { 1018 if ( ASCII_SPACE( val.bv_val[ i ] ) ) { 1019 break; 1020 } 1021 } 1022 1023 bv.bv_len = &val.bv_val[ i ] - bv.bv_val; 1024 1025 if ( !bvmatch( &bv, &slap_true_bv ) && !bvmatch( &bv, &slap_false_bv ) ) 1026 { 1027 return LDAP_INVALID_SYNTAX; 1028 } 1029 1030 if ( i == val.bv_len ) { 1031 return LDAP_SUCCESS; 1032 } 1033 1034 if ( val.bv_val[ i ] != ' ' ) { 1035 return LDAP_INVALID_SYNTAX; 1036 } 1037 1038 for ( i++; i < val.bv_len; i++ ) { 1039 if ( !ASCII_SPACE( val.bv_val[ i ] ) ) { 1040 break; 1041 } 1042 } 1043 1044 if ( i == val.bv_len ) { 1045 return LDAP_SUCCESS; 1046 } 1047 } 1048 1049 /* extract and check controlValue */ 1050 if ( strncasecmp( &val.bv_val[ i ], "controlValue ", STRLENOF( "controlValue " ) ) == 0 ) 1051 { 1052 i += STRLENOF( "controlValue " ); 1053 for ( ; i < val.bv_len; i++ ) { 1054 if ( !ASCII_SPACE( val.bv_val[ i ] ) ) { 1055 break; 1056 } 1057 } 1058 1059 if ( i == val.bv_len ) { 1060 return LDAP_INVALID_SYNTAX; 1061 } 1062 1063 if ( val.bv_val[ i ] != '"' ) { 1064 return LDAP_INVALID_SYNTAX; 1065 } 1066 1067 for ( ; i < val.bv_len; i++ ) { 1068 if ( val.bv_val[ i ] == '"' ) { 1069 break; 1070 } 1071 1072 if ( !ASCII_HEX( val.bv_val[ i ] ) ) { 1073 return LDAP_INVALID_SYNTAX; 1074 } 1075 } 1076 1077 if ( val.bv_val[ i ] != '"' ) { 1078 return LDAP_INVALID_SYNTAX; 1079 } 1080 1081 for ( ; i < val.bv_len; i++ ) { 1082 if ( !ASCII_SPACE( val.bv_val[ i ] ) ) { 1083 break; 1084 } 1085 } 1086 1087 if ( i == val.bv_len ) { 1088 return LDAP_SUCCESS; 1089 } 1090 } 1091 1092 return LDAP_INVALID_SYNTAX; 1093} 1094 1095static int 1096accesslog_ctrls( 1097 LDAPControl **ctrls, 1098 BerVarray *valsp, 1099 BerVarray *nvalsp, 1100 void *memctx ) 1101{ 1102 long i, rc = 0; 1103 1104 assert( valsp != NULL ); 1105 assert( ctrls != NULL ); 1106 1107 *valsp = NULL; 1108 *nvalsp = NULL; 1109 1110 for ( i = 0; ctrls[ i ] != NULL; i++ ) { 1111 struct berval idx, 1112 oid, 1113 noid, 1114 bv; 1115 char *ptr, 1116 buf[ 32 ]; 1117 1118 if ( ctrls[ i ]->ldctl_oid == NULL ) { 1119 return LDAP_PROTOCOL_ERROR; 1120 } 1121 1122 idx.bv_len = snprintf( buf, sizeof( buf ), "{%ld}", i ); 1123 idx.bv_val = buf; 1124 1125 ber_str2bv( ctrls[ i ]->ldctl_oid, 0, 0, &oid ); 1126 noid.bv_len = idx.bv_len + oid.bv_len; 1127 ptr = noid.bv_val = ber_memalloc_x( noid.bv_len + 1, memctx ); 1128 ptr = lutil_strcopy( ptr, idx.bv_val ); 1129 ptr = lutil_strcopy( ptr, oid.bv_val ); 1130 1131 bv.bv_len = idx.bv_len + STRLENOF( "{}" ) + oid.bv_len; 1132 1133 if ( ctrls[ i ]->ldctl_iscritical ) { 1134 bv.bv_len += STRLENOF( " criticality TRUE" ); 1135 } 1136 1137 if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) { 1138 bv.bv_len += STRLENOF( " controlValue \"\"" ) 1139 + 2 * ctrls[ i ]->ldctl_value.bv_len; 1140 } 1141 1142 ptr = bv.bv_val = ber_memalloc_x( bv.bv_len + 1, memctx ); 1143 if ( ptr == NULL ) { 1144 ber_bvarray_free( *valsp ); 1145 *valsp = NULL; 1146 ber_bvarray_free( *nvalsp ); 1147 *nvalsp = NULL; 1148 return LDAP_OTHER; 1149 } 1150 1151 ptr = lutil_strcopy( ptr, idx.bv_val ); 1152 1153 *ptr++ = '{' /*}*/ ; 1154 ptr = lutil_strcopy( ptr, oid.bv_val ); 1155 1156 if ( ctrls[ i ]->ldctl_iscritical ) { 1157 ptr = lutil_strcopy( ptr, " criticality TRUE" ); 1158 } 1159 1160 if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) { 1161 ber_len_t j; 1162 1163 ptr = lutil_strcopy( ptr, " controlValue \"" ); 1164 for ( j = 0; j < ctrls[ i ]->ldctl_value.bv_len; j++ ) 1165 { 1166 unsigned char o; 1167 1168 o = ( ( ctrls[ i ]->ldctl_value.bv_val[ j ] >> 4 ) & 0xF ); 1169 if ( o < 10 ) { 1170 *ptr++ = '0' + o; 1171 1172 } else { 1173 *ptr++ = 'A' + o; 1174 } 1175 1176 o = ( ctrls[ i ]->ldctl_value.bv_val[ j ] & 0xF ); 1177 if ( o < 10 ) { 1178 *ptr++ = '0' + o; 1179 1180 } else { 1181 *ptr++ = 'A' + o; 1182 } 1183 } 1184 1185 *ptr++ = '"'; 1186 } 1187 1188 *ptr++ = '}'; 1189 *ptr = '\0'; 1190 1191 ber_bvarray_add_x( valsp, &bv, memctx ); 1192 ber_bvarray_add_x( nvalsp, &noid, memctx ); 1193 } 1194 1195 return rc; 1196 1197} 1198 1199static Entry *accesslog_entry( Operation *op, SlapReply *rs, int logop, 1200 Operation *op2 ) { 1201 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1202 log_info *li = on->on_bi.bi_private; 1203 1204 char rdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8]; 1205 char nrdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8]; 1206 1207 struct berval rdn, nrdn, timestamp, ntimestamp, bv; 1208 slap_verbmasks *lo = logops+logop+EN_OFFSET; 1209 1210 Entry *e = entry_alloc(); 1211 1212 strcpy( rdnbuf, RDNEQ ); 1213 rdn.bv_val = rdnbuf; 1214 strcpy( nrdnbuf, RDNEQ ); 1215 nrdn.bv_val = nrdnbuf; 1216 1217 timestamp.bv_val = rdnbuf+STRLENOF(RDNEQ); 1218 timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ); 1219 slap_timestamp( &op->o_time, ×tamp ); 1220 snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op->o_tincr ); 1221 timestamp.bv_len += STRLENOF(".123456"); 1222 1223 rdn.bv_len = STRLENOF(RDNEQ)+timestamp.bv_len; 1224 ad_reqStart->ad_type->sat_equality->smr_normalize( 1225 SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, ad_reqStart->ad_type->sat_syntax, 1226 ad_reqStart->ad_type->sat_equality, ×tamp, &ntimestamp, 1227 op->o_tmpmemctx ); 1228 1229 strcpy( nrdn.bv_val + STRLENOF(RDNEQ), ntimestamp.bv_val ); 1230 nrdn.bv_len = STRLENOF(RDNEQ)+ntimestamp.bv_len; 1231 build_new_dn( &e->e_name, li->li_db->be_suffix, &rdn, NULL ); 1232 build_new_dn( &e->e_nname, li->li_db->be_nsuffix, &nrdn, NULL ); 1233 1234 attr_merge_one( e, slap_schema.si_ad_objectClass, 1235 &log_ocs[logop]->soc_cname, NULL ); 1236 attr_merge_one( e, slap_schema.si_ad_structuralObjectClass, 1237 &log_ocs[logop]->soc_cname, NULL ); 1238 attr_merge_one( e, ad_reqStart, ×tamp, &ntimestamp ); 1239 op->o_tmpfree( ntimestamp.bv_val, op->o_tmpmemctx ); 1240 1241 slap_op_time( &op2->o_time, &op2->o_tincr ); 1242 1243 timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ); 1244 slap_timestamp( &op2->o_time, ×tamp ); 1245 snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op2->o_tincr ); 1246 timestamp.bv_len += STRLENOF(".123456"); 1247 1248 attr_merge_normalize_one( e, ad_reqEnd, ×tamp, op->o_tmpmemctx ); 1249 1250 /* Exops have OID appended */ 1251 if ( logop == LOG_EN_EXTENDED ) { 1252 bv.bv_len = lo->word.bv_len + op->ore_reqoid.bv_len + 2; 1253 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 1254 AC_MEMCPY( bv.bv_val, lo->word.bv_val, lo->word.bv_len ); 1255 bv.bv_val[lo->word.bv_len] = '{'; 1256 AC_MEMCPY( bv.bv_val+lo->word.bv_len+1, op->ore_reqoid.bv_val, 1257 op->ore_reqoid.bv_len ); 1258 bv.bv_val[bv.bv_len-1] = '}'; 1259 bv.bv_val[bv.bv_len] = '\0'; 1260 attr_merge_one( e, ad_reqType, &bv, NULL ); 1261 } else { 1262 attr_merge_one( e, ad_reqType, &lo->word, NULL ); 1263 } 1264 1265 rdn.bv_len = snprintf( rdn.bv_val, sizeof( rdnbuf ), "%lu", op->o_connid ); 1266 if ( rdn.bv_len >= 0 || rdn.bv_len < sizeof( rdnbuf ) ) { 1267 attr_merge_one( e, ad_reqSession, &rdn, NULL ); 1268 } /* else? */ 1269 1270 if ( BER_BVISNULL( &op->o_dn ) ) { 1271 attr_merge_one( e, ad_reqAuthzID, (struct berval *)&slap_empty_bv, 1272 (struct berval *)&slap_empty_bv ); 1273 } else { 1274 attr_merge_one( e, ad_reqAuthzID, &op->o_dn, &op->o_ndn ); 1275 } 1276 1277 /* FIXME: need to add reqControls and reqRespControls */ 1278 if ( op->o_ctrls ) { 1279 BerVarray vals = NULL, 1280 nvals = NULL; 1281 1282 if ( accesslog_ctrls( op->o_ctrls, &vals, &nvals, 1283 op->o_tmpmemctx ) == LDAP_SUCCESS && vals ) 1284 { 1285 attr_merge( e, ad_reqControls, vals, nvals ); 1286 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 1287 ber_bvarray_free_x( nvals, op->o_tmpmemctx ); 1288 } 1289 } 1290 1291 if ( rs->sr_ctrls ) { 1292 BerVarray vals = NULL, 1293 nvals = NULL; 1294 1295 if ( accesslog_ctrls( rs->sr_ctrls, &vals, &nvals, 1296 op->o_tmpmemctx ) == LDAP_SUCCESS && vals ) 1297 { 1298 attr_merge( e, ad_reqRespControls, vals, nvals ); 1299 ber_bvarray_free_x( vals, op->o_tmpmemctx ); 1300 ber_bvarray_free_x( nvals, op->o_tmpmemctx ); 1301 } 1302 1303 } 1304 1305 return e; 1306} 1307 1308static struct berval scopes[] = { 1309 BER_BVC("base"), 1310 BER_BVC("one"), 1311 BER_BVC("sub"), 1312 BER_BVC("subord") 1313}; 1314 1315static struct berval derefs[] = { 1316 BER_BVC("never"), 1317 BER_BVC("searching"), 1318 BER_BVC("finding"), 1319 BER_BVC("always") 1320}; 1321 1322static struct berval simple = BER_BVC("SIMPLE"); 1323 1324static void accesslog_val2val(AttributeDescription *ad, struct berval *val, 1325 char c_op, struct berval *dst) { 1326 char *ptr; 1327 1328 dst->bv_len = ad->ad_cname.bv_len + val->bv_len + 2; 1329 if ( c_op ) dst->bv_len++; 1330 1331 dst->bv_val = ch_malloc( dst->bv_len+1 ); 1332 1333 ptr = lutil_strcopy( dst->bv_val, ad->ad_cname.bv_val ); 1334 *ptr++ = ':'; 1335 if ( c_op ) 1336 *ptr++ = c_op; 1337 *ptr++ = ' '; 1338 AC_MEMCPY( ptr, val->bv_val, val->bv_len ); 1339 dst->bv_val[dst->bv_len] = '\0'; 1340} 1341 1342static int accesslog_response(Operation *op, SlapReply *rs) { 1343 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1344 log_info *li = on->on_bi.bi_private; 1345 Attribute *a, *last_attr; 1346 Modifications *m; 1347 struct berval *b; 1348 int i; 1349 int logop; 1350 slap_verbmasks *lo; 1351 Entry *e = NULL, *old = NULL; 1352 char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE+8]; 1353 struct berval bv; 1354 char *ptr; 1355 BerVarray vals; 1356 Operation op2 = {0}; 1357 SlapReply rs2 = {REP_RESULT}; 1358 1359 if ( rs->sr_type != REP_RESULT && rs->sr_type != REP_EXTENDED ) 1360 return SLAP_CB_CONTINUE; 1361 1362 switch ( op->o_tag ) { 1363 case LDAP_REQ_ADD: logop = LOG_EN_ADD; break; 1364 case LDAP_REQ_DELETE: logop = LOG_EN_DELETE; break; 1365 case LDAP_REQ_MODIFY: logop = LOG_EN_MODIFY; break; 1366 case LDAP_REQ_MODRDN: logop = LOG_EN_MODRDN; break; 1367 case LDAP_REQ_COMPARE: logop = LOG_EN_COMPARE; break; 1368 case LDAP_REQ_SEARCH: logop = LOG_EN_SEARCH; break; 1369 case LDAP_REQ_BIND: logop = LOG_EN_BIND; break; 1370 case LDAP_REQ_EXTENDED: logop = LOG_EN_EXTENDED; break; 1371 default: /* unknown operation type */ 1372 logop = LOG_EN_UNKNOWN; break; 1373 } /* Unbind and Abandon never reach here */ 1374 1375 lo = logops+logop+EN_OFFSET; 1376 if ( !( li->li_ops & lo->mask )) 1377 return SLAP_CB_CONTINUE; 1378 1379 if ( lo->mask & LOG_OP_WRITES ) { 1380 slap_callback *cb; 1381 ldap_pvt_thread_mutex_lock( &li->li_log_mutex ); 1382 old = li->li_old; 1383 li->li_old = NULL; 1384 /* Disarm mod_cleanup */ 1385 for ( cb = op->o_callback; cb; cb = cb->sc_next ) { 1386 if ( cb->sc_private == (void *)on ) { 1387 cb->sc_private = NULL; 1388 break; 1389 } 1390 } 1391 ldap_pvt_thread_rmutex_unlock( &li->li_op_rmutex, op->o_tid ); 1392 } 1393 1394 if ( li->li_success && rs->sr_err != LDAP_SUCCESS ) 1395 goto done; 1396 1397 e = accesslog_entry( op, rs, logop, &op2 ); 1398 1399 attr_merge_one( e, ad_reqDN, &op->o_req_dn, &op->o_req_ndn ); 1400 1401 if ( rs->sr_text ) { 1402 ber_str2bv( rs->sr_text, 0, 0, &bv ); 1403 attr_merge_one( e, ad_reqMessage, &bv, NULL ); 1404 } 1405 bv.bv_len = snprintf( timebuf, sizeof( timebuf ), "%d", rs->sr_err ); 1406 if ( bv.bv_len < sizeof( timebuf ) ) { 1407 bv.bv_val = timebuf; 1408 attr_merge_one( e, ad_reqResult, &bv, NULL ); 1409 } 1410 1411 last_attr = attr_find( e->e_attrs, ad_reqResult ); 1412 1413 switch( logop ) { 1414 case LOG_EN_ADD: 1415 case LOG_EN_DELETE: { 1416 char c_op; 1417 Entry *e2; 1418 1419 if ( logop == LOG_EN_ADD ) { 1420 e2 = op->ora_e; 1421 c_op = '+'; 1422 } else { 1423 if ( !old ) 1424 break; 1425 e2 = old; 1426 c_op = 0; 1427 } 1428 /* count all the vals */ 1429 i = 0; 1430 for ( a=e2->e_attrs; a; a=a->a_next ) { 1431 i += a->a_numvals; 1432 } 1433 vals = ch_malloc( (i+1) * sizeof( struct berval )); 1434 i = 0; 1435 for ( a=e2->e_attrs; a; a=a->a_next ) { 1436 if ( a->a_vals ) { 1437 for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) { 1438 accesslog_val2val( a->a_desc, b, c_op, &vals[i] ); 1439 } 1440 } 1441 } 1442 vals[i].bv_val = NULL; 1443 vals[i].bv_len = 0; 1444 a = attr_alloc( logop == LOG_EN_ADD ? ad_reqMod : ad_reqOld ); 1445 a->a_numvals = i; 1446 a->a_vals = vals; 1447 a->a_nvals = vals; 1448 last_attr->a_next = a; 1449 break; 1450 } 1451 1452 case LOG_EN_MODRDN: 1453 case LOG_EN_MODIFY: 1454 /* count all the mods */ 1455 i = 0; 1456 for ( m = op->orm_modlist; m; m = m->sml_next ) { 1457 if ( m->sml_values ) { 1458 i += m->sml_numvals; 1459 } else if ( m->sml_op == LDAP_MOD_DELETE || 1460 m->sml_op == LDAP_MOD_REPLACE ) 1461 { 1462 i++; 1463 } 1464 } 1465 vals = ch_malloc( (i+1) * sizeof( struct berval )); 1466 i = 0; 1467 1468 /* init flags on old entry */ 1469 if ( old ) { 1470 for ( a = old->e_attrs; a; a = a->a_next ) { 1471 log_attr *la; 1472 a->a_flags = 0; 1473 1474 /* look for attrs that are always logged */ 1475 for ( la = li->li_oldattrs; la; la = la->next ) { 1476 if ( a->a_desc == la->attr ) { 1477 a->a_flags = 1; 1478 } 1479 } 1480 } 1481 } 1482 1483 for ( m = op->orm_modlist; m; m = m->sml_next ) { 1484 /* Mark this attribute as modified */ 1485 if ( old ) { 1486 a = attr_find( old->e_attrs, m->sml_desc ); 1487 if ( a ) { 1488 a->a_flags = 1; 1489 } 1490 } 1491 1492 /* don't log the RDN mods; they're explicitly logged later */ 1493 if ( logop == LOG_EN_MODRDN && 1494 ( m->sml_op == SLAP_MOD_SOFTADD || 1495 m->sml_op == LDAP_MOD_DELETE ) ) 1496 { 1497 continue; 1498 } 1499 1500 if ( m->sml_values ) { 1501 for ( b = m->sml_values; !BER_BVISNULL( b ); b++, i++ ) { 1502 char c_op; 1503 1504 switch ( m->sml_op ) { 1505 case LDAP_MOD_ADD: c_op = '+'; break; 1506 case LDAP_MOD_DELETE: c_op = '-'; break; 1507 case LDAP_MOD_REPLACE: c_op = '='; break; 1508 case LDAP_MOD_INCREMENT: c_op = '#'; break; 1509 1510 /* unknown op. there shouldn't be any of these. we 1511 * don't know what to do with it, but we shouldn't just 1512 * ignore it. 1513 */ 1514 default: c_op = '?'; break; 1515 } 1516 accesslog_val2val( m->sml_desc, b, c_op, &vals[i] ); 1517 } 1518 } else if ( m->sml_op == LDAP_MOD_DELETE || 1519 m->sml_op == LDAP_MOD_REPLACE ) 1520 { 1521 vals[i].bv_len = m->sml_desc->ad_cname.bv_len + 2; 1522 vals[i].bv_val = ch_malloc( vals[i].bv_len + 1 ); 1523 ptr = lutil_strcopy( vals[i].bv_val, 1524 m->sml_desc->ad_cname.bv_val ); 1525 *ptr++ = ':'; 1526 if ( m->sml_op == LDAP_MOD_DELETE ) { 1527 *ptr++ = '-'; 1528 } else { 1529 *ptr++ = '='; 1530 } 1531 *ptr = '\0'; 1532 i++; 1533 } 1534 } 1535 1536 if ( i > 0 ) { 1537 BER_BVZERO( &vals[i] ); 1538 a = attr_alloc( ad_reqMod ); 1539 a->a_numvals = i; 1540 a->a_vals = vals; 1541 a->a_nvals = vals; 1542 last_attr->a_next = a; 1543 last_attr = a; 1544 1545 } else { 1546 ch_free( vals ); 1547 } 1548 1549 if ( old ) { 1550 /* count all the vals */ 1551 i = 0; 1552 for ( a = old->e_attrs; a != NULL; a = a->a_next ) { 1553 if ( a->a_vals && a->a_flags ) { 1554 i += a->a_numvals; 1555 } 1556 } 1557 if ( i ) { 1558 vals = ch_malloc( (i + 1) * sizeof( struct berval ) ); 1559 i = 0; 1560 for ( a=old->e_attrs; a; a=a->a_next ) { 1561 if ( a->a_vals && a->a_flags ) { 1562 for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) { 1563 accesslog_val2val( a->a_desc, b, 0, &vals[i] ); 1564 } 1565 } 1566 } 1567 vals[i].bv_val = NULL; 1568 vals[i].bv_len = 0; 1569 a = attr_alloc( ad_reqOld ); 1570 a->a_numvals = i; 1571 a->a_vals = vals; 1572 a->a_nvals = vals; 1573 last_attr->a_next = a; 1574 } 1575 } 1576 if ( logop == LOG_EN_MODIFY ) { 1577 break; 1578 } 1579 1580 /* Now log the actual modRDN info */ 1581 attr_merge_one( e, ad_reqNewRDN, &op->orr_newrdn, &op->orr_nnewrdn ); 1582 attr_merge_one( e, ad_reqDeleteOldRDN, op->orr_deleteoldrdn ? 1583 (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv, 1584 NULL ); 1585 if ( op->orr_newSup ) { 1586 attr_merge_one( e, ad_reqNewSuperior, op->orr_newSup, op->orr_nnewSup ); 1587 } 1588 break; 1589 1590 case LOG_EN_COMPARE: 1591 bv.bv_len = op->orc_ava->aa_desc->ad_cname.bv_len + 1 + 1592 op->orc_ava->aa_value.bv_len; 1593 bv.bv_val = op->o_tmpalloc( bv.bv_len+1, op->o_tmpmemctx ); 1594 ptr = lutil_strcopy( bv.bv_val, op->orc_ava->aa_desc->ad_cname.bv_val ); 1595 *ptr++ = '='; 1596 AC_MEMCPY( ptr, op->orc_ava->aa_value.bv_val, op->orc_ava->aa_value.bv_len ); 1597 bv.bv_val[bv.bv_len] = '\0'; 1598 attr_merge_one( e, ad_reqAssertion, &bv, NULL ); 1599 op->o_tmpfree( bv.bv_val, op->o_tmpmemctx ); 1600 break; 1601 1602 case LOG_EN_SEARCH: 1603 attr_merge_one( e, ad_reqScope, &scopes[op->ors_scope], NULL ); 1604 attr_merge_one( e, ad_reqDerefAliases, &derefs[op->ors_deref], NULL ); 1605 attr_merge_one( e, ad_reqAttrsOnly, op->ors_attrsonly ? 1606 (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv, 1607 NULL ); 1608 if ( !BER_BVISEMPTY( &op->ors_filterstr )) 1609 attr_merge_one( e, ad_reqFilter, &op->ors_filterstr, NULL ); 1610 if ( op->ors_attrs ) { 1611 /* count them */ 1612 for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++) 1613 ; 1614 vals = op->o_tmpalloc( (i+1) * sizeof(struct berval), 1615 op->o_tmpmemctx ); 1616 for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++) 1617 vals[i] = op->ors_attrs[i].an_name; 1618 vals[i].bv_val = NULL; 1619 vals[i].bv_len = 0; 1620 attr_merge( e, ad_reqAttr, vals, NULL ); 1621 op->o_tmpfree( vals, op->o_tmpmemctx ); 1622 } 1623 bv.bv_val = timebuf; 1624 bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", rs->sr_nentries ); 1625 if ( bv.bv_len < sizeof( timebuf ) ) { 1626 attr_merge_one( e, ad_reqEntries, &bv, NULL ); 1627 } /* else? */ 1628 1629 bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_tlimit ); 1630 if ( bv.bv_len < sizeof( timebuf ) ) { 1631 attr_merge_one( e, ad_reqTimeLimit, &bv, NULL ); 1632 } /* else? */ 1633 1634 bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_slimit ); 1635 if ( bv.bv_len < sizeof( timebuf ) ) { 1636 attr_merge_one( e, ad_reqSizeLimit, &bv, NULL ); 1637 } /* else? */ 1638 break; 1639 1640 case LOG_EN_BIND: 1641 bv.bv_val = timebuf; 1642 bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->o_protocol ); 1643 if ( bv.bv_len < sizeof( timebuf ) ) { 1644 attr_merge_one( e, ad_reqVersion, &bv, NULL ); 1645 } /* else? */ 1646 if ( op->orb_method == LDAP_AUTH_SIMPLE ) { 1647 attr_merge_one( e, ad_reqMethod, &simple, NULL ); 1648 } else { 1649 bv.bv_len = STRLENOF("SASL()") + op->orb_mech.bv_len; 1650 bv.bv_val = op->o_tmpalloc( bv.bv_len + 1, op->o_tmpmemctx ); 1651 ptr = lutil_strcopy( bv.bv_val, "SASL(" ); 1652 ptr = lutil_strcopy( ptr, op->orb_mech.bv_val ); 1653 *ptr++ = ')'; 1654 *ptr = '\0'; 1655 attr_merge_one( e, ad_reqMethod, &bv, NULL ); 1656 op->o_tmpfree( bv.bv_val, op->o_tmpmemctx ); 1657 } 1658 1659 break; 1660 1661 case LOG_EN_EXTENDED: 1662 if ( op->ore_reqdata ) { 1663 attr_merge_one( e, ad_reqData, op->ore_reqdata, NULL ); 1664 } 1665 break; 1666 1667 case LOG_EN_UNKNOWN: 1668 /* we don't know its parameters, don't add any */ 1669 break; 1670 } 1671 1672 op2.o_hdr = op->o_hdr; 1673 op2.o_tag = LDAP_REQ_ADD; 1674 op2.o_bd = li->li_db; 1675 op2.o_dn = li->li_db->be_rootdn; 1676 op2.o_ndn = li->li_db->be_rootndn; 1677 op2.o_req_dn = e->e_name; 1678 op2.o_req_ndn = e->e_nname; 1679 op2.ora_e = e; 1680 op2.o_callback = &nullsc; 1681 1682 if (( lo->mask & LOG_OP_WRITES ) && !BER_BVISEMPTY( &op->o_csn )) { 1683 slap_queue_csn( &op2, &op->o_csn ); 1684 } 1685 1686 op2.o_bd->be_add( &op2, &rs2 ); 1687 if ( e == op2.ora_e ) entry_free( e ); 1688 e = NULL; 1689 1690done: 1691 if ( lo->mask & LOG_OP_WRITES ) 1692 ldap_pvt_thread_mutex_unlock( &li->li_log_mutex ); 1693 if ( old ) entry_free( old ); 1694 return SLAP_CB_CONTINUE; 1695} 1696 1697/* Since Bind success is sent by the frontend, it won't normally enter 1698 * the overlay response callback. Add another callback to make sure it 1699 * gets here. 1700 */ 1701static int 1702accesslog_bind_resp( Operation *op, SlapReply *rs ) 1703{ 1704 BackendDB *be, db; 1705 int rc; 1706 slap_callback *sc; 1707 1708 be = op->o_bd; 1709 db = *be; 1710 op->o_bd = &db; 1711 db.bd_info = op->o_callback->sc_private; 1712 rc = accesslog_response( op, rs ); 1713 op->o_bd = be; 1714 sc = op->o_callback; 1715 op->o_callback = sc->sc_next; 1716 op->o_tmpfree( sc, op->o_tmpmemctx ); 1717 return rc; 1718} 1719 1720static int 1721accesslog_op_bind( Operation *op, SlapReply *rs ) 1722{ 1723 slap_callback *sc; 1724 1725 sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx ); 1726 sc->sc_response = accesslog_bind_resp; 1727 sc->sc_private = op->o_bd->bd_info; 1728 1729 if ( op->o_callback ) { 1730 sc->sc_next = op->o_callback->sc_next; 1731 op->o_callback->sc_next = sc; 1732 } else { 1733 op->o_callback = sc; 1734 } 1735 return SLAP_CB_CONTINUE; 1736} 1737 1738static int 1739accesslog_mod_cleanup( Operation *op, SlapReply *rs ) 1740{ 1741 slap_callback *sc = op->o_callback; 1742 slap_overinst *on = sc->sc_private; 1743 op->o_callback = sc->sc_next; 1744 1745 op->o_tmpfree( sc, op->o_tmpmemctx ); 1746 1747 if ( on ) { 1748 BackendInfo *bi = op->o_bd->bd_info; 1749 op->o_bd->bd_info = (BackendInfo *)on; 1750 accesslog_response( op, rs ); 1751 op->o_bd->bd_info = bi; 1752 } 1753 return 0; 1754} 1755 1756static int 1757accesslog_op_mod( Operation *op, SlapReply *rs ) 1758{ 1759 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1760 log_info *li = on->on_bi.bi_private; 1761 1762 if ( li->li_ops & LOG_OP_WRITES ) { 1763 slap_callback *cb = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx ), *cb2; 1764 cb->sc_cleanup = accesslog_mod_cleanup; 1765 cb->sc_response = NULL; 1766 cb->sc_private = on; 1767 cb->sc_next = NULL; 1768 for ( cb2 = op->o_callback; cb2->sc_next; cb2 = cb2->sc_next ); 1769 cb2->sc_next = cb; 1770 1771 ldap_pvt_thread_rmutex_lock( &li->li_op_rmutex, op->o_tid ); 1772 if ( li->li_oldf && ( op->o_tag == LDAP_REQ_DELETE || 1773 op->o_tag == LDAP_REQ_MODIFY || 1774 ( op->o_tag == LDAP_REQ_MODRDN && li->li_oldattrs ))) { 1775 int rc; 1776 Entry *e; 1777 1778 op->o_bd->bd_info = (BackendInfo *)on->on_info; 1779 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 1780 if ( e ) { 1781 if ( test_filter( op, e, li->li_oldf ) == LDAP_COMPARE_TRUE ) 1782 li->li_old = entry_dup( e ); 1783 be_entry_release_rw( op, e, 0 ); 1784 } 1785 op->o_bd->bd_info = (BackendInfo *)on; 1786 } 1787 } 1788 return SLAP_CB_CONTINUE; 1789} 1790 1791/* unbinds are broadcast to all backends; we only log it if this 1792 * backend was used for the original bind. 1793 */ 1794static int 1795accesslog_unbind( Operation *op, SlapReply *rs ) 1796{ 1797 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1798 if ( op->o_conn->c_authz_backend == on->on_info->oi_origdb ) { 1799 log_info *li = on->on_bi.bi_private; 1800 Operation op2 = {0}; 1801 void *cids[SLAP_MAX_CIDS]; 1802 SlapReply rs2 = {REP_RESULT}; 1803 Entry *e; 1804 1805 if ( !( li->li_ops & LOG_OP_UNBIND )) 1806 return SLAP_CB_CONTINUE; 1807 1808 e = accesslog_entry( op, rs, LOG_EN_UNBIND, &op2 ); 1809 op2.o_hdr = op->o_hdr; 1810 op2.o_tag = LDAP_REQ_ADD; 1811 op2.o_bd = li->li_db; 1812 op2.o_dn = li->li_db->be_rootdn; 1813 op2.o_ndn = li->li_db->be_rootndn; 1814 op2.o_req_dn = e->e_name; 1815 op2.o_req_ndn = e->e_nname; 1816 op2.ora_e = e; 1817 op2.o_callback = &nullsc; 1818 op2.o_controls = cids; 1819 memset(cids, 0, sizeof( cids )); 1820 1821 op2.o_bd->be_add( &op2, &rs2 ); 1822 if ( e == op2.ora_e ) 1823 entry_free( e ); 1824 } 1825 return SLAP_CB_CONTINUE; 1826} 1827 1828static int 1829accesslog_abandon( Operation *op, SlapReply *rs ) 1830{ 1831 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1832 log_info *li = on->on_bi.bi_private; 1833 Operation op2 = {0}; 1834 void *cids[SLAP_MAX_CIDS]; 1835 SlapReply rs2 = {REP_RESULT}; 1836 Entry *e; 1837 char buf[64]; 1838 struct berval bv; 1839 1840 if ( !op->o_time || !( li->li_ops & LOG_OP_ABANDON )) 1841 return SLAP_CB_CONTINUE; 1842 1843 e = accesslog_entry( op, rs, LOG_EN_ABANDON, &op2 ); 1844 bv.bv_val = buf; 1845 bv.bv_len = snprintf( buf, sizeof( buf ), "%d", op->orn_msgid ); 1846 if ( bv.bv_len < sizeof( buf ) ) { 1847 attr_merge_one( e, ad_reqId, &bv, NULL ); 1848 } /* else? */ 1849 1850 op2.o_hdr = op->o_hdr; 1851 op2.o_tag = LDAP_REQ_ADD; 1852 op2.o_bd = li->li_db; 1853 op2.o_dn = li->li_db->be_rootdn; 1854 op2.o_ndn = li->li_db->be_rootndn; 1855 op2.o_req_dn = e->e_name; 1856 op2.o_req_ndn = e->e_nname; 1857 op2.ora_e = e; 1858 op2.o_callback = &nullsc; 1859 op2.o_controls = cids; 1860 memset(cids, 0, sizeof( cids )); 1861 1862 op2.o_bd->be_add( &op2, &rs2 ); 1863 if ( e == op2.ora_e ) 1864 entry_free( e ); 1865 1866 return SLAP_CB_CONTINUE; 1867} 1868 1869static int 1870accesslog_operational( Operation *op, SlapReply *rs ) 1871{ 1872 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1873 log_info *li = on->on_bi.bi_private; 1874 1875 if ( op->o_sync != SLAP_CONTROL_NONE ) 1876 return SLAP_CB_CONTINUE; 1877 1878 if ( rs->sr_entry != NULL 1879 && dn_match( &op->o_bd->be_nsuffix[0], &rs->sr_entry->e_nname ) ) 1880 { 1881 Attribute **ap; 1882 1883 for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) 1884 /* just count */ ; 1885 1886 if ( SLAP_OPATTRS( rs->sr_attr_flags ) || 1887 ad_inlist( ad_auditContext, rs->sr_attrs ) ) 1888 { 1889 *ap = attr_alloc( ad_auditContext ); 1890 attr_valadd( *ap, 1891 &li->li_db->be_suffix[0], 1892 &li->li_db->be_nsuffix[0], 1 ); 1893 } 1894 } 1895 1896 return SLAP_CB_CONTINUE; 1897} 1898 1899static slap_overinst accesslog; 1900 1901static int 1902accesslog_db_init( 1903 BackendDB *be, 1904 ConfigReply *cr 1905) 1906{ 1907 slap_overinst *on = (slap_overinst *)be->bd_info; 1908 log_info *li = ch_calloc(1, sizeof(log_info)); 1909 1910 on->on_bi.bi_private = li; 1911 ldap_pvt_thread_rmutex_init( &li->li_op_rmutex ); 1912 ldap_pvt_thread_mutex_init( &li->li_log_mutex ); 1913 return 0; 1914} 1915 1916static int 1917accesslog_db_destroy( 1918 BackendDB *be, 1919 ConfigReply *cr 1920) 1921{ 1922 slap_overinst *on = (slap_overinst *)be->bd_info; 1923 log_info *li = on->on_bi.bi_private; 1924 log_attr *la; 1925 1926 if ( li->li_oldf ) 1927 filter_free( li->li_oldf ); 1928 for ( la=li->li_oldattrs; la; la=li->li_oldattrs ) { 1929 li->li_oldattrs = la->next; 1930 ch_free( la ); 1931 } 1932 ldap_pvt_thread_mutex_destroy( &li->li_log_mutex ); 1933 ldap_pvt_thread_rmutex_destroy( &li->li_op_rmutex ); 1934 free( li ); 1935 return LDAP_SUCCESS; 1936} 1937 1938/* Create the logdb's root entry if it's missing */ 1939static void * 1940accesslog_db_root( 1941 void *ctx, 1942 void *arg ) 1943{ 1944 struct re_s *rtask = arg; 1945 slap_overinst *on = rtask->arg; 1946 log_info *li = on->on_bi.bi_private; 1947 1948 Connection conn = {0}; 1949 OperationBuffer opbuf; 1950 Operation *op; 1951 1952 Entry *e; 1953 int rc; 1954 1955 connection_fake_init( &conn, &opbuf, ctx ); 1956 op = &opbuf.ob_op; 1957 op->o_bd = li->li_db; 1958 op->o_dn = li->li_db->be_rootdn; 1959 op->o_ndn = li->li_db->be_rootndn; 1960 rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e ); 1961 1962 if ( e ) { 1963 be_entry_release_rw( op, e, 0 ); 1964 1965 } else { 1966 SlapReply rs = {REP_RESULT}; 1967 struct berval rdn, nrdn, attr; 1968 char *ptr; 1969 AttributeDescription *ad = NULL; 1970 const char *text = NULL; 1971 Entry *e_ctx; 1972 1973 e = entry_alloc(); 1974 ber_dupbv( &e->e_name, li->li_db->be_suffix ); 1975 ber_dupbv( &e->e_nname, li->li_db->be_nsuffix ); 1976 1977 attr_merge_one( e, slap_schema.si_ad_objectClass, 1978 &log_container->soc_cname, NULL ); 1979 1980 dnRdn( &e->e_name, &rdn ); 1981 dnRdn( &e->e_nname, &nrdn ); 1982 ptr = ber_bvchr( &rdn, '=' ); 1983 1984 assert( ptr != NULL ); 1985 1986 attr.bv_val = rdn.bv_val; 1987 attr.bv_len = ptr - rdn.bv_val; 1988 1989 slap_bv2ad( &attr, &ad, &text ); 1990 1991 rdn.bv_val = ptr+1; 1992 rdn.bv_len -= attr.bv_len + 1; 1993 ptr = ber_bvchr( &nrdn, '=' ); 1994 nrdn.bv_len -= ptr - nrdn.bv_val + 1; 1995 nrdn.bv_val = ptr+1; 1996 attr_merge_one( e, ad, &rdn, &nrdn ); 1997 1998 /* Get contextCSN from main DB */ 1999 op->o_bd = on->on_info->oi_origdb; 2000 rc = be_entry_get_rw( op, op->o_bd->be_nsuffix, NULL, 2001 slap_schema.si_ad_contextCSN, 0, &e_ctx ); 2002 2003 if ( e_ctx ) { 2004 Attribute *a; 2005 2006 a = attr_find( e_ctx->e_attrs, slap_schema.si_ad_contextCSN ); 2007 if ( a ) { 2008 /* FIXME: contextCSN could have multiple values! 2009 * should select the one with the server's SID */ 2010 attr_merge_one( e, slap_schema.si_ad_entryCSN, 2011 &a->a_vals[0], &a->a_nvals[0] ); 2012 attr_merge( e, a->a_desc, a->a_vals, a->a_nvals ); 2013 } 2014 be_entry_release_rw( op, e_ctx, 0 ); 2015 } 2016 op->o_bd = li->li_db; 2017 2018 op->ora_e = e; 2019 op->o_req_dn = e->e_name; 2020 op->o_req_ndn = e->e_nname; 2021 op->o_callback = &nullsc; 2022 SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD; 2023 rc = op->o_bd->be_add( op, &rs ); 2024 SLAP_DBFLAGS( op->o_bd ) ^= SLAP_DBFLAG_NOLASTMOD; 2025 if ( e == op->ora_e ) 2026 entry_free( e ); 2027 } 2028 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 2029 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask ); 2030 ldap_pvt_runqueue_remove( &slapd_rq, rtask ); 2031 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 2032 2033 return NULL; 2034} 2035 2036static int 2037accesslog_db_open( 2038 BackendDB *be, 2039 ConfigReply *cr 2040) 2041{ 2042 slap_overinst *on = (slap_overinst *)be->bd_info; 2043 log_info *li = on->on_bi.bi_private; 2044 2045 2046 if ( !BER_BVISEMPTY( &li->li_db_suffix )) { 2047 li->li_db = select_backend( &li->li_db_suffix, 0 ); 2048 ch_free( li->li_db_suffix.bv_val ); 2049 BER_BVZERO( &li->li_db_suffix ); 2050 } 2051 if ( li->li_db == NULL ) { 2052 Debug( LDAP_DEBUG_ANY, 2053 "accesslog: \"logdb <suffix>\" missing or invalid.\n", 2054 0, 0, 0 ); 2055 return 1; 2056 } 2057 2058 if ( slapMode & SLAP_TOOL_MODE ) 2059 return 0; 2060 2061 if ( BER_BVISEMPTY( &li->li_db->be_rootndn )) { 2062 ber_dupbv( &li->li_db->be_rootdn, li->li_db->be_suffix ); 2063 ber_dupbv( &li->li_db->be_rootndn, li->li_db->be_nsuffix ); 2064 } 2065 2066 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 2067 ldap_pvt_runqueue_insert( &slapd_rq, 3600, accesslog_db_root, on, 2068 "accesslog_db_root", li->li_db->be_suffix[0].bv_val ); 2069 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 2070 2071 return 0; 2072} 2073 2074int accesslog_initialize() 2075{ 2076 int i, rc; 2077 2078 accesslog.on_bi.bi_type = "accesslog"; 2079 accesslog.on_bi.bi_db_init = accesslog_db_init; 2080 accesslog.on_bi.bi_db_destroy = accesslog_db_destroy; 2081 accesslog.on_bi.bi_db_open = accesslog_db_open; 2082 2083 accesslog.on_bi.bi_op_add = accesslog_op_mod; 2084 accesslog.on_bi.bi_op_bind = accesslog_op_bind; 2085 accesslog.on_bi.bi_op_delete = accesslog_op_mod; 2086 accesslog.on_bi.bi_op_modify = accesslog_op_mod; 2087 accesslog.on_bi.bi_op_modrdn = accesslog_op_mod; 2088 accesslog.on_bi.bi_op_unbind = accesslog_unbind; 2089 accesslog.on_bi.bi_op_abandon = accesslog_abandon; 2090 accesslog.on_bi.bi_operational = accesslog_operational; 2091 accesslog.on_response = accesslog_response; 2092 2093 accesslog.on_bi.bi_cf_ocs = log_cfocs; 2094 2095 nullsc.sc_response = slap_null_cb; 2096 2097 rc = config_register_schema( log_cfats, log_cfocs ); 2098 if ( rc ) return rc; 2099 2100 /* log schema integration */ 2101 for ( i=0; lsyntaxes[i].oid; i++ ) { 2102 int code; 2103 2104 code = register_syntax( &lsyntaxes[ i ].syn ); 2105 if ( code != 0 ) { 2106 Debug( LDAP_DEBUG_ANY, 2107 "accesslog_init: register_syntax failed\n", 2108 0, 0, 0 ); 2109 return code; 2110 } 2111 2112 if ( lsyntaxes[i].mrs != NULL ) { 2113 code = mr_make_syntax_compat_with_mrs( 2114 lsyntaxes[i].oid, lsyntaxes[i].mrs ); 2115 if ( code < 0 ) { 2116 Debug( LDAP_DEBUG_ANY, 2117 "accesslog_init: " 2118 "mr_make_syntax_compat_with_mrs " 2119 "failed\n", 2120 0, 0, 0 ); 2121 return code; 2122 } 2123 } 2124 } 2125 2126 for ( i=0; lattrs[i].at; i++ ) { 2127 int code; 2128 2129 code = register_at( lattrs[i].at, lattrs[i].ad, 0 ); 2130 if ( code ) { 2131 Debug( LDAP_DEBUG_ANY, 2132 "accesslog_init: register_at failed\n", 2133 0, 0, 0 ); 2134 return -1; 2135 } 2136#ifndef LDAP_DEVEL 2137 (*lattrs[i].ad)->ad_type->sat_flags |= SLAP_AT_HIDE; 2138#endif 2139 } 2140 2141 for ( i=0; locs[i].ot; i++ ) { 2142 int code; 2143 2144 code = register_oc( locs[i].ot, locs[i].oc, 0 ); 2145 if ( code ) { 2146 Debug( LDAP_DEBUG_ANY, 2147 "accesslog_init: register_oc failed\n", 2148 0, 0, 0 ); 2149 return -1; 2150 } 2151#ifndef LDAP_DEVEL 2152 (*locs[i].oc)->soc_flags |= SLAP_OC_HIDE; 2153#endif 2154 } 2155 2156 return overlay_register(&accesslog); 2157} 2158 2159#if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC 2160int 2161init_module( int argc, char *argv[] ) 2162{ 2163 return accesslog_initialize(); 2164} 2165#endif 2166 2167#endif /* SLAPD_OVER_ACCESSLOG */ 2168