#ifdef SLAPD_OVER_AUDITLOG
#include <stdio.h>
+#include <fcntl.h>
#include <ac/string.h>
#include <ac/ctype.h>
typedef struct auditlog_data {
ldap_pvt_thread_mutex_t ad_mutex;
char *ad_logfile;
+ int ad_nonblocking;
} auditlog_data;
static ConfigTable auditlogcfg[] = {
"DESC 'Filename for auditlogging' "
"EQUALITY caseExactMatch "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "auditlognonblocking", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(auditlog_data, ad_nonblocking),
+ "( OLcfgOvAt:15.2 NAME 'olcAuditlogNonBlocking' "
+ "DESC 'Set audit log file descriptor to non-blocking mode' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};
"NAME 'olcAuditlogConfig' "
"DESC 'Auditlog configuration' "
"SUP olcOverlayConfig "
- "MAY ( olcAuditlogFile ) )",
+ "MAY ( olcAuditlogFile $ olcAuditlogNonBlocking ) )",
Cft_Overlay, auditlogcfg },
{ NULL, 0, NULL }
};
struct berval *b, *who = NULL, peername;
char *what, *whatm, *suffix;
time_t stamp;
- int i;
+ int i, flags, fd;
if ( rs->sr_err != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
peername = op->o_conn->c_peer_name;
ldap_pvt_thread_mutex_lock(&ad->ad_mutex);
- if((f = fopen(ad->ad_logfile, "a")) == NULL) {
- ldap_pvt_thread_mutex_unlock(&ad->ad_mutex);
- return SLAP_CB_CONTINUE;
+
+ /* Open file with optional non-blocking mode */
+ flags = O_WRONLY | O_CREAT | O_APPEND;
+ if ( ad->ad_nonblocking ) {
+ flags |= O_NONBLOCK;
+ }
+
+ fd = open(ad->ad_logfile, flags, 0666);
+ if ( fd == -1 ) goto done;
+
+ f = fdopen(fd, "a");
+ if ( f == NULL ) {
+ close(fd);
+ goto done;
}
stamp = slap_get_time();
fprintf(f, "# end %s %ld\n\n", what, (long)stamp);
fclose(f);
+done:
ldap_pvt_thread_mutex_unlock(&ad->ad_mutex);
return SLAP_CB_CONTINUE;
}
--- /dev/null
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2025 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $AUDITLOG = auditlogno; then
+ echo "Auditlog overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $TESTDIR/confdir
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $NAKEDCONF > $CONF1
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $TESTOUT
+
+if [ "$AUDITLOG" = auditlogmod ]; then
+ echo "Inserting auditlog overlay on provider..."
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: auditlog.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+indexInclude="" mainInclude="" nullExclude=""
+test $INDEXDB = indexdb || indexInclude="# "
+test $MAINDB = maindb || mainInclude="# "
+case $BACKEND in
+null) nullExclude="# " ;;
+esac
+
+AUDITLOGFILE="$TESTDIR/audit.log"
+
+echo "Running ldapadd to build slapd config database..."
+$LDAPADD -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
+ >> $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+olcRootDN: cn=Manager,$BASEDN
+olcRootPW:: c2VjcmV0
+olcMonitoring: TRUE
+${nullExclude}olcDbDirectory: $TESTDIR/db.1.a/
+${indexInclude}olcDbIndex: objectClass eq
+${indexInclude}olcDbIndex: cn pres,eq,sub
+${indexInclude}olcDbIndex: uid pres,eq,sub
+${indexInclude}olcDbIndex: sn pres,eq,sub
+${mainInclude}olcDbMode: 384
+
+dn: olcOverlay={0}auditlog,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAuditlogConfig
+olcOverlay: {0}auditlog
+olcAuditlogFile: $AUDITLOGFILE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapadd to build slapd database..."
+$LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: $BASEDN
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=People,$BASEDN
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=John Doe,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: John Doe
+sn: Doe
+givenName: John
+mail: john.doe@example.com
+
+dn: cn=Jane Smith,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Jane Smith
+sn: Smith
+givenName: Jane
+mail: jane.smith@example.com
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ADD operations are logged..."
+$LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Bob Jones,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Bob Jones
+sn: Jones
+givenName: Bob
+mail: bob.jones@example.com
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Verifying ADD operation was logged..."
+grep "# add " "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain ADD operations!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+grep "cn=Bob Jones" "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain Bob Jones ADD entry!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing MODIFY operations are logged..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=John Doe,ou=People,$BASEDN
+changetype: modify
+replace: mail
+mail: john.doe.new@example.com
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Verifying MODIFY operation was logged..."
+grep "# modify " "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain MODIFY operations!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+grep "john.doe.new@example.com" "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain the modified email address!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing MODRDN operations are logged..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Jane Smith,ou=People,$BASEDN
+changetype: modrdn
+newrdn: cn=Jane Brown
+deleteoldrdn: 1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify modrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Verifying MODRDN operation was logged..."
+grep "# modrdn " "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain MODRDN operations!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+grep "newrdn: cn=Jane Brown" "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain the new RDN!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing DELETE operations are logged..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Bob Jones,ou=People,$BASEDN
+changetype: delete
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Verifying DELETE operation was logged..."
+grep "# delete " "$AUDITLOGFILE" > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Auditlog does not contain DELETE operations!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Cleaning up audit log"
+rm -f "$AUDITLOGFILE"
+
+if test "$OS_WINDOWS" == "yes"; then
+ echo "Skipping non-blocking tests on Windows..."
+else
+ echo "Creating named pipe for blocking/non-blocking test"
+ PIPEFILE="$TESTDIR/audit.pipe"
+ mknod "$PIPEFILE" p
+
+ echo "Testing non-blocking mode configuration..."
+ $LDAPMODIFY -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
+ >> $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}auditlog,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcAuditlogFile
+olcAuditlogFile: $PIPEFILE
+-
+replace: olcAuditlogNonBlocking
+olcAuditlogNonBlocking: TRUE
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed for non-blocking config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing operation with non-blocking mode and no pipe reader (should succeed)..."
+ $LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=NonBlocking Test,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: NonBlocking Test
+sn: Test
+givenName: NonBlocking
+mail: nonblocking.test@example.com
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed with non-blocking mode and no reader ($RC)! This should have succeeded."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Now testing non-blocking mode with pipe reader available..."
+ cat $PIPEFILE > $TESTDIR/pipe.log &
+ CATPID=$!
+
+ echo "Adding entry with non-blocking mode and active reader..."
+ $LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Test User,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Test User
+sn: User
+givenName: Test
+mail: test.user@example.com
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed with non-blocking mode and reader ($RC)!"
+ kill $CATPID 2>/dev/null
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Verifying entry was logged to named pipe..."
+ sleep 1
+ kill $CATPID 2>/dev/null
+ wait $CATPID 2>/dev/null
+
+ if [ ! -f $TESTDIR/pipe.log ]; then
+ echo "Named pipe log file was not created!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ grep "cn=Test User" $TESTDIR/pipe.log > /dev/null
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Auditlog does not contain new entries with non-blocking mode and reader!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Testing blocking mode and no pipe reader (should timeout)..."
+ $LDAPMODIFY -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
+ >> $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}auditlog,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcAuditlogFile
+olcAuditlogFile: $PIPEFILE
+-
+replace: olcAuditlogNonBlocking
+olcAuditlogNonBlocking: FALSE
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed for blocking config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Adding entry with blocking mode and no active reader..."
+ timeout 1 $LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Blocking Test,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Blocking Test
+sn: Test
+givenName: Blocking
+mail: blocking.test@example.com
+
+EOF
+ RC=$?
+ if test $RC != 124 ; then
+ echo "Blocking mode result: $BLOCKING_RC (should be timeout/error)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ # Briefly open the pipe, so that blocking writes are unblocked, and we can kill slapd cleanly
+ exec 3<>"$PIPEFILE"
+
+ echo "Cleaning up named pipe and audit logs..."
+ rm -f $PIPEFILE $TESTDIR/pipe.log "$AUDITLOGFILE"
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0