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