Back to index

openldap  2.4.31
accesslog.c
Go to the documentation of this file.
00001 /* accesslog.c - log operations for audit/history purposes */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2005-2012 The OpenLDAP Foundation.
00006  * Portions copyright 2004-2005 Symas Corporation.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by Howard Chu for inclusion in
00019  * OpenLDAP Software.
00020  */
00021 
00022 #include "portable.h"
00023 
00024 #ifdef SLAPD_OVER_ACCESSLOG
00025 
00026 #include <stdio.h>
00027 
00028 #include <ac/string.h>
00029 #include <ac/ctype.h>
00030 
00031 #include "slap.h"
00032 #include "config.h"
00033 #include "lutil.h"
00034 #include "ldap_rq.h"
00035 
00036 #define LOG_OP_ADD   0x001
00037 #define LOG_OP_DELETE       0x002
00038 #define       LOG_OP_MODIFY 0x004
00039 #define LOG_OP_MODRDN       0x008
00040 #define       LOG_OP_COMPARE       0x010
00041 #define       LOG_OP_SEARCH 0x020
00042 #define LOG_OP_BIND  0x040
00043 #define LOG_OP_UNBIND       0x080
00044 #define       LOG_OP_ABANDON       0x100
00045 #define       LOG_OP_EXTENDED      0x200
00046 #define LOG_OP_UNKNOWN      0x400
00047 
00048 #define       LOG_OP_WRITES (LOG_OP_ADD|LOG_OP_DELETE|LOG_OP_MODIFY|LOG_OP_MODRDN)
00049 #define       LOG_OP_READS  (LOG_OP_COMPARE|LOG_OP_SEARCH)
00050 #define       LOG_OP_SESSION       (LOG_OP_BIND|LOG_OP_UNBIND|LOG_OP_ABANDON)
00051 #define LOG_OP_ALL          (LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \
00052        LOG_OP_EXTENDED|LOG_OP_UNKNOWN)
00053 
00054 typedef struct log_attr {
00055        struct log_attr *next;
00056        AttributeDescription *attr;
00057 } log_attr;
00058 
00059 typedef struct log_base {
00060        struct log_base *lb_next;
00061        slap_mask_t lb_ops;
00062        struct berval lb_base;
00063        struct berval lb_line;
00064 } log_base;
00065 
00066 typedef struct log_info {
00067        BackendDB *li_db;
00068        struct berval li_db_suffix;
00069        slap_mask_t li_ops;
00070        int li_age;
00071        int li_cycle;
00072        struct re_s *li_task;
00073        Filter *li_oldf;
00074        Entry *li_old;
00075        log_attr *li_oldattrs;
00076        struct berval li_uuid;
00077        int li_success;
00078        log_base *li_bases;
00079        ldap_pvt_thread_rmutex_t li_op_rmutex;
00080        ldap_pvt_thread_mutex_t li_log_mutex;
00081 } log_info;
00082 
00083 static ConfigDriver log_cf_gen;
00084 
00085 enum {
00086        LOG_DB = 1,
00087        LOG_OPS,
00088        LOG_PURGE,
00089        LOG_SUCCESS,
00090        LOG_OLD,
00091        LOG_OLDATTR,
00092        LOG_BASE
00093 };
00094 
00095 static ConfigTable log_cfats[] = {
00096        { "logdb", "suffix", 2, 2, 0, ARG_DN|ARG_MAGIC|LOG_DB,
00097               log_cf_gen, "( OLcfgOvAt:4.1 NAME 'olcAccessLogDB' "
00098                      "DESC 'Suffix of database for log content' "
00099                      "SUP distinguishedName SINGLE-VALUE )", NULL, NULL },
00100        { "logops", "op|writes|reads|session|all", 2, 0, 0,
00101               ARG_MAGIC|LOG_OPS,
00102               log_cf_gen, "( OLcfgOvAt:4.2 NAME 'olcAccessLogOps' "
00103                      "DESC 'Operation types to log' "
00104                      "EQUALITY caseIgnoreMatch "
00105                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00106        { "logpurge", "age> <interval", 3, 3, 0, ARG_MAGIC|LOG_PURGE,
00107               log_cf_gen, "( OLcfgOvAt:4.3 NAME 'olcAccessLogPurge' "
00108                      "DESC 'Log cleanup parameters' "
00109                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
00110        { "logsuccess", NULL, 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|LOG_SUCCESS,
00111               log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' "
00112                      "DESC 'Log successful ops only' "
00113                      "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
00114        { "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD,
00115               log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' "
00116                      "DESC 'Log old values when modifying entries matching the filter' "
00117                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
00118        { "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR,
00119               log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' "
00120                      "DESC 'Log old values of these attributes even if unmodified' "
00121                      "EQUALITY caseIgnoreMatch "
00122                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00123        { "logbase", "op|writes|reads|session|all< <baseDN", 3, 3, 0,
00124               ARG_MAGIC|LOG_BASE,
00125               log_cf_gen, "( OLcfgOvAt:4.7 NAME 'olcAccessLogBase' "
00126                      "DESC 'Operation types to log under a specific branch' "
00127                      "EQUALITY caseIgnoreMatch "
00128                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00129        { NULL }
00130 };
00131 
00132 static ConfigOCs log_cfocs[] = {
00133        { "( OLcfgOvOc:4.1 "
00134               "NAME 'olcAccessLogConfig' "
00135               "DESC 'Access log configuration' "
00136               "SUP olcOverlayConfig "
00137               "MUST olcAccessLogDB "
00138               "MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ "
00139                      "olcAccessLogOld $ olcAccessLogOldAttr $ olcAccessLogBase ) )",
00140                      Cft_Overlay, log_cfats },
00141        { NULL }
00142 };
00143 
00144 static slap_verbmasks logops[] = {
00145        { BER_BVC("all"),           LOG_OP_ALL },
00146        { BER_BVC("writes"), LOG_OP_WRITES },
00147        { BER_BVC("session"),       LOG_OP_SESSION },
00148        { BER_BVC("reads"),         LOG_OP_READS },
00149        { BER_BVC("add"),           LOG_OP_ADD },
00150        { BER_BVC("delete"), LOG_OP_DELETE },
00151        { BER_BVC("modify"), LOG_OP_MODIFY },
00152        { BER_BVC("modrdn"), LOG_OP_MODRDN },
00153        { BER_BVC("compare"),       LOG_OP_COMPARE },
00154        { BER_BVC("search"), LOG_OP_SEARCH },
00155        { BER_BVC("bind"),          LOG_OP_BIND },
00156        { BER_BVC("unbind"), LOG_OP_UNBIND },
00157        { BER_BVC("abandon"),       LOG_OP_ABANDON },
00158        { BER_BVC("extended"),      LOG_OP_EXTENDED },
00159        { BER_BVC("unknown"),       LOG_OP_UNKNOWN },
00160        { BER_BVNULL, 0 }
00161 };
00162 
00163 /* Start with "add" in logops */
00164 #define EN_OFFSET    4
00165 
00166 enum {
00167        LOG_EN_ADD = 0,
00168        LOG_EN_DELETE,
00169        LOG_EN_MODIFY,
00170        LOG_EN_MODRDN,
00171        LOG_EN_COMPARE,
00172        LOG_EN_SEARCH,
00173        LOG_EN_BIND,
00174        LOG_EN_UNBIND,
00175        LOG_EN_ABANDON,
00176        LOG_EN_EXTENDED,
00177        LOG_EN_UNKNOWN,
00178        LOG_EN__COUNT
00179 };
00180 
00181 static ObjectClass *log_ocs[LOG_EN__COUNT], *log_container,
00182        *log_oc_read, *log_oc_write;
00183 
00184 #define LOG_SCHEMA_ROOT     "1.3.6.1.4.1.4203.666.11.5"
00185 
00186 #define LOG_SCHEMA_AT LOG_SCHEMA_ROOT ".1"
00187 #define LOG_SCHEMA_OC LOG_SCHEMA_ROOT ".2"
00188 #define LOG_SCHEMA_SYN LOG_SCHEMA_ROOT ".3"
00189 
00190 static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType,
00191        *ad_reqSession, *ad_reqResult, *ad_reqAuthzID, *ad_reqControls,
00192        *ad_reqRespControls, *ad_reqMethod, *ad_reqAssertion, *ad_reqNewRDN,
00193        *ad_reqNewSuperior, *ad_reqDeleteOldRDN, *ad_reqMod,
00194        *ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries,
00195        *ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData,
00196        *ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases,
00197        *ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID;
00198 
00199 static int
00200 logSchemaControlValidate(
00201        Syntax        *syntax,
00202        struct berval *val );
00203 
00204 char   *mrControl[] = {
00205        "objectIdentifierFirstComponentMatch",
00206        NULL
00207 };
00208 
00209 static struct {
00210        char                 *oid;
00211        slap_syntax_defs_rec syn;
00212        char                 **mrs;
00213 } lsyntaxes[] = {
00214        { LOG_SCHEMA_SYN ".1" ,
00215               { "( " LOG_SCHEMA_SYN ".1 DESC 'Control' )",
00216                      SLAP_SYNTAX_HIDE,
00217                      NULL,
00218                      logSchemaControlValidate,
00219                      NULL },
00220               mrControl },
00221        { NULL }
00222 };
00223 
00224 static struct {
00225        char *at;
00226        AttributeDescription **ad;
00227 } lattrs[] = {
00228        { "( " LOG_SCHEMA_AT ".1 NAME 'reqDN' "
00229               "DESC 'Target DN of request' "
00230               "EQUALITY distinguishedNameMatch "
00231               "SYNTAX OMsDN "
00232               "SINGLE-VALUE )", &ad_reqDN },
00233        { "( " LOG_SCHEMA_AT ".2 NAME 'reqStart' "
00234               "DESC 'Start time of request' "
00235               "EQUALITY generalizedTimeMatch "
00236               "ORDERING generalizedTimeOrderingMatch "
00237               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
00238               "SINGLE-VALUE )", &ad_reqStart },
00239        { "( " LOG_SCHEMA_AT ".3 NAME 'reqEnd' "
00240               "DESC 'End time of request' "
00241               "EQUALITY generalizedTimeMatch "
00242               "ORDERING generalizedTimeOrderingMatch "
00243               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
00244               "SINGLE-VALUE )", &ad_reqEnd },
00245        { "( " LOG_SCHEMA_AT ".4 NAME 'reqType' "
00246               "DESC 'Type of request' "
00247               "EQUALITY caseIgnoreMatch "
00248               "SYNTAX OMsDirectoryString "
00249               "SINGLE-VALUE )", &ad_reqType },
00250        { "( " LOG_SCHEMA_AT ".5 NAME 'reqSession' "
00251               "DESC 'Session ID of request' "
00252               "EQUALITY caseIgnoreMatch "
00253               "SYNTAX OMsDirectoryString "
00254               "SINGLE-VALUE )", &ad_reqSession },
00255        { "( " LOG_SCHEMA_AT ".6 NAME 'reqAuthzID' "
00256               "DESC 'Authorization ID of requestor' "
00257               "EQUALITY distinguishedNameMatch "
00258               "SYNTAX OMsDN "
00259               "SINGLE-VALUE )", &ad_reqAuthzID },
00260        { "( " LOG_SCHEMA_AT ".7 NAME 'reqResult' "
00261               "DESC 'Result code of request' "
00262               "EQUALITY integerMatch "
00263               "ORDERING integerOrderingMatch "
00264               "SYNTAX OMsInteger "
00265               "SINGLE-VALUE )", &ad_reqResult },
00266        { "( " LOG_SCHEMA_AT ".8 NAME 'reqMessage' "
00267               "DESC 'Error text of request' "
00268               "EQUALITY caseIgnoreMatch "
00269               "SUBSTR caseIgnoreSubstringsMatch "
00270               "SYNTAX OMsDirectoryString "
00271               "SINGLE-VALUE )", &ad_reqMessage },
00272        { "( " LOG_SCHEMA_AT ".9 NAME 'reqReferral' "
00273               "DESC 'Referrals returned for request' "
00274               "SUP labeledURI )", &ad_reqReferral },
00275        { "( " LOG_SCHEMA_AT ".10 NAME 'reqControls' "
00276               "DESC 'Request controls' "
00277               "EQUALITY objectIdentifierFirstComponentMatch "
00278               "SYNTAX " LOG_SCHEMA_SYN ".1 "
00279               "X-ORDERED 'VALUES' )", &ad_reqControls },
00280        { "( " LOG_SCHEMA_AT ".11 NAME 'reqRespControls' "
00281               "DESC 'Response controls of request' "
00282               "EQUALITY objectIdentifierFirstComponentMatch "
00283               "SYNTAX " LOG_SCHEMA_SYN ".1 "
00284               "X-ORDERED 'VALUES' )", &ad_reqRespControls },
00285        { "( " LOG_SCHEMA_AT ".12 NAME 'reqId' "
00286               "DESC 'ID of Request to Abandon' "
00287               "EQUALITY integerMatch "
00288               "ORDERING integerOrderingMatch "
00289               "SYNTAX OMsInteger "
00290               "SINGLE-VALUE )", &ad_reqId },
00291        { "( " LOG_SCHEMA_AT ".13 NAME 'reqVersion' "
00292               "DESC 'Protocol version of Bind request' "
00293               "EQUALITY integerMatch "
00294               "ORDERING integerOrderingMatch "
00295               "SYNTAX OMsInteger "
00296               "SINGLE-VALUE )", &ad_reqVersion },
00297        { "( " LOG_SCHEMA_AT ".14 NAME 'reqMethod' "
00298               "DESC 'Bind method of request' "
00299               "EQUALITY caseIgnoreMatch "
00300               "SYNTAX OMsDirectoryString "
00301               "SINGLE-VALUE )", &ad_reqMethod },
00302        { "( " LOG_SCHEMA_AT ".15 NAME 'reqAssertion' "
00303               "DESC 'Compare Assertion of request' "
00304               "SYNTAX OMsDirectoryString "
00305               "SINGLE-VALUE )", &ad_reqAssertion },
00306        { "( " LOG_SCHEMA_AT ".16 NAME 'reqMod' "
00307               "DESC 'Modifications of request' "
00308               "EQUALITY octetStringMatch "
00309               "SUBSTR octetStringSubstringsMatch "
00310               "SYNTAX OMsOctetString )", &ad_reqMod },
00311        { "( " LOG_SCHEMA_AT ".17 NAME 'reqOld' "
00312               "DESC 'Old values of entry before request completed' "
00313               "EQUALITY octetStringMatch "
00314               "SUBSTR octetStringSubstringsMatch "
00315               "SYNTAX OMsOctetString )", &ad_reqOld },
00316        { "( " LOG_SCHEMA_AT ".18 NAME 'reqNewRDN' "
00317               "DESC 'New RDN of request' "
00318               "EQUALITY distinguishedNameMatch "
00319               "SYNTAX OMsDN "
00320               "SINGLE-VALUE )", &ad_reqNewRDN },
00321        { "( " LOG_SCHEMA_AT ".19 NAME 'reqDeleteOldRDN' "
00322               "DESC 'Delete old RDN' "
00323               "EQUALITY booleanMatch "
00324               "SYNTAX OMsBoolean "
00325               "SINGLE-VALUE )", &ad_reqDeleteOldRDN },
00326        { "( " LOG_SCHEMA_AT ".20 NAME 'reqNewSuperior' "
00327               "DESC 'New superior DN of request' "
00328               "EQUALITY distinguishedNameMatch "
00329               "SYNTAX OMsDN "
00330               "SINGLE-VALUE )", &ad_reqNewSuperior },
00331        { "( " LOG_SCHEMA_AT ".21 NAME 'reqScope' "
00332               "DESC 'Scope of request' "
00333               "EQUALITY caseIgnoreMatch "
00334               "SYNTAX OMsDirectoryString "
00335               "SINGLE-VALUE )", &ad_reqScope },
00336        { "( " LOG_SCHEMA_AT ".22 NAME 'reqDerefAliases' "
00337               "DESC 'Disposition of Aliases in request' "
00338               "EQUALITY caseIgnoreMatch "
00339               "SYNTAX OMsDirectoryString "
00340               "SINGLE-VALUE )", &ad_reqDerefAliases },
00341        { "( " LOG_SCHEMA_AT ".23 NAME 'reqAttrsOnly' "
00342               "DESC 'Attributes and values of request' "
00343               "EQUALITY booleanMatch "
00344               "SYNTAX OMsBoolean "
00345               "SINGLE-VALUE )", &ad_reqAttrsOnly },
00346        { "( " LOG_SCHEMA_AT ".24 NAME 'reqFilter' "
00347               "DESC 'Filter of request' "
00348               "EQUALITY caseIgnoreMatch "
00349               "SUBSTR caseIgnoreSubstringsMatch "
00350               "SYNTAX OMsDirectoryString "
00351               "SINGLE-VALUE )", &ad_reqFilter },
00352        { "( " LOG_SCHEMA_AT ".25 NAME 'reqAttr' "
00353               "DESC 'Attributes of request' "
00354               "EQUALITY caseIgnoreMatch "
00355               "SYNTAX OMsDirectoryString )", &ad_reqAttr },
00356        { "( " LOG_SCHEMA_AT ".26 NAME 'reqSizeLimit' "
00357               "DESC 'Size limit of request' "
00358               "EQUALITY integerMatch "
00359               "ORDERING integerOrderingMatch "
00360               "SYNTAX OMsInteger "
00361               "SINGLE-VALUE )", &ad_reqSizeLimit },
00362        { "( " LOG_SCHEMA_AT ".27 NAME 'reqTimeLimit' "
00363               "DESC 'Time limit of request' "
00364               "EQUALITY integerMatch "
00365               "ORDERING integerOrderingMatch "
00366               "SYNTAX OMsInteger "
00367               "SINGLE-VALUE )", &ad_reqTimeLimit },
00368        { "( " LOG_SCHEMA_AT ".28 NAME 'reqEntries' "
00369               "DESC 'Number of entries returned' "
00370               "EQUALITY integerMatch "
00371               "ORDERING integerOrderingMatch "
00372               "SYNTAX OMsInteger "
00373               "SINGLE-VALUE )", &ad_reqEntries },
00374        { "( " LOG_SCHEMA_AT ".29 NAME 'reqData' "
00375               "DESC 'Data of extended request' "
00376               "EQUALITY octetStringMatch "
00377               "SUBSTR octetStringSubstringsMatch "
00378               "SYNTAX OMsOctetString "
00379               "SINGLE-VALUE )", &ad_reqData },
00380 
00381        /*
00382         * from <draft-chu-ldap-logschema-01.txt>:
00383         *
00384 
00385    ( LOG_SCHEMA_AT .30 NAME 'auditContext'
00386    DESC 'DN of auditContainer'
00387    EQUALITY distinguishedNameMatch
00388    SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
00389    SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
00390 
00391         * - removed EQUALITY matchingRule
00392         * - changed directoryOperation in dSAOperation
00393         */
00394        { "( " LOG_SCHEMA_AT ".30 NAME 'auditContext' "
00395               "DESC 'DN of auditContainer' "
00396               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
00397               "SINGLE-VALUE "
00398               "NO-USER-MODIFICATION "
00399               "USAGE dSAOperation )", &ad_auditContext },
00400 
00401        /*
00402         * ITS#6656
00403         */
00404        { "( " LOG_SCHEMA_AT ".31 NAME 'reqEntryUUID' "
00405               "DESC 'UUID of entry' "
00406               "EQUALITY UUIDMatch "
00407               "ORDERING UUIDOrderingMatch "
00408               "SYNTAX 1.3.6.1.1.16.1 "
00409               "SINGLE-VALUE )", &ad_reqEntryUUID },
00410        { NULL, NULL }
00411 };
00412 
00413 static struct {
00414        char *ot;
00415        ObjectClass **oc;
00416 } locs[] = {
00417        { "( " LOG_SCHEMA_OC ".0 NAME 'auditContainer' "
00418               "DESC 'AuditLog container' "
00419               "SUP top STRUCTURAL "
00420               "MAY ( cn $ reqStart $ reqEnd ) )", &log_container },
00421        { "( " LOG_SCHEMA_OC ".1 NAME 'auditObject' "
00422               "DESC 'OpenLDAP request auditing' "
00423               "SUP top STRUCTURAL "
00424               "MUST ( reqStart $ reqType $ reqSession ) "
00425               "MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $ reqEnd $ "
00426                      "reqResult $ reqMessage $ reqReferral $ reqEntryUUID ) )",
00427                             &log_ocs[LOG_EN_UNBIND] },
00428        { "( " LOG_SCHEMA_OC ".2 NAME 'auditReadObject' "
00429               "DESC 'OpenLDAP read request record' "
00430               "SUP auditObject STRUCTURAL )", &log_oc_read },
00431        { "( " LOG_SCHEMA_OC ".3 NAME 'auditWriteObject' "
00432               "DESC 'OpenLDAP write request record' "
00433               "SUP auditObject STRUCTURAL )", &log_oc_write },
00434        { "( " LOG_SCHEMA_OC ".4 NAME 'auditAbandon' "
00435               "DESC 'Abandon operation' "
00436               "SUP auditObject STRUCTURAL "
00437               "MUST reqId )", &log_ocs[LOG_EN_ABANDON] },
00438        { "( " LOG_SCHEMA_OC ".5 NAME 'auditAdd' "
00439               "DESC 'Add operation' "
00440               "SUP auditWriteObject STRUCTURAL "
00441               "MUST reqMod )", &log_ocs[LOG_EN_ADD] },
00442        { "( " LOG_SCHEMA_OC ".6 NAME 'auditBind' "
00443               "DESC 'Bind operation' "
00444               "SUP auditObject STRUCTURAL "
00445               "MUST ( reqVersion $ reqMethod ) )", &log_ocs[LOG_EN_BIND] },
00446        { "( " LOG_SCHEMA_OC ".7 NAME 'auditCompare' "
00447               "DESC 'Compare operation' "
00448               "SUP auditReadObject STRUCTURAL "
00449               "MUST reqAssertion )", &log_ocs[LOG_EN_COMPARE] },
00450        { "( " LOG_SCHEMA_OC ".8 NAME 'auditDelete' "
00451               "DESC 'Delete operation' "
00452               "SUP auditWriteObject STRUCTURAL "
00453               "MAY reqOld )", &log_ocs[LOG_EN_DELETE] },
00454        { "( " LOG_SCHEMA_OC ".9 NAME 'auditModify' "
00455               "DESC 'Modify operation' "
00456               "SUP auditWriteObject STRUCTURAL "
00457               "MAY reqOld MUST reqMod )", &log_ocs[LOG_EN_MODIFY] },
00458        { "( " LOG_SCHEMA_OC ".10 NAME 'auditModRDN' "
00459               "DESC 'ModRDN operation' "
00460               "SUP auditWriteObject STRUCTURAL "
00461               "MUST ( reqNewRDN $ reqDeleteOldRDN ) "
00462               "MAY ( reqNewSuperior $ reqMod $ reqOld ) )", &log_ocs[LOG_EN_MODRDN] },
00463        { "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' "
00464               "DESC 'Search operation' "
00465               "SUP auditReadObject STRUCTURAL "
00466               "MUST ( reqScope $ reqDerefAliases $ reqAttrsonly ) "
00467               "MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $ "
00468                      "reqTimeLimit ) )", &log_ocs[LOG_EN_SEARCH] },
00469        { "( " LOG_SCHEMA_OC ".12 NAME 'auditExtended' "
00470               "DESC 'Extended operation' "
00471               "SUP auditObject STRUCTURAL "
00472               "MAY reqData )", &log_ocs[LOG_EN_EXTENDED] },
00473        { NULL, NULL }
00474 };
00475 
00476 #define       RDNEQ  "reqStart="
00477 
00478 /* Our time intervals are of the form [ddd+]hh:mm[:ss]
00479  * If a field is present, it must be two digits. (Except for
00480  * days, which can be arbitrary width.)
00481  */
00482 static int
00483 log_age_parse(char *agestr)
00484 {
00485        int t1, t2;
00486        int gotdays = 0;
00487        char *endptr;
00488 
00489        t1 = strtol( agestr, &endptr, 10 );
00490        /* Is there a days delimiter? */
00491        if ( *endptr == '+' ) {
00492               /* 32 bit time only covers about 68 years */
00493               if ( t1 < 0 || t1 > 25000 )
00494                      return -1;
00495               t1 *= 24;
00496               gotdays = 1;
00497               agestr = endptr + 1;
00498        } else {
00499               if ( agestr[2] != ':' ) {
00500                      /* No valid delimiter found, fail */
00501                      return -1;
00502               }
00503               t1 *= 60;
00504               agestr += 3;
00505        }
00506 
00507        t2 = atoi( agestr );
00508        t1 += t2;
00509 
00510        if ( agestr[2] ) {
00511               /* if there's a delimiter, it can only be a colon */
00512               if ( agestr[2] != ':' )
00513                      return -1;
00514        } else {
00515               /* If we're at the end of the string, and we started with days,
00516                * fail because we expected to find minutes too.
00517                */
00518               return gotdays ? -1 : t1 * 60;
00519        }
00520 
00521        agestr += 3;
00522        t2 = atoi( agestr );
00523 
00524        /* last field can only be seconds */
00525        if ( agestr[2] && ( agestr[2] != ':' || !gotdays ))
00526               return -1;
00527        t1 *= 60;
00528        t1 += t2;
00529 
00530        if ( agestr[2] ) {
00531               agestr += 3;
00532               if ( agestr[2] )
00533                      return -1;
00534               t1 *= 60;
00535               t1 += atoi( agestr );
00536        } else if ( gotdays ) {
00537               /* only got days+hh:mm */
00538               t1 *= 60;
00539        }
00540        return t1;
00541 }
00542 
00543 static void
00544 log_age_unparse( int age, struct berval *agebv, size_t size )
00545 {
00546        int dd, hh, mm, ss, len;
00547        char *ptr;
00548 
00549        assert( size > 0 );
00550 
00551        ss = age % 60;
00552        age /= 60;
00553        mm = age % 60;
00554        age /= 60;
00555        hh = age % 24;
00556        age /= 24;
00557        dd = age;
00558 
00559        ptr = agebv->bv_val;
00560 
00561        if ( dd ) {
00562               len = snprintf( ptr, size, "%d+", dd );
00563               assert( len >= 0 && (unsigned) len < size );
00564               size -= len;
00565               ptr += len;
00566        }
00567        len = snprintf( ptr, size, "%02d:%02d", hh, mm );
00568        assert( len >= 0 && (unsigned) len < size );
00569        size -= len;
00570        ptr += len;
00571        if ( ss ) {
00572               len = snprintf( ptr, size, ":%02d", ss );
00573               assert( len >= 0 && (unsigned) len < size );
00574               size -= len;
00575               ptr += len;
00576        }
00577 
00578        agebv->bv_len = ptr - agebv->bv_val;
00579 }
00580 
00581 static slap_callback nullsc = { NULL, NULL, NULL, NULL };
00582 
00583 #define PURGE_INCREMENT     100
00584 
00585 typedef struct purge_data {
00586        int slots;
00587        int used;
00588        BerVarray dn;
00589        BerVarray ndn;
00590        struct berval csn;   /* an arbitrary old CSN */
00591 } purge_data;
00592 
00593 static int
00594 log_old_lookup( Operation *op, SlapReply *rs )
00595 {
00596        purge_data *pd = op->o_callback->sc_private;
00597        Attribute *a;
00598 
00599        if ( rs->sr_type != REP_SEARCH) return 0;
00600 
00601        if ( slapd_shutdown ) return 0;
00602 
00603        /* Remember max CSN: should always be the last entry
00604         * seen, since log entries are ordered chronologically...
00605         */
00606        a = attr_find( rs->sr_entry->e_attrs,
00607               slap_schema.si_ad_entryCSN );
00608        if ( a ) {
00609               ber_len_t len = a->a_nvals[0].bv_len;
00610               /* Paranoid len check, normalized CSNs are always the same length */
00611               if ( len > LDAP_PVT_CSNSTR_BUFSIZE )
00612                      len = LDAP_PVT_CSNSTR_BUFSIZE;
00613               if ( memcmp( a->a_nvals[0].bv_val, pd->csn.bv_val, len ) > 0 ) {
00614                      AC_MEMCPY( pd->csn.bv_val, a->a_nvals[0].bv_val, len );
00615                      pd->csn.bv_len = len;
00616               }
00617        }
00618        if ( pd->used >= pd->slots ) {
00619               pd->slots += PURGE_INCREMENT;
00620               pd->dn = ch_realloc( pd->dn, pd->slots * sizeof( struct berval ));
00621               pd->ndn = ch_realloc( pd->ndn, pd->slots * sizeof( struct berval ));
00622        }
00623        ber_dupbv( &pd->dn[pd->used], &rs->sr_entry->e_name );
00624        ber_dupbv( &pd->ndn[pd->used], &rs->sr_entry->e_nname );
00625        pd->used++;
00626        return 0;
00627 }
00628 
00629 /* Periodically search for old entries in the log database and delete them */
00630 static void *
00631 accesslog_purge( void *ctx, void *arg )
00632 {
00633        struct re_s *rtask = arg;
00634        struct log_info *li = rtask->arg;
00635 
00636        Connection conn = {0};
00637        OperationBuffer opbuf;
00638        Operation *op;
00639        SlapReply rs = {REP_RESULT};
00640        slap_callback cb = { NULL, log_old_lookup, NULL, NULL };
00641        Filter f;
00642        AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
00643        purge_data pd = {0};
00644        char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
00645        char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
00646        time_t old = slap_get_time();
00647 
00648        connection_fake_init( &conn, &opbuf, ctx );
00649        op = &opbuf.ob_op;
00650 
00651        f.f_choice = LDAP_FILTER_LE;
00652        f.f_ava = &ava;
00653        f.f_next = NULL;
00654 
00655        ava.aa_desc = ad_reqStart;
00656        ava.aa_value.bv_val = timebuf;
00657        ava.aa_value.bv_len = sizeof(timebuf);
00658 
00659        old -= li->li_age;
00660        slap_timestamp( &old, &ava.aa_value );
00661 
00662        op->o_tag = LDAP_REQ_SEARCH;
00663        op->o_bd = li->li_db;
00664        op->o_dn = li->li_db->be_rootdn;
00665        op->o_ndn = li->li_db->be_rootndn;
00666        op->o_req_dn = li->li_db->be_suffix[0];
00667        op->o_req_ndn = li->li_db->be_nsuffix[0];
00668        op->o_callback = &cb;
00669        op->ors_scope = LDAP_SCOPE_ONELEVEL;
00670        op->ors_deref = LDAP_DEREF_NEVER;
00671        op->ors_tlimit = SLAP_NO_LIMIT;
00672        op->ors_slimit = SLAP_NO_LIMIT;
00673        op->ors_filter = &f;
00674        filter2bv_x( op, &f, &op->ors_filterstr );
00675        op->ors_attrs = slap_anlist_no_attrs;
00676        op->ors_attrsonly = 1;
00677        
00678        pd.csn.bv_len = sizeof( csnbuf );
00679        pd.csn.bv_val = csnbuf;
00680        csnbuf[0] = '\0';
00681        cb.sc_private = &pd;
00682 
00683        op->o_bd->be_search( op, &rs );
00684        op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
00685 
00686        if ( pd.used ) {
00687               int i;
00688 
00689               /* delete the expired entries */
00690               op->o_tag = LDAP_REQ_DELETE;
00691               op->o_callback = &nullsc;
00692               op->o_csn = pd.csn;
00693               op->o_dont_replicate = 1;
00694 
00695               for (i=0; i<pd.used; i++) {
00696                      op->o_req_dn = pd.dn[i];
00697                      op->o_req_ndn = pd.ndn[i];
00698                      if ( !slapd_shutdown ) {
00699                             rs_reinit( &rs, REP_RESULT );
00700                             op->o_bd->be_delete( op, &rs );
00701                      }
00702                      ch_free( pd.ndn[i].bv_val );
00703                      ch_free( pd.dn[i].bv_val );
00704               }
00705               ch_free( pd.ndn );
00706               ch_free( pd.dn );
00707 
00708               {
00709                      Modifications mod;
00710                      struct berval bv[2];
00711                      rs_reinit( &rs, REP_RESULT );
00712                      /* update context's entryCSN to reflect oldest CSN */
00713                      mod.sml_numvals = 1;
00714                      mod.sml_values = bv;
00715                      bv[0] = pd.csn;
00716                      BER_BVZERO(&bv[1]);
00717                      mod.sml_nvalues = NULL;
00718                      mod.sml_desc = slap_schema.si_ad_entryCSN;
00719                      mod.sml_op = LDAP_MOD_REPLACE;
00720                      mod.sml_flags = SLAP_MOD_INTERNAL;
00721                      mod.sml_next = NULL;
00722 
00723                      op->o_tag = LDAP_REQ_MODIFY;
00724                      op->orm_modlist = &mod;
00725                      op->orm_no_opattrs = 1;
00726                      op->o_req_dn = li->li_db->be_suffix[0];
00727                      op->o_req_ndn = li->li_db->be_nsuffix[0];
00728                      op->o_no_schema_check = 1;
00729                      op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
00730                      op->o_bd->be_modify( op, &rs );
00731                      if ( mod.sml_next ) {
00732                             slap_mods_free( mod.sml_next, 1 );
00733                      }
00734               }
00735        }
00736 
00737        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
00738        ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
00739        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
00740 
00741        return NULL;
00742 }
00743 
00744 static int
00745 log_cf_gen(ConfigArgs *c)
00746 {
00747        slap_overinst *on = (slap_overinst *)c->bi;
00748        struct log_info *li = on->on_bi.bi_private;
00749        int rc = 0;
00750        slap_mask_t tmask = 0;
00751        char agebuf[2*STRLENOF("ddddd+hh:mm:ss  ")];
00752        struct berval agebv, cyclebv;
00753 
00754        switch( c->op ) {
00755        case SLAP_CONFIG_EMIT:
00756               switch( c->type ) {
00757               case LOG_DB:
00758                      if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
00759                             value_add_one( &c->rvalue_vals, &li->li_db_suffix );
00760                             value_add_one( &c->rvalue_nvals, &li->li_db_suffix );
00761                      } else if ( li->li_db ) {
00762                             value_add_one( &c->rvalue_vals, li->li_db->be_suffix );
00763                             value_add_one( &c->rvalue_nvals, li->li_db->be_nsuffix );
00764                      } else {
00765                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
00766                                    "accesslog: \"logdb <suffix>\" must be specified" );
00767                             Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
00768                                    c->log, c->cr_msg, c->value_dn.bv_val );
00769                             rc = 1;
00770                             break;
00771                      }
00772                      break;
00773               case LOG_OPS:
00774                      rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals );
00775                      break;
00776               case LOG_PURGE:
00777                      if ( !li->li_age ) {
00778                             rc = 1;
00779                             break;
00780                      }
00781                      agebv.bv_val = agebuf;
00782                      log_age_unparse( li->li_age, &agebv, sizeof( agebuf ) );
00783                      agebv.bv_val[agebv.bv_len] = ' ';
00784                      agebv.bv_len++;
00785                      cyclebv.bv_val = agebv.bv_val + agebv.bv_len;
00786                      log_age_unparse( li->li_cycle, &cyclebv, sizeof( agebuf ) - agebv.bv_len );
00787                      agebv.bv_len += cyclebv.bv_len;
00788                      value_add_one( &c->rvalue_vals, &agebv );
00789                      break;
00790               case LOG_SUCCESS:
00791                      if ( li->li_success )
00792                             c->value_int = li->li_success;
00793                      else
00794                             rc = 1;
00795                      break;
00796               case LOG_OLD:
00797                      if ( li->li_oldf ) {
00798                             filter2bv( li->li_oldf, &agebv );
00799                             ber_bvarray_add( &c->rvalue_vals, &agebv );
00800                      }
00801                      else
00802                             rc = 1;
00803                      break;
00804               case LOG_OLDATTR:
00805                      if ( li->li_oldattrs ) {
00806                             log_attr *la;
00807 
00808                             for ( la = li->li_oldattrs; la; la=la->next )
00809                                    value_add_one( &c->rvalue_vals, &la->attr->ad_cname );
00810                      }
00811                      else
00812                             rc = 1;
00813                      break;
00814               case LOG_BASE:
00815                      if ( li->li_bases ) {
00816                             log_base *lb;
00817 
00818                             for ( lb = li->li_bases; lb; lb=lb->lb_next )
00819                                    value_add_one( &c->rvalue_vals, &lb->lb_line );
00820                      }
00821                      else
00822                             rc = 1;
00823                      break;
00824               }
00825               break;
00826        case LDAP_MOD_DELETE:
00827               switch( c->type ) {
00828               case LOG_DB:
00829                      /* noop. this should always be a valid backend. */
00830                      break;
00831               case LOG_OPS:
00832                      if ( c->valx < 0 ) {
00833                             li->li_ops = 0;
00834                      } else {
00835                             rc = verbs_to_mask( 1, &c->line, logops, &tmask );
00836                             if ( rc == 0 )
00837                                    li->li_ops &= ~tmask;
00838                      }
00839                      break;
00840               case LOG_PURGE:
00841                      if ( li->li_task ) {
00842                             struct re_s *re = li->li_task;
00843                             li->li_task = NULL;
00844                             ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
00845                             if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ))
00846                                    ldap_pvt_runqueue_stoptask( &slapd_rq, re );
00847                             ldap_pvt_runqueue_remove( &slapd_rq, re );
00848                             ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
00849                      }
00850                      li->li_age = 0;
00851                      li->li_cycle = 0;
00852                      break;
00853               case LOG_SUCCESS:
00854                      li->li_success = 0;
00855                      break;
00856               case LOG_OLD:
00857                      if ( li->li_oldf ) {
00858                             filter_free( li->li_oldf );
00859                             li->li_oldf = NULL;
00860                      }
00861                      break;
00862               case LOG_OLDATTR:
00863                      if ( c->valx < 0 ) {
00864                             log_attr *la, *ln;
00865 
00866                             for ( la = li->li_oldattrs; la; la = ln ) {
00867                                    ln = la->next;
00868                                    ch_free( la );
00869                             }
00870                      } else {
00871                             log_attr *la = NULL, **lp;
00872                             int i;
00873 
00874                             for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) {
00875                                    la = *lp;
00876                                    lp = &la->next;
00877                             }
00878                             *lp = la->next;
00879                             ch_free( la );
00880                      }
00881                      break;
00882               case LOG_BASE:
00883                      if ( c->valx < 0 ) {
00884                             log_base *lb, *ln;
00885 
00886                             for ( lb = li->li_bases; lb; lb = ln ) {
00887                                    ln = lb->lb_next;
00888                                    ch_free( lb );
00889                             }
00890                      } else {
00891                             log_base *lb = NULL, **lp;
00892                             int i;
00893 
00894                             for ( lp = &li->li_bases, i=0; i < c->valx; i++ ) {
00895                                    lb = *lp;
00896                                    lp = &lb->lb_next;
00897                             }
00898                             *lp = lb->lb_next;
00899                             ch_free( lb );
00900                      }
00901                      break;
00902               }
00903               break;
00904        default:
00905               switch( c->type ) {
00906               case LOG_DB:
00907                      if ( CONFIG_ONLINE_ADD( c )) {
00908                             li->li_db = select_backend( &c->value_ndn, 0 );
00909                             if ( !li->li_db ) {
00910                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
00911                                           "<%s> no matching backend found for suffix",
00912                                           c->argv[0] );
00913                                    Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
00914                                           c->log, c->cr_msg, c->value_dn.bv_val );
00915                                    rc = 1;
00916                             }
00917                             ch_free( c->value_ndn.bv_val );
00918                      } else {
00919                             li->li_db_suffix = c->value_ndn;
00920                      }
00921                      ch_free( c->value_dn.bv_val );
00922                      break;
00923               case LOG_OPS:
00924                      rc = verbs_to_mask( c->argc, c->argv, logops, &tmask );
00925                      if ( rc == 0 )
00926                             li->li_ops |= tmask;
00927                      break;
00928               case LOG_PURGE:
00929                      li->li_age = log_age_parse( c->argv[1] );
00930                      if ( li->li_age < 1 ) {
00931                             rc = 1;
00932                      } else {
00933                             li->li_cycle = log_age_parse( c->argv[2] );
00934                             if ( li->li_cycle < 1 ) {
00935                                    rc = 1;
00936                             } else if ( slapMode & SLAP_SERVER_MODE ) {
00937                                    struct re_s *re = li->li_task;
00938                                    if ( re )
00939                                           re->interval.tv_sec = li->li_cycle;
00940                                    else {
00941                                           ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
00942                                           li->li_task = ldap_pvt_runqueue_insert( &slapd_rq,
00943                                                  li->li_cycle, accesslog_purge, li,
00944                                                  "accesslog_purge", li->li_db ?
00945                                                         li->li_db->be_suffix[0].bv_val :
00946                                                         c->be->be_suffix[0].bv_val );
00947                                           ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
00948                                    }
00949                             }
00950                      }
00951                      break;
00952               case LOG_SUCCESS:
00953                      li->li_success = c->value_int;
00954                      break;
00955               case LOG_OLD:
00956                      li->li_oldf = str2filter( c->argv[1] );
00957                      if ( !li->li_oldf ) {
00958                             snprintf( c->cr_msg, sizeof( c->cr_msg ), "bad filter!" );
00959                             rc = 1;
00960                      }
00961                      break;
00962               case LOG_OLDATTR: {
00963                      int i;
00964                      AttributeDescription *ad;
00965                      const char *text;
00966 
00967                      for ( i=1; i< c->argc; i++ ) {
00968                             ad = NULL;
00969                             if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) {
00970                                    log_attr *la = ch_malloc( sizeof( log_attr ));
00971                                    la->attr = ad;
00972                                    la->next = li->li_oldattrs;
00973                                    li->li_oldattrs = la;
00974                             } else {
00975                                    snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s",
00976                                           c->argv[0], c->argv[i], text );
00977                                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
00978                                           "%s: %s\n", c->log, c->cr_msg, 0 );
00979                                    rc = ARG_BAD_CONF;
00980                                    break;
00981                             }
00982                      }
00983                      }
00984                      break;
00985               case LOG_BASE: {
00986                      slap_mask_t m = 0;
00987                      rc = verbstring_to_mask( logops, c->argv[1], '|', &m );
00988                      if ( rc == 0 ) {
00989                             struct berval dn, ndn;
00990                             ber_str2bv( c->argv[2], 0, 0, &dn );
00991                             rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
00992                             if ( rc == 0 ) {
00993                                    log_base *lb;
00994                                    struct berval mbv;
00995                                    char *ptr;
00996                                    mask_to_verbstring( logops, m, '|', &mbv );
00997                                    lb = ch_malloc( sizeof( log_base ) + mbv.bv_len + ndn.bv_len + 3 + 1 );
00998                                    lb->lb_line.bv_val = (char *)(lb + 1);
00999                                    lb->lb_line.bv_len = mbv.bv_len + ndn.bv_len + 3;
01000                                    ptr = lutil_strcopy( lb->lb_line.bv_val, mbv.bv_val );
01001                                    *ptr++ = ' ';
01002                                    *ptr++ = '"';
01003                                    lb->lb_base.bv_val = ptr;
01004                                    lb->lb_base.bv_len = ndn.bv_len;
01005                                    ptr = lutil_strcopy( ptr, ndn.bv_val );
01006                                    *ptr++ = '"';
01007                                    lb->lb_ops = m;
01008                                    lb->lb_next = li->li_bases;
01009                                    li->li_bases = lb;
01010                             } else {
01011                                    snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid DN: %s",
01012                                           c->argv[0], c->argv[2] );
01013                                    Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
01014                                           "%s: %s\n", c->log, c->cr_msg, 0 );
01015                                    rc = ARG_BAD_CONF;
01016                             }
01017                      } else {
01018                             snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid ops: %s",
01019                                    c->argv[0], c->argv[1] );
01020                             Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
01021                                    "%s: %s\n", c->log, c->cr_msg, 0 );
01022                             rc = ARG_BAD_CONF;
01023                      }
01024                      }
01025                      break;
01026               }
01027               break;
01028        }
01029        return rc;
01030 }
01031 
01032 static int
01033 logSchemaControlValidate(
01034        Syntax        *syntax,
01035        struct berval *valp )
01036 {
01037        struct berval val, bv;
01038        ber_len_t            i;
01039        int           rc = LDAP_SUCCESS;
01040 
01041        assert( valp != NULL );
01042 
01043        val = *valp;
01044 
01045        /* check minimal size */
01046        if ( val.bv_len < STRLENOF( "{*}" ) ) {
01047               return LDAP_INVALID_SYNTAX;
01048        }
01049 
01050        val.bv_len--;
01051 
01052        /* check SEQUENCE boundaries */
01053        if ( val.bv_val[ 0 ] != '{' /*}*/ ||
01054               val.bv_val[ val.bv_len ] != /*{*/ '}' )
01055        {
01056               return LDAP_INVALID_SYNTAX;
01057        }
01058 
01059        /* extract and check OID */
01060        for ( i = 1; i < val.bv_len; i++ ) {
01061               if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
01062                      break;
01063               }
01064        }
01065 
01066        bv.bv_val = &val.bv_val[ i ];
01067 
01068        for ( i++; i < val.bv_len; i++ ) {
01069               if ( ASCII_SPACE( val.bv_val[ i ] ) )
01070               {
01071                      break;
01072               }
01073        }
01074 
01075        bv.bv_len = &val.bv_val[ i ] - bv.bv_val;
01076 
01077        rc = numericoidValidate( NULL, &bv );
01078        if ( rc != LDAP_SUCCESS ) {
01079               return rc;
01080        }
01081 
01082        if ( i == val.bv_len ) {
01083               return LDAP_SUCCESS;
01084        }
01085 
01086        if ( val.bv_val[ i ] != ' ' ) {
01087               return LDAP_INVALID_SYNTAX;
01088        }
01089 
01090        for ( i++; i < val.bv_len; i++ ) {
01091               if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
01092                      break;
01093               }
01094        }
01095 
01096        if ( i == val.bv_len ) {
01097               return LDAP_SUCCESS;
01098        }
01099 
01100        /* extract and check criticality */
01101        if ( strncasecmp( &val.bv_val[ i ], "criticality ", STRLENOF( "criticality " ) ) == 0 )
01102        {
01103               i += STRLENOF( "criticality " );
01104               for ( ; i < val.bv_len; i++ ) {
01105                      if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
01106                             break;
01107                      }
01108               }
01109 
01110               if ( i == val.bv_len ) {
01111                      return LDAP_INVALID_SYNTAX;
01112               }
01113 
01114               bv.bv_val = &val.bv_val[ i ];
01115 
01116               for ( ; i < val.bv_len; i++ ) {
01117                      if ( ASCII_SPACE( val.bv_val[ i ] ) ) {
01118                             break;
01119                      }
01120               }
01121 
01122               bv.bv_len = &val.bv_val[ i ] - bv.bv_val;
01123 
01124               if ( !bvmatch( &bv, &slap_true_bv ) && !bvmatch( &bv, &slap_false_bv ) ) 
01125               {
01126                      return LDAP_INVALID_SYNTAX;
01127               }
01128 
01129               if ( i == val.bv_len ) {
01130                      return LDAP_SUCCESS;
01131               }
01132 
01133               if ( val.bv_val[ i ] != ' ' ) {
01134                      return LDAP_INVALID_SYNTAX;
01135               }
01136 
01137               for ( i++; i < val.bv_len; i++ ) {
01138                      if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
01139                             break;
01140                      }
01141               }
01142 
01143               if ( i == val.bv_len ) {
01144                      return LDAP_SUCCESS;
01145               }
01146        }
01147 
01148        /* extract and check controlValue */
01149        if ( strncasecmp( &val.bv_val[ i ], "controlValue ", STRLENOF( "controlValue " ) ) == 0 )
01150        {
01151               ber_len_t valueStart, valueLen;
01152 
01153               i += STRLENOF( "controlValue " );
01154               for ( ; i < val.bv_len; i++ ) {
01155                      if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
01156                             break;
01157                      }
01158               }
01159 
01160               if ( i == val.bv_len ) {
01161                      return LDAP_INVALID_SYNTAX;
01162               }
01163 
01164               if ( val.bv_val[ i ] != '"' ) {
01165                      return LDAP_INVALID_SYNTAX;
01166               }
01167 
01168               i++;
01169               valueStart = i;
01170 
01171               for ( ; i < val.bv_len; i++ ) {
01172                      if ( val.bv_val[ i ] == '"' ) {
01173                             break;
01174                      }
01175 
01176                      if ( !ASCII_HEX( val.bv_val[ i ] ) ) {
01177                             return LDAP_INVALID_SYNTAX;
01178                      }
01179               }
01180 
01181               if ( val.bv_val[ i ] != '"' ) {
01182                      return LDAP_INVALID_SYNTAX;
01183               }
01184 
01185               valueLen = i - valueStart;
01186               if ( (valueLen/2)*2 != valueLen ) {
01187                      return LDAP_INVALID_SYNTAX;
01188               }
01189 
01190               for ( i++; i < val.bv_len; i++ ) {
01191                      if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
01192                             break;
01193                      }
01194               }
01195 
01196               if ( i == val.bv_len ) {
01197                      return LDAP_SUCCESS;
01198               }
01199        }
01200 
01201        return LDAP_INVALID_SYNTAX;
01202 }
01203 
01204 static int
01205 accesslog_ctrls(
01206        LDAPControl **ctrls,
01207        BerVarray *valsp,
01208        BerVarray *nvalsp,
01209        void *memctx )
01210 {
01211        long          i, rc = 0;
01212 
01213        assert( valsp != NULL );
01214        assert( ctrls != NULL );
01215 
01216        *valsp = NULL;
01217        *nvalsp = NULL;
01218 
01219        for ( i = 0; ctrls[ i ] != NULL; i++ ) {
01220               struct berval idx,
01221                             oid,
01222                             noid,
01223                             bv;
01224               char          *ptr,
01225                             buf[ 32 ];
01226 
01227               if ( ctrls[ i ]->ldctl_oid == NULL ) {
01228                      return LDAP_PROTOCOL_ERROR;
01229               }
01230 
01231               idx.bv_len = snprintf( buf, sizeof( buf ), "{%ld}", i );
01232               idx.bv_val = buf;
01233 
01234               ber_str2bv( ctrls[ i ]->ldctl_oid, 0, 0, &oid );
01235               noid.bv_len = idx.bv_len + oid.bv_len;
01236               ptr = noid.bv_val = ber_memalloc_x( noid.bv_len + 1, memctx );
01237               ptr = lutil_strcopy( ptr, idx.bv_val );
01238               ptr = lutil_strcopy( ptr, oid.bv_val );
01239 
01240               bv.bv_len = idx.bv_len + STRLENOF( "{}" ) + oid.bv_len;
01241 
01242               if ( ctrls[ i ]->ldctl_iscritical ) {
01243                      bv.bv_len += STRLENOF( " criticality TRUE" );
01244               }
01245 
01246               if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
01247                      bv.bv_len += STRLENOF( " controlValue \"\"" )
01248                             + 2 * ctrls[ i ]->ldctl_value.bv_len;
01249               }
01250 
01251               ptr = bv.bv_val = ber_memalloc_x( bv.bv_len + 1, memctx );
01252               if ( ptr == NULL ) {
01253                      ber_bvarray_free( *valsp );
01254                      *valsp = NULL;
01255                      ber_bvarray_free( *nvalsp );
01256                      *nvalsp = NULL;
01257                      return LDAP_OTHER;
01258               }
01259 
01260               ptr = lutil_strcopy( ptr, idx.bv_val );
01261 
01262               *ptr++ = '{' /*}*/ ;
01263               ptr = lutil_strcopy( ptr, oid.bv_val );
01264 
01265               if ( ctrls[ i ]->ldctl_iscritical ) {
01266                      ptr = lutil_strcopy( ptr, " criticality TRUE" );
01267               }
01268               
01269               if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
01270                      ber_len_t     j;
01271 
01272                      ptr = lutil_strcopy( ptr, " controlValue \"" );
01273                      for ( j = 0; j < ctrls[ i ]->ldctl_value.bv_len; j++ ) {
01274                             *ptr++ = SLAP_ESCAPE_HI(ctrls[ i ]->ldctl_value.bv_val[ j ]);
01275                             *ptr++ = SLAP_ESCAPE_LO(ctrls[ i ]->ldctl_value.bv_val[ j ]);
01276                      }
01277 
01278                      *ptr++ = '"';
01279               }
01280 
01281               *ptr++ = '}';
01282               *ptr = '\0';
01283 
01284               ber_bvarray_add_x( valsp, &bv, memctx );
01285               ber_bvarray_add_x( nvalsp, &noid, memctx );
01286        }
01287 
01288        return rc;
01289        
01290 }
01291 
01292 static Entry *accesslog_entry( Operation *op, SlapReply *rs, int logop,
01293        Operation *op2 ) {
01294        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01295        log_info *li = on->on_bi.bi_private;
01296 
01297        char rdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
01298        char nrdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
01299 
01300        struct berval rdn, nrdn, timestamp, ntimestamp, bv;
01301        slap_verbmasks *lo = logops+logop+EN_OFFSET;
01302 
01303        Entry *e = entry_alloc();
01304 
01305        strcpy( rdnbuf, RDNEQ );
01306        rdn.bv_val = rdnbuf;
01307        strcpy( nrdnbuf, RDNEQ );
01308        nrdn.bv_val = nrdnbuf;
01309 
01310        timestamp.bv_val = rdnbuf+STRLENOF(RDNEQ);
01311        timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
01312        slap_timestamp( &op->o_time, &timestamp );
01313        snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op->o_tincr );
01314        timestamp.bv_len += STRLENOF(".123456");
01315 
01316        rdn.bv_len = STRLENOF(RDNEQ)+timestamp.bv_len;
01317        ad_reqStart->ad_type->sat_equality->smr_normalize(
01318               SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, ad_reqStart->ad_type->sat_syntax,
01319               ad_reqStart->ad_type->sat_equality, &timestamp, &ntimestamp,
01320               op->o_tmpmemctx );
01321 
01322        strcpy( nrdn.bv_val + STRLENOF(RDNEQ), ntimestamp.bv_val );
01323        nrdn.bv_len = STRLENOF(RDNEQ)+ntimestamp.bv_len;
01324        build_new_dn( &e->e_name, li->li_db->be_suffix, &rdn, NULL );
01325        build_new_dn( &e->e_nname, li->li_db->be_nsuffix, &nrdn, NULL );
01326 
01327        attr_merge_one( e, slap_schema.si_ad_objectClass,
01328               &log_ocs[logop]->soc_cname, NULL );
01329        attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
01330               &log_ocs[logop]->soc_cname, NULL );
01331        attr_merge_one( e, ad_reqStart, &timestamp, &ntimestamp );
01332        op->o_tmpfree( ntimestamp.bv_val, op->o_tmpmemctx );
01333 
01334        slap_op_time( &op2->o_time, &op2->o_tincr );
01335 
01336        timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
01337        slap_timestamp( &op2->o_time, &timestamp );
01338        snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op2->o_tincr );
01339        timestamp.bv_len += STRLENOF(".123456");
01340 
01341        attr_merge_normalize_one( e, ad_reqEnd, &timestamp, op->o_tmpmemctx );
01342 
01343        /* Exops have OID appended */
01344        if ( logop == LOG_EN_EXTENDED ) {
01345               bv.bv_len = lo->word.bv_len + op->ore_reqoid.bv_len + 2;
01346               bv.bv_val = ch_malloc( bv.bv_len + 1 );
01347               AC_MEMCPY( bv.bv_val, lo->word.bv_val, lo->word.bv_len );
01348               bv.bv_val[lo->word.bv_len] = '{';
01349               AC_MEMCPY( bv.bv_val+lo->word.bv_len+1, op->ore_reqoid.bv_val,
01350                      op->ore_reqoid.bv_len );
01351               bv.bv_val[bv.bv_len-1] = '}';
01352               bv.bv_val[bv.bv_len] = '\0';
01353               attr_merge_one( e, ad_reqType, &bv, NULL );
01354        } else {
01355               attr_merge_one( e, ad_reqType, &lo->word, NULL );
01356        }
01357 
01358        rdn.bv_len = snprintf( rdn.bv_val, sizeof( rdnbuf ), "%lu", op->o_connid );
01359        if ( rdn.bv_len < sizeof( rdnbuf ) ) {
01360               attr_merge_one( e, ad_reqSession, &rdn, NULL );
01361        } /* else? */
01362 
01363        if ( BER_BVISNULL( &op->o_dn ) ) {
01364               attr_merge_one( e, ad_reqAuthzID, (struct berval *)&slap_empty_bv,
01365                      (struct berval *)&slap_empty_bv );
01366        } else {
01367               attr_merge_one( e, ad_reqAuthzID, &op->o_dn, &op->o_ndn );
01368        }
01369 
01370        /* FIXME: need to add reqControls and reqRespControls */
01371        if ( op->o_ctrls ) {
01372               BerVarray     vals = NULL,
01373                             nvals = NULL;
01374 
01375               if ( accesslog_ctrls( op->o_ctrls, &vals, &nvals,
01376                      op->o_tmpmemctx ) == LDAP_SUCCESS && vals )
01377               {
01378                      attr_merge( e, ad_reqControls, vals, nvals );
01379                      ber_bvarray_free_x( vals, op->o_tmpmemctx );
01380                      ber_bvarray_free_x( nvals, op->o_tmpmemctx );
01381               }
01382        }
01383 
01384        if ( rs->sr_ctrls ) {
01385               BerVarray     vals = NULL,
01386                             nvals = NULL;
01387 
01388               if ( accesslog_ctrls( rs->sr_ctrls, &vals, &nvals,
01389                      op->o_tmpmemctx ) == LDAP_SUCCESS && vals )
01390               {
01391                      attr_merge( e, ad_reqRespControls, vals, nvals );
01392                      ber_bvarray_free_x( vals, op->o_tmpmemctx );
01393                      ber_bvarray_free_x( nvals, op->o_tmpmemctx );
01394               }
01395 
01396        }
01397 
01398        return e;
01399 }
01400 
01401 static struct berval scopes[] = {
01402        BER_BVC("base"),
01403        BER_BVC("one"),
01404        BER_BVC("sub"),
01405        BER_BVC("subord")
01406 };
01407 
01408 static struct berval derefs[] = {
01409        BER_BVC("never"),
01410        BER_BVC("searching"),
01411        BER_BVC("finding"),
01412        BER_BVC("always")
01413 };
01414 
01415 static struct berval simple = BER_BVC("SIMPLE");
01416 
01417 static void accesslog_val2val(AttributeDescription *ad, struct berval *val,
01418        char c_op, struct berval *dst) {
01419        char *ptr;
01420 
01421        dst->bv_len = ad->ad_cname.bv_len + val->bv_len + 2;
01422        if ( c_op ) dst->bv_len++;
01423 
01424        dst->bv_val = ch_malloc( dst->bv_len+1 );
01425 
01426        ptr = lutil_strcopy( dst->bv_val, ad->ad_cname.bv_val );
01427        *ptr++ = ':';
01428        if ( c_op )
01429               *ptr++ = c_op;
01430        *ptr++ = ' ';
01431        AC_MEMCPY( ptr, val->bv_val, val->bv_len );
01432        dst->bv_val[dst->bv_len] = '\0';
01433 }
01434 
01435 static int
01436 accesslog_op2logop( Operation *op )
01437 {
01438        switch ( op->o_tag ) {
01439        case LDAP_REQ_ADD:          return LOG_EN_ADD;
01440        case LDAP_REQ_DELETE:       return LOG_EN_DELETE;
01441        case LDAP_REQ_MODIFY:       return LOG_EN_MODIFY;
01442        case LDAP_REQ_MODRDN:       return LOG_EN_MODRDN;
01443        case LDAP_REQ_COMPARE:      return LOG_EN_COMPARE;
01444        case LDAP_REQ_SEARCH:       return LOG_EN_SEARCH;
01445        case LDAP_REQ_BIND:         return LOG_EN_BIND;
01446        case LDAP_REQ_EXTENDED:     return LOG_EN_EXTENDED;
01447        default:      /* unknown operation type */
01448               break;
01449        }      /* Unbind and Abandon never reach here */
01450        return LOG_EN_UNKNOWN;
01451 }
01452 
01453 static int accesslog_response(Operation *op, SlapReply *rs) {
01454        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01455        log_info *li = on->on_bi.bi_private;
01456        Attribute *a, *last_attr;
01457        Modifications *m;
01458        struct berval *b, uuid = BER_BVNULL;
01459        int i;
01460        int logop;
01461        slap_verbmasks *lo;
01462        Entry *e = NULL, *old = NULL, *e_uuid = NULL;
01463        char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE+8];
01464        struct berval bv;
01465        char *ptr;
01466        BerVarray vals;
01467        Operation op2 = {0};
01468        SlapReply rs2 = {REP_RESULT};
01469 
01470        if ( rs->sr_type != REP_RESULT && rs->sr_type != REP_EXTENDED )
01471               return SLAP_CB_CONTINUE;
01472 
01473        logop = accesslog_op2logop( op );
01474        lo = logops+logop+EN_OFFSET;
01475        if ( !( li->li_ops & lo->mask )) {
01476               log_base *lb;
01477 
01478               i = 0;
01479               for ( lb = li->li_bases; lb; lb=lb->lb_next )
01480                      if (( lb->lb_ops & lo->mask ) && dnIsSuffix( &op->o_req_ndn, &lb->lb_base )) {
01481                             i = 1;
01482                             break;
01483                      }
01484               if ( !i )
01485                      return SLAP_CB_CONTINUE;
01486        }
01487 
01488        /* mutex and so were only set for write operations;
01489         * if we got here, the operation must be logged */
01490        if ( lo->mask & LOG_OP_WRITES ) {
01491               slap_callback *cb;
01492 
01493               /* These internal ops are not logged */
01494               if ( op->o_dont_replicate && op->orm_no_opattrs )
01495                      return SLAP_CB_CONTINUE;
01496 
01497               ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
01498               old = li->li_old;
01499               uuid = li->li_uuid;
01500               li->li_old = NULL;
01501               BER_BVZERO( &li->li_uuid );
01502               /* Disarm mod_cleanup */
01503               for ( cb = op->o_callback; cb; cb = cb->sc_next ) {
01504                      if ( cb->sc_private == (void *)on ) {
01505                             cb->sc_private = NULL;
01506                             break;
01507                      }
01508               }
01509               ldap_pvt_thread_rmutex_unlock( &li->li_op_rmutex, op->o_tid );
01510        }
01511 
01512        if ( li->li_success && rs->sr_err != LDAP_SUCCESS )
01513               goto done;
01514 
01515        e = accesslog_entry( op, rs, logop, &op2 );
01516 
01517        attr_merge_one( e, ad_reqDN, &op->o_req_dn, &op->o_req_ndn );
01518 
01519        if ( rs->sr_text ) {
01520               ber_str2bv( rs->sr_text, 0, 0, &bv );
01521               attr_merge_one( e, ad_reqMessage, &bv, NULL );
01522        }
01523        bv.bv_len = snprintf( timebuf, sizeof( timebuf ), "%d", rs->sr_err );
01524        if ( bv.bv_len < sizeof( timebuf ) ) {
01525               bv.bv_val = timebuf;
01526               attr_merge_one( e, ad_reqResult, &bv, NULL );
01527        }
01528 
01529        last_attr = attr_find( e->e_attrs, ad_reqResult );
01530 
01531        e_uuid = old;
01532        switch( logop ) {
01533        case LOG_EN_ADD:
01534        case LOG_EN_DELETE: {
01535               char c_op;
01536               Entry *e2;
01537 
01538               if ( logop == LOG_EN_ADD ) {
01539                      e2 = op->ora_e;
01540                      e_uuid = op->ora_e;
01541                      c_op = '+';
01542 
01543               } else {
01544                      if ( !old )
01545                             break;
01546                      e2 = old;
01547                      c_op = 0;
01548               }
01549               /* count all the vals */
01550               i = 0;
01551               for ( a=e2->e_attrs; a; a=a->a_next ) {
01552                      i += a->a_numvals;
01553               }
01554               vals = ch_malloc( (i+1) * sizeof( struct berval ));
01555               i = 0;
01556               for ( a=e2->e_attrs; a; a=a->a_next ) {
01557                      if ( a->a_vals ) {
01558                             for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
01559                                    accesslog_val2val( a->a_desc, b, c_op, &vals[i] );
01560                             }
01561                      }
01562               }
01563               vals[i].bv_val = NULL;
01564               vals[i].bv_len = 0;
01565               a = attr_alloc( logop == LOG_EN_ADD ? ad_reqMod : ad_reqOld );
01566               a->a_numvals = i;
01567               a->a_vals = vals;
01568               a->a_nvals = vals;
01569               last_attr->a_next = a;
01570               break;
01571        }
01572 
01573        case LOG_EN_MODRDN:
01574        case LOG_EN_MODIFY:
01575               /* count all the mods */
01576               i = 0;
01577               for ( m = op->orm_modlist; m; m = m->sml_next ) {
01578                      if ( m->sml_values ) {
01579                             i += m->sml_numvals;
01580                      } else if ( m->sml_op == LDAP_MOD_DELETE ||
01581                             m->sml_op == LDAP_MOD_REPLACE )
01582                      {
01583                             i++;
01584                      }
01585               }
01586               vals = ch_malloc( (i+1) * sizeof( struct berval ));
01587               i = 0;
01588 
01589               /* init flags on old entry */
01590               if ( old ) {
01591                      for ( a = old->e_attrs; a; a = a->a_next ) {
01592                             log_attr *la;
01593                             a->a_flags = 0;
01594 
01595                             /* look for attrs that are always logged */
01596                             for ( la = li->li_oldattrs; la; la = la->next ) {
01597                                    if ( a->a_desc == la->attr ) {
01598                                           a->a_flags = 1;
01599                                    }
01600                             }
01601                      }
01602               }
01603 
01604               for ( m = op->orm_modlist; m; m = m->sml_next ) {
01605                      /* Mark this attribute as modified */
01606                      if ( old ) {
01607                             a = attr_find( old->e_attrs, m->sml_desc );
01608                             if ( a ) {
01609                                    a->a_flags = 1;
01610                             }
01611                      }
01612 
01613                      /* don't log the RDN mods; they're explicitly logged later */
01614                      if ( logop == LOG_EN_MODRDN &&
01615                             ( m->sml_op == SLAP_MOD_SOFTADD ||
01616                               m->sml_op == LDAP_MOD_DELETE ) )
01617                      {
01618                             continue;
01619                      }
01620 
01621                      if ( m->sml_values ) {
01622                             for ( b = m->sml_values; !BER_BVISNULL( b ); b++, i++ ) {
01623                                    char c_op;
01624 
01625                                    switch ( m->sml_op ) {
01626                                    case LDAP_MOD_ADD: c_op = '+'; break;
01627                                    case LDAP_MOD_DELETE:       c_op = '-'; break;
01628                                    case LDAP_MOD_REPLACE:      c_op = '='; break;
01629                                    case LDAP_MOD_INCREMENT:    c_op = '#'; break;
01630 
01631                                    /* unknown op. there shouldn't be any of these. we
01632                                     * don't know what to do with it, but we shouldn't just
01633                                     * ignore it.
01634                                     */
01635                                    default: c_op = '?'; break;
01636                                    }
01637                                    accesslog_val2val( m->sml_desc, b, c_op, &vals[i] );
01638                             }
01639                      } else if ( m->sml_op == LDAP_MOD_DELETE ||
01640                             m->sml_op == LDAP_MOD_REPLACE )
01641                      {
01642                             vals[i].bv_len = m->sml_desc->ad_cname.bv_len + 2;
01643                             vals[i].bv_val = ch_malloc( vals[i].bv_len + 1 );
01644                             ptr = lutil_strcopy( vals[i].bv_val,
01645                                    m->sml_desc->ad_cname.bv_val );
01646                             *ptr++ = ':';
01647                             if ( m->sml_op == LDAP_MOD_DELETE ) {
01648                                    *ptr++ = '-';
01649                             } else {
01650                                    *ptr++ = '=';
01651                             }
01652                             *ptr = '\0';
01653                             i++;
01654                      }
01655               }
01656 
01657               if ( i > 0 ) {
01658                      BER_BVZERO( &vals[i] );
01659                      a = attr_alloc( ad_reqMod );
01660                      a->a_numvals = i;
01661                      a->a_vals = vals;
01662                      a->a_nvals = vals;
01663                      last_attr->a_next = a;
01664                      last_attr = a;
01665 
01666               } else {
01667                      ch_free( vals );
01668               }
01669 
01670               if ( old ) {
01671                      /* count all the vals */
01672                      i = 0;
01673                      for ( a = old->e_attrs; a != NULL; a = a->a_next ) {
01674                             if ( a->a_vals && a->a_flags ) {
01675                                    i += a->a_numvals;
01676                             }
01677                      }
01678                      if ( i ) {
01679                             vals = ch_malloc( (i + 1) * sizeof( struct berval ) );
01680                             i = 0;
01681                             for ( a=old->e_attrs; a; a=a->a_next ) {
01682                                    if ( a->a_vals && a->a_flags ) {
01683                                           for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
01684                                                  accesslog_val2val( a->a_desc, b, 0, &vals[i] );
01685                                           }
01686                                    }
01687                             }
01688                             vals[i].bv_val = NULL;
01689                             vals[i].bv_len = 0;
01690                             a = attr_alloc( ad_reqOld );
01691                             a->a_numvals = i;
01692                             a->a_vals = vals;
01693                             a->a_nvals = vals;
01694                             last_attr->a_next = a;
01695                      }
01696               }
01697               if ( logop == LOG_EN_MODIFY ) {
01698                      break;
01699               }
01700 
01701               /* Now log the actual modRDN info */
01702               attr_merge_one( e, ad_reqNewRDN, &op->orr_newrdn, &op->orr_nnewrdn );
01703               attr_merge_one( e, ad_reqDeleteOldRDN, op->orr_deleteoldrdn ?
01704                      (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
01705                      NULL );
01706               if ( op->orr_newSup ) {
01707                      attr_merge_one( e, ad_reqNewSuperior, op->orr_newSup, op->orr_nnewSup );
01708               }
01709               break;
01710 
01711        case LOG_EN_COMPARE:
01712               bv.bv_len = op->orc_ava->aa_desc->ad_cname.bv_len + 1 +
01713                      op->orc_ava->aa_value.bv_len;
01714               bv.bv_val = op->o_tmpalloc( bv.bv_len+1, op->o_tmpmemctx );
01715               ptr = lutil_strcopy( bv.bv_val, op->orc_ava->aa_desc->ad_cname.bv_val );
01716               *ptr++ = '=';
01717               AC_MEMCPY( ptr, op->orc_ava->aa_value.bv_val, op->orc_ava->aa_value.bv_len );
01718               bv.bv_val[bv.bv_len] = '\0';
01719               attr_merge_one( e, ad_reqAssertion, &bv, NULL );
01720               op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
01721               break;
01722 
01723        case LOG_EN_SEARCH:
01724               attr_merge_one( e, ad_reqScope, &scopes[op->ors_scope], NULL );
01725               attr_merge_one( e, ad_reqDerefAliases, &derefs[op->ors_deref], NULL );
01726               attr_merge_one( e, ad_reqAttrsOnly, op->ors_attrsonly ?
01727                      (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
01728                      NULL );
01729               if ( !BER_BVISEMPTY( &op->ors_filterstr ))
01730                      attr_merge_one( e, ad_reqFilter, &op->ors_filterstr, NULL );
01731               if ( op->ors_attrs ) {
01732                      int j;
01733                      /* count them */
01734                      for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++)
01735                             ;
01736                      vals = op->o_tmpalloc( (i+1) * sizeof(struct berval),
01737                             op->o_tmpmemctx );
01738                      for (i=0, j=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++) {
01739                             if (!BER_BVISEMPTY(&op->ors_attrs[i].an_name)) {
01740                                    vals[j] = op->ors_attrs[i].an_name;
01741                                    j++;
01742                             }
01743                      }
01744                      BER_BVZERO(&vals[j]);
01745                      attr_merge( e, ad_reqAttr, vals, NULL );
01746                      op->o_tmpfree( vals, op->o_tmpmemctx );
01747               }
01748               bv.bv_val = timebuf;
01749               bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", rs->sr_nentries );
01750               if ( bv.bv_len < sizeof( timebuf ) ) {
01751                      attr_merge_one( e, ad_reqEntries, &bv, NULL );
01752               } /* else? */
01753 
01754               bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_tlimit );
01755               if ( bv.bv_len < sizeof( timebuf ) ) {
01756                      attr_merge_one( e, ad_reqTimeLimit, &bv, NULL );
01757               } /* else? */
01758 
01759               bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_slimit );
01760               if ( bv.bv_len < sizeof( timebuf ) ) {
01761                      attr_merge_one( e, ad_reqSizeLimit, &bv, NULL );
01762               } /* else? */
01763               break;
01764 
01765        case LOG_EN_BIND:
01766               bv.bv_val = timebuf;
01767               bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->o_protocol );
01768               if ( bv.bv_len < sizeof( timebuf ) ) {
01769                      attr_merge_one( e, ad_reqVersion, &bv, NULL );
01770               } /* else? */
01771               if ( op->orb_method == LDAP_AUTH_SIMPLE ) {
01772                      attr_merge_one( e, ad_reqMethod, &simple, NULL );
01773               } else {
01774                      bv.bv_len = STRLENOF("SASL()") + op->orb_mech.bv_len;
01775                      bv.bv_val = op->o_tmpalloc( bv.bv_len + 1, op->o_tmpmemctx );
01776                      ptr = lutil_strcopy( bv.bv_val, "SASL(" );
01777                      ptr = lutil_strcopy( ptr, op->orb_mech.bv_val );
01778                      *ptr++ = ')';
01779                      *ptr = '\0';
01780                      attr_merge_one( e, ad_reqMethod, &bv, NULL );
01781                      op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
01782               }
01783 
01784               break;
01785 
01786        case LOG_EN_EXTENDED:
01787               if ( op->ore_reqdata ) {
01788                      attr_merge_one( e, ad_reqData, op->ore_reqdata, NULL );
01789               }
01790               break;
01791 
01792        case LOG_EN_UNKNOWN:
01793               /* we don't know its parameters, don't add any */
01794               break;
01795        }
01796 
01797        if ( e_uuid || !BER_BVISNULL( &uuid ) ) {
01798               struct berval *pbv = NULL;
01799 
01800               if ( !BER_BVISNULL( &uuid ) ) {
01801                      pbv = &uuid;
01802 
01803               } else {
01804                      a = attr_find( e_uuid->e_attrs, slap_schema.si_ad_entryUUID );
01805                      if ( a ) {
01806                             pbv = &a->a_vals[0];
01807                      }
01808               } 
01809 
01810               if ( pbv ) {
01811                      attr_merge_normalize_one( e, ad_reqEntryUUID, pbv, op->o_tmpmemctx );
01812               }
01813 
01814               if ( !BER_BVISNULL( &uuid ) ) {
01815                      ber_memfree( uuid.bv_val );
01816                      BER_BVZERO( &uuid );
01817               }
01818        }
01819 
01820        op2.o_hdr = op->o_hdr;
01821        op2.o_tag = LDAP_REQ_ADD;
01822        op2.o_bd = li->li_db;
01823        op2.o_dn = li->li_db->be_rootdn;
01824        op2.o_ndn = li->li_db->be_rootndn;
01825        op2.o_req_dn = e->e_name;
01826        op2.o_req_ndn = e->e_nname;
01827        op2.ora_e = e;
01828        op2.o_callback = &nullsc;
01829 
01830        if (( lo->mask & LOG_OP_WRITES ) && !BER_BVISEMPTY( &op->o_csn )) {
01831               slap_queue_csn( &op2, &op->o_csn );
01832        }
01833 
01834        op2.o_bd->be_add( &op2, &rs2 );
01835        if ( e == op2.ora_e ) entry_free( e );
01836        e = NULL;
01837 
01838 done:
01839        if ( lo->mask & LOG_OP_WRITES )
01840               ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
01841        if ( old ) entry_free( old );
01842        return SLAP_CB_CONTINUE;
01843 }
01844 
01845 /* Since Bind success is sent by the frontend, it won't normally enter
01846  * the overlay response callback. Add another callback to make sure it
01847  * gets here.
01848  */
01849 static int
01850 accesslog_bind_resp( Operation *op, SlapReply *rs )
01851 {
01852        BackendDB *be, db;
01853        int rc;
01854        slap_callback *sc;
01855 
01856        be = op->o_bd;
01857        db = *be;
01858        op->o_bd = &db;
01859        db.bd_info = op->o_callback->sc_private;
01860        rc = accesslog_response( op, rs );
01861        op->o_bd = be;
01862        sc = op->o_callback;
01863        op->o_callback = sc->sc_next;
01864        op->o_tmpfree( sc, op->o_tmpmemctx );
01865        return rc;
01866 }
01867 
01868 static int
01869 accesslog_op_bind( Operation *op, SlapReply *rs )
01870 {
01871        slap_callback *sc;
01872 
01873        sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
01874        sc->sc_response = accesslog_bind_resp;
01875        sc->sc_private = op->o_bd->bd_info;
01876 
01877        if ( op->o_callback ) {
01878               sc->sc_next = op->o_callback->sc_next;
01879               op->o_callback->sc_next = sc;
01880        } else {
01881               op->o_callback = sc;
01882        }
01883        return SLAP_CB_CONTINUE;
01884 }
01885 
01886 static int
01887 accesslog_mod_cleanup( Operation *op, SlapReply *rs )
01888 {
01889        slap_callback *sc = op->o_callback;
01890        slap_overinst *on = sc->sc_private;
01891        op->o_callback = sc->sc_next;
01892 
01893        op->o_tmpfree( sc, op->o_tmpmemctx );
01894 
01895        if ( on ) {
01896               BackendInfo *bi = op->o_bd->bd_info;
01897               op->o_bd->bd_info = (BackendInfo *)on;
01898               accesslog_response( op, rs );
01899               op->o_bd->bd_info = bi;
01900        }
01901        return 0;
01902 }
01903 
01904 static int
01905 accesslog_op_mod( Operation *op, SlapReply *rs )
01906 {
01907        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01908        log_info *li = on->on_bi.bi_private;
01909        slap_verbmasks *lo;
01910        int logop;
01911        int doit = 0;
01912 
01913        /* These internal ops are not logged */
01914        if ( op->o_dont_replicate && op->orm_no_opattrs )
01915               return SLAP_CB_CONTINUE;
01916 
01917        logop = accesslog_op2logop( op );
01918        lo = logops+logop+EN_OFFSET;
01919 
01920        if ( li->li_ops & lo->mask ) {
01921               doit = 1;
01922        } else {
01923               log_base *lb;
01924               for ( lb = li->li_bases; lb; lb = lb->lb_next )
01925                      if (( lb->lb_ops & lo->mask ) && dnIsSuffix( &op->o_req_ndn, &lb->lb_base )) {
01926                             doit = 1;
01927                             break;
01928                      }
01929        }
01930                      
01931        if ( doit ) {
01932               slap_callback *cb = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx ), *cb2;
01933               cb->sc_cleanup = accesslog_mod_cleanup;
01934               cb->sc_response = NULL;
01935               cb->sc_private = on;
01936               cb->sc_next = NULL;
01937               for ( cb2 = op->o_callback; cb2->sc_next; cb2 = cb2->sc_next );
01938               cb2->sc_next = cb;
01939 
01940               ldap_pvt_thread_rmutex_lock( &li->li_op_rmutex, op->o_tid );
01941               if ( li->li_oldf && ( op->o_tag == LDAP_REQ_DELETE ||
01942                      op->o_tag == LDAP_REQ_MODIFY ||
01943                      ( op->o_tag == LDAP_REQ_MODRDN && li->li_oldattrs )))
01944               {
01945                      int rc;
01946                      Entry *e;
01947 
01948                      op->o_bd->bd_info = (BackendInfo *)on->on_info;
01949                      rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
01950                      if ( e ) {
01951                             if ( test_filter( op, e, li->li_oldf ) == LDAP_COMPARE_TRUE )
01952                                    li->li_old = entry_dup( e );
01953                             be_entry_release_rw( op, e, 0 );
01954                      }
01955                      op->o_bd->bd_info = (BackendInfo *)on;
01956 
01957               } else {
01958                      int rc;
01959                      Entry *e;
01960 
01961                      op->o_bd->bd_info = (BackendInfo *)on->on_info;
01962                      rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
01963                      if ( e ) {
01964                             Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
01965                             if ( a ) {
01966                                    ber_dupbv( &li->li_uuid, &a->a_vals[0] );
01967                             }
01968                             be_entry_release_rw( op, e, 0 );
01969                      }
01970                      op->o_bd->bd_info = (BackendInfo *)on;
01971               }
01972        }
01973        return SLAP_CB_CONTINUE;
01974 }
01975 
01976 /* unbinds are broadcast to all backends; we only log it if this
01977  * backend was used for the original bind.
01978  */
01979 static int
01980 accesslog_unbind( Operation *op, SlapReply *rs )
01981 {
01982        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01983        if ( op->o_conn->c_authz_backend == on->on_info->oi_origdb ) {
01984               log_info *li = on->on_bi.bi_private;
01985               Operation op2 = {0};
01986               void *cids[SLAP_MAX_CIDS];
01987               SlapReply rs2 = {REP_RESULT};
01988               Entry *e;
01989 
01990               if ( !( li->li_ops & LOG_OP_UNBIND )) {
01991                      log_base *lb;
01992                      int i = 0;
01993 
01994                      for ( lb = li->li_bases; lb; lb=lb->lb_next )
01995                             if (( lb->lb_ops & LOG_OP_UNBIND ) && dnIsSuffix( &op->o_ndn, &lb->lb_base )) {
01996                                    i = 1;
01997                                    break;
01998                             }
01999                      if ( !i )
02000                             return SLAP_CB_CONTINUE;
02001               }
02002 
02003               e = accesslog_entry( op, rs, LOG_EN_UNBIND, &op2 );
02004               op2.o_hdr = op->o_hdr;
02005               op2.o_tag = LDAP_REQ_ADD;
02006               op2.o_bd = li->li_db;
02007               op2.o_dn = li->li_db->be_rootdn;
02008               op2.o_ndn = li->li_db->be_rootndn;
02009               op2.o_req_dn = e->e_name;
02010               op2.o_req_ndn = e->e_nname;
02011               op2.ora_e = e;
02012               op2.o_callback = &nullsc;
02013               op2.o_controls = cids;
02014               memset(cids, 0, sizeof( cids ));
02015 
02016               op2.o_bd->be_add( &op2, &rs2 );
02017               if ( e == op2.ora_e )
02018                      entry_free( e );
02019        }
02020        return SLAP_CB_CONTINUE;
02021 }
02022 
02023 static int
02024 accesslog_abandon( Operation *op, SlapReply *rs )
02025 {
02026        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
02027        log_info *li = on->on_bi.bi_private;
02028        Operation op2 = {0};
02029        void *cids[SLAP_MAX_CIDS];
02030        SlapReply rs2 = {REP_RESULT};
02031        Entry *e;
02032        char buf[64];
02033        struct berval bv;
02034 
02035        if ( !op->o_time )
02036               return SLAP_CB_CONTINUE;
02037 
02038        if ( !( li->li_ops & LOG_OP_ABANDON )) {
02039               log_base *lb;
02040               int i = 0;
02041 
02042               for ( lb = li->li_bases; lb; lb=lb->lb_next )
02043                      if (( lb->lb_ops & LOG_OP_ABANDON ) && dnIsSuffix( &op->o_ndn, &lb->lb_base )) {
02044                             i = 1;
02045                             break;
02046                      }
02047               if ( !i )
02048                      return SLAP_CB_CONTINUE;
02049        }
02050 
02051        e = accesslog_entry( op, rs, LOG_EN_ABANDON, &op2 );
02052        bv.bv_val = buf;
02053        bv.bv_len = snprintf( buf, sizeof( buf ), "%d", op->orn_msgid );
02054        if ( bv.bv_len < sizeof( buf ) ) {
02055               attr_merge_one( e, ad_reqId, &bv, NULL );
02056        } /* else? */
02057 
02058        op2.o_hdr = op->o_hdr;
02059        op2.o_tag = LDAP_REQ_ADD;
02060        op2.o_bd = li->li_db;
02061        op2.o_dn = li->li_db->be_rootdn;
02062        op2.o_ndn = li->li_db->be_rootndn;
02063        op2.o_req_dn = e->e_name;
02064        op2.o_req_ndn = e->e_nname;
02065        op2.ora_e = e;
02066        op2.o_callback = &nullsc;
02067        op2.o_controls = cids;
02068        memset(cids, 0, sizeof( cids ));
02069 
02070        op2.o_bd->be_add( &op2, &rs2 );
02071        if ( e == op2.ora_e )
02072               entry_free( e );
02073 
02074        return SLAP_CB_CONTINUE;
02075 }
02076 
02077 static int
02078 accesslog_operational( Operation *op, SlapReply *rs )
02079 {
02080        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
02081        log_info *li = on->on_bi.bi_private;
02082 
02083        if ( op->o_sync != SLAP_CONTROL_NONE )
02084               return SLAP_CB_CONTINUE;
02085 
02086        if ( rs->sr_entry != NULL
02087               && dn_match( &op->o_bd->be_nsuffix[0], &rs->sr_entry->e_nname ) )
02088        {
02089               Attribute     **ap;
02090 
02091               for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
02092                      /* just count */ ;
02093 
02094               if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
02095                             ad_inlist( ad_auditContext, rs->sr_attrs ) )
02096               {
02097                      *ap = attr_alloc( ad_auditContext );
02098                      attr_valadd( *ap,
02099                             &li->li_db->be_suffix[0],
02100                             &li->li_db->be_nsuffix[0], 1 );
02101               }
02102        }
02103 
02104        return SLAP_CB_CONTINUE;
02105 }
02106 
02107 static slap_overinst accesslog;
02108 
02109 static int
02110 accesslog_db_init(
02111        BackendDB *be,
02112        ConfigReply *cr
02113 )
02114 {
02115        slap_overinst *on = (slap_overinst *)be->bd_info;
02116        log_info *li = ch_calloc(1, sizeof(log_info));
02117 
02118        on->on_bi.bi_private = li;
02119        ldap_pvt_thread_rmutex_init( &li->li_op_rmutex );
02120        ldap_pvt_thread_mutex_init( &li->li_log_mutex );
02121        return 0;
02122 }
02123 
02124 static int
02125 accesslog_db_destroy(
02126        BackendDB *be,
02127        ConfigReply *cr
02128 )
02129 {
02130        slap_overinst *on = (slap_overinst *)be->bd_info;
02131        log_info *li = on->on_bi.bi_private;
02132        log_attr *la;
02133 
02134        if ( li->li_oldf )
02135               filter_free( li->li_oldf );
02136        for ( la=li->li_oldattrs; la; la=li->li_oldattrs ) {
02137               li->li_oldattrs = la->next;
02138               ch_free( la );
02139        }
02140        ldap_pvt_thread_mutex_destroy( &li->li_log_mutex );
02141        ldap_pvt_thread_rmutex_destroy( &li->li_op_rmutex );
02142        free( li );
02143        return LDAP_SUCCESS;
02144 }
02145 
02146 /* Create the logdb's root entry if it's missing */
02147 static void *
02148 accesslog_db_root(
02149        void *ctx,
02150        void *arg )
02151 {
02152        struct re_s *rtask = arg;
02153        slap_overinst *on = rtask->arg;
02154        log_info *li = on->on_bi.bi_private;
02155 
02156        Connection conn = {0};
02157        OperationBuffer opbuf;
02158        Operation *op;
02159 
02160        Entry *e;
02161        int rc;
02162 
02163        connection_fake_init( &conn, &opbuf, ctx );
02164        op = &opbuf.ob_op;
02165        op->o_bd = li->li_db;
02166        op->o_dn = li->li_db->be_rootdn;
02167        op->o_ndn = li->li_db->be_rootndn;
02168        rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e );
02169 
02170        if ( e ) {
02171               be_entry_release_rw( op, e, 0 );
02172 
02173        } else {
02174               SlapReply rs = {REP_RESULT};
02175               struct berval rdn, nrdn, attr;
02176               char *ptr;
02177               AttributeDescription *ad = NULL;
02178               const char *text = NULL;
02179               Entry *e_ctx;
02180               BackendDB db;
02181 
02182               e = entry_alloc();
02183               ber_dupbv( &e->e_name, li->li_db->be_suffix );
02184               ber_dupbv( &e->e_nname, li->li_db->be_nsuffix );
02185 
02186               attr_merge_one( e, slap_schema.si_ad_objectClass,
02187                      &log_container->soc_cname, NULL );
02188 
02189               dnRdn( &e->e_name, &rdn );
02190               dnRdn( &e->e_nname, &nrdn );
02191               ptr = ber_bvchr( &rdn, '=' );
02192 
02193               assert( ptr != NULL );
02194 
02195               attr.bv_val = rdn.bv_val;
02196               attr.bv_len = ptr - rdn.bv_val;
02197 
02198               slap_bv2ad( &attr, &ad, &text );
02199 
02200               rdn.bv_val = ptr+1;
02201               rdn.bv_len -= attr.bv_len + 1;
02202               ptr = ber_bvchr( &nrdn, '=' );
02203               nrdn.bv_len -= ptr - nrdn.bv_val + 1;
02204               nrdn.bv_val = ptr+1;
02205               attr_merge_one( e, ad, &rdn, &nrdn );
02206 
02207               /* Get contextCSN from main DB */
02208               op->o_bd = on->on_info->oi_origdb;
02209               rc = be_entry_get_rw( op, op->o_bd->be_nsuffix, NULL,
02210                      slap_schema.si_ad_contextCSN, 0, &e_ctx );
02211 
02212               if ( e_ctx ) {
02213                      Attribute *a;
02214 
02215                      a = attr_find( e_ctx->e_attrs, slap_schema.si_ad_contextCSN );
02216                      if ( a ) {
02217                             /* FIXME: contextCSN could have multiple values!
02218                              * should select the one with the server's SID */
02219                             attr_merge_one( e, slap_schema.si_ad_entryCSN,
02220                                    &a->a_vals[0], &a->a_nvals[0] );
02221                             attr_merge( e, a->a_desc, a->a_vals, a->a_nvals );
02222                      }
02223                      be_entry_release_rw( op, e_ctx, 0 );
02224               }
02225               db = *li->li_db;
02226               op->o_bd = &db;
02227 
02228               op->ora_e = e;
02229               op->o_req_dn = e->e_name;
02230               op->o_req_ndn = e->e_nname;
02231               op->o_callback = &nullsc;
02232               SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
02233               rc = op->o_bd->be_add( op, &rs );
02234               if ( e == op->ora_e )
02235                      entry_free( e );
02236        }
02237        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
02238        ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
02239        ldap_pvt_runqueue_remove( &slapd_rq, rtask );
02240        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
02241 
02242        return NULL;
02243 }
02244 
02245 static int
02246 accesslog_db_open(
02247        BackendDB *be,
02248        ConfigReply *cr
02249 )
02250 {
02251        slap_overinst *on = (slap_overinst *)be->bd_info;
02252        log_info *li = on->on_bi.bi_private;
02253 
02254 
02255        if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
02256               li->li_db = select_backend( &li->li_db_suffix, 0 );
02257               ch_free( li->li_db_suffix.bv_val );
02258               BER_BVZERO( &li->li_db_suffix );
02259        }
02260        if ( li->li_db == NULL ) {
02261               Debug( LDAP_DEBUG_ANY,
02262                      "accesslog: \"logdb <suffix>\" missing or invalid.\n",
02263                      0, 0, 0 );
02264               return 1;
02265        }
02266 
02267        if ( slapMode & SLAP_TOOL_MODE )
02268               return 0;
02269 
02270        if ( BER_BVISEMPTY( &li->li_db->be_rootndn )) {
02271               ber_dupbv( &li->li_db->be_rootdn, li->li_db->be_suffix );
02272               ber_dupbv( &li->li_db->be_rootndn, li->li_db->be_nsuffix );
02273        }
02274 
02275        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
02276        ldap_pvt_runqueue_insert( &slapd_rq, 3600, accesslog_db_root, on,
02277               "accesslog_db_root", li->li_db->be_suffix[0].bv_val );
02278        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
02279 
02280        return 0;
02281 }
02282 
02283 int accesslog_initialize()
02284 {
02285        int i, rc;
02286 
02287        accesslog.on_bi.bi_type = "accesslog";
02288        accesslog.on_bi.bi_db_init = accesslog_db_init;
02289        accesslog.on_bi.bi_db_destroy = accesslog_db_destroy;
02290        accesslog.on_bi.bi_db_open = accesslog_db_open;
02291 
02292        accesslog.on_bi.bi_op_add = accesslog_op_mod;
02293        accesslog.on_bi.bi_op_bind = accesslog_op_bind;
02294        accesslog.on_bi.bi_op_delete = accesslog_op_mod;
02295        accesslog.on_bi.bi_op_modify = accesslog_op_mod;
02296        accesslog.on_bi.bi_op_modrdn = accesslog_op_mod;
02297        accesslog.on_bi.bi_op_unbind = accesslog_unbind;
02298        accesslog.on_bi.bi_op_abandon = accesslog_abandon;
02299        accesslog.on_bi.bi_operational = accesslog_operational;
02300        accesslog.on_response = accesslog_response;
02301 
02302        accesslog.on_bi.bi_cf_ocs = log_cfocs;
02303 
02304        nullsc.sc_response = slap_null_cb;
02305 
02306        rc = config_register_schema( log_cfats, log_cfocs );
02307        if ( rc ) return rc;
02308 
02309        /* log schema integration */
02310        for ( i=0; lsyntaxes[i].oid; i++ ) {
02311               int code;
02312 
02313               code = register_syntax( &lsyntaxes[ i ].syn );
02314               if ( code != 0 ) {
02315                      Debug( LDAP_DEBUG_ANY,
02316                             "accesslog_init: register_syntax failed\n",
02317                             0, 0, 0 );
02318                      return code;
02319               }
02320 
02321               if ( lsyntaxes[i].mrs != NULL ) {
02322                      code = mr_make_syntax_compat_with_mrs(
02323                             lsyntaxes[i].oid, lsyntaxes[i].mrs );
02324                      if ( code < 0 ) {
02325                             Debug( LDAP_DEBUG_ANY,
02326                                    "accesslog_init: "
02327                                    "mr_make_syntax_compat_with_mrs "
02328                                    "failed\n",
02329                                    0, 0, 0 );
02330                             return code;
02331                      }
02332               }
02333        }
02334 
02335        for ( i=0; lattrs[i].at; i++ ) {
02336               int code;
02337 
02338               code = register_at( lattrs[i].at, lattrs[i].ad, 0 );
02339               if ( code ) {
02340                      Debug( LDAP_DEBUG_ANY,
02341                             "accesslog_init: register_at failed\n",
02342                             0, 0, 0 );
02343                      return -1;
02344               }
02345 #ifndef LDAP_DEVEL
02346               (*lattrs[i].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
02347 #endif
02348        }
02349 
02350        for ( i=0; locs[i].ot; i++ ) {
02351               int code;
02352 
02353               code = register_oc( locs[i].ot, locs[i].oc, 0 );
02354               if ( code ) {
02355                      Debug( LDAP_DEBUG_ANY,
02356                             "accesslog_init: register_oc failed\n",
02357                             0, 0, 0 );
02358                      return -1;
02359               }
02360 #ifndef LDAP_DEVEL
02361               (*locs[i].oc)->soc_flags |= SLAP_OC_HIDE;
02362 #endif
02363        }
02364 
02365        return overlay_register(&accesslog);
02366 }
02367 
02368 #if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC
02369 int
02370 init_module( int argc, char *argv[] )
02371 {
02372        return accesslog_initialize();
02373 }
02374 #endif
02375 
02376 #endif /* SLAPD_OVER_ACCESSLOG */