summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/slapd-modules/README64
-rw-r--r--contrib/slapd-modules/acl/Makefile50
-rw-r--r--contrib/slapd-modules/acl/README.gssacl32
-rw-r--r--contrib/slapd-modules/acl/README.posixgroup35
-rw-r--r--contrib/slapd-modules/acl/gssacl.c316
-rw-r--r--contrib/slapd-modules/acl/posixgroup.c329
-rw-r--r--contrib/slapd-modules/addpartial/Makefile46
-rw-r--r--contrib/slapd-modules/addpartial/README72
-rw-r--r--contrib/slapd-modules/addpartial/addpartial-overlay.c349
-rw-r--r--contrib/slapd-modules/adremap/Makefile68
-rw-r--r--contrib/slapd-modules/adremap/adremap.c652
-rw-r--r--contrib/slapd-modules/adremap/slapo-adremap.5104
-rw-r--r--contrib/slapd-modules/allop/Makefile58
-rw-r--r--contrib/slapd-modules/allop/README26
-rw-r--r--contrib/slapd-modules/allop/allop.c262
-rw-r--r--contrib/slapd-modules/allop/slapo-allop.563
-rw-r--r--contrib/slapd-modules/allowed/Makefile58
-rw-r--r--contrib/slapd-modules/allowed/README73
-rw-r--r--contrib/slapd-modules/allowed/allowed.c504
-rw-r--r--contrib/slapd-modules/authzid/Makefile58
-rw-r--r--contrib/slapd-modules/authzid/authzid.c390
-rw-r--r--contrib/slapd-modules/autogroup/Makefile46
-rw-r--r--contrib/slapd-modules/autogroup/README120
-rw-r--r--contrib/slapd-modules/autogroup/autogroup.c2236
-rw-r--r--contrib/slapd-modules/autogroup/slapo-autogroup.5116
-rw-r--r--contrib/slapd-modules/cloak/Makefile58
-rw-r--r--contrib/slapd-modules/cloak/cloak.c354
-rw-r--r--contrib/slapd-modules/cloak/slapo-cloak.582
-rw-r--r--contrib/slapd-modules/comp_match/Makefile69
-rw-r--r--contrib/slapd-modules/comp_match/README127
-rw-r--r--contrib/slapd-modules/comp_match/asn.h57
-rw-r--r--contrib/slapd-modules/comp_match/asn_to_syn_mr.c282
-rw-r--r--contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn65
-rw-r--r--contrib/slapd-modules/comp_match/authorityKeyIdentifier.c2058
-rw-r--r--contrib/slapd-modules/comp_match/authorityKeyIdentifier.h327
-rw-r--r--contrib/slapd-modules/comp_match/certificate.asn1175
-rw-r--r--contrib/slapd-modules/comp_match/certificate.c3249
-rw-r--r--contrib/slapd-modules/comp_match/certificate.h379
-rw-r--r--contrib/slapd-modules/comp_match/componentlib.c2370
-rw-r--r--contrib/slapd-modules/comp_match/componentlib.h593
-rw-r--r--contrib/slapd-modules/comp_match/crl.c1294
-rw-r--r--contrib/slapd-modules/comp_match/crl.h359
-rw-r--r--contrib/slapd-modules/comp_match/init.c839
-rw-r--r--contrib/slapd-modules/datamorph/Makefile77
-rw-r--r--contrib/slapd-modules/datamorph/datamorph.c2091
-rw-r--r--contrib/slapd-modules/datamorph/slapo-datamorph.5338
-rw-r--r--contrib/slapd-modules/datamorph/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/config.ldif108
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/datamorph.conf49
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test.ldif434
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif3
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif4
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif4
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif3
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-config.ldif9
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif31
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif23
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test003-config.ldif30
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test003-out.ldif125
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif10
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif6
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif30
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-out.ldif212
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test007-config.ldif30
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/run229
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/all102
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/common.sh152
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test001-config248
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test002-add-delete147
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test003-search106
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test004-compare62
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test005-modify89
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test006-modrdn52
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication296
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication299
-rw-r--r--contrib/slapd-modules/denyop/Makefile46
-rw-r--r--contrib/slapd-modules/denyop/denyop.c260
-rw-r--r--contrib/slapd-modules/dsaschema/Makefile46
-rw-r--r--contrib/slapd-modules/dsaschema/README23
-rw-r--r--contrib/slapd-modules/dsaschema/dsaschema.c369
-rw-r--r--contrib/slapd-modules/dupent/Makefile58
-rw-r--r--contrib/slapd-modules/dupent/dupent.c558
-rw-r--r--contrib/slapd-modules/emptyds/Makefile78
-rw-r--r--contrib/slapd-modules/emptyds/README66
-rw-r--r--contrib/slapd-modules/emptyds/emptyds.c325
-rw-r--r--contrib/slapd-modules/emptyds/slapo-emptyds.568
-rw-r--r--contrib/slapd-modules/emptyds/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/emptyds/tests/data/emptyds.conf54
-rw-r--r--contrib/slapd-modules/emptyds/tests/data/test001.ldif71
-rw-r--r--contrib/slapd-modules/emptyds/tests/data/test001.out54
-rwxr-xr-xcontrib/slapd-modules/emptyds/tests/run218
-rwxr-xr-xcontrib/slapd-modules/emptyds/tests/scripts/all92
-rwxr-xr-xcontrib/slapd-modules/emptyds/tests/scripts/test001-emptyds137
-rw-r--r--contrib/slapd-modules/kinit/Makefile46
-rw-r--r--contrib/slapd-modules/kinit/README36
-rw-r--r--contrib/slapd-modules/kinit/kinit.c295
-rw-r--r--contrib/slapd-modules/lastbind/Makefile68
-rw-r--r--contrib/slapd-modules/lastbind/lastbind.c320
-rw-r--r--contrib/slapd-modules/lastbind/slapo-lastbind.5108
-rw-r--r--contrib/slapd-modules/lastmod/Makefile58
-rw-r--r--contrib/slapd-modules/lastmod/lastmod.c963
-rw-r--r--contrib/slapd-modules/lastmod/slapo-lastmod.5185
-rw-r--r--contrib/slapd-modules/noopsrch/Makefile58
-rw-r--r--contrib/slapd-modules/noopsrch/noopsrch.c255
-rw-r--r--contrib/slapd-modules/nops/Makefile58
-rw-r--r--contrib/slapd-modules/nops/nops.c178
-rw-r--r--contrib/slapd-modules/nops/slapo-nops.532
-rw-r--r--contrib/slapd-modules/nssov/Makefile86
-rw-r--r--contrib/slapd-modules/nssov/README134
-rw-r--r--contrib/slapd-modules/nssov/alias.c116
-rw-r--r--contrib/slapd-modules/nssov/ether.c167
-rw-r--r--contrib/slapd-modules/nssov/group.c346
-rw-r--r--contrib/slapd-modules/nssov/host.c161
-rw-r--r--contrib/slapd-modules/nssov/ldapns.schema38
-rw-r--r--contrib/slapd-modules/nssov/netgroup.c199
-rw-r--r--contrib/slapd-modules/nssov/network.c161
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/README15
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h91
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h391
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h305
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c520
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h83
-rw-r--r--contrib/slapd-modules/nssov/nssov.c1045
-rw-r--r--contrib/slapd-modules/nssov/nssov.h348
-rw-r--r--contrib/slapd-modules/nssov/pam.c862
-rw-r--r--contrib/slapd-modules/nssov/passwd.c435
-rw-r--r--contrib/slapd-modules/nssov/protocol.c156
-rw-r--r--contrib/slapd-modules/nssov/rpc.c158
-rw-r--r--contrib/slapd-modules/nssov/service.c250
-rw-r--r--contrib/slapd-modules/nssov/shadow.c257
-rw-r--r--contrib/slapd-modules/nssov/slapo-nssov.5316
-rw-r--r--contrib/slapd-modules/passwd/Makefile70
-rw-r--r--contrib/slapd-modules/passwd/README69
-rw-r--r--contrib/slapd-modules/passwd/apr1-atol.pl29
-rw-r--r--contrib/slapd-modules/passwd/apr1-ltoa.pl31
-rw-r--r--contrib/slapd-modules/passwd/apr1.c236
-rw-r--r--contrib/slapd-modules/passwd/kerberos.c211
-rw-r--r--contrib/slapd-modules/passwd/netscape.c83
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/Makefile62
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/README99
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c451
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5112
-rw-r--r--contrib/slapd-modules/passwd/radius.c149
-rw-r--r--contrib/slapd-modules/passwd/sha2/Makefile59
-rw-r--r--contrib/slapd-modules/passwd/sha2/README144
-rw-r--r--contrib/slapd-modules/passwd/sha2/sha2.c1070
-rw-r--r--contrib/slapd-modules/passwd/sha2/sha2.h236
-rw-r--r--contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5118
-rw-r--r--contrib/slapd-modules/passwd/sha2/slapd-sha2.c508
-rw-r--r--contrib/slapd-modules/passwd/slapd-pw-radius.5110
-rw-r--r--contrib/slapd-modules/passwd/totp/Makefile58
-rw-r--r--contrib/slapd-modules/passwd/totp/README87
-rw-r--r--contrib/slapd-modules/passwd/totp/slapd-totp.c1000
-rw-r--r--contrib/slapd-modules/passwd/totp/slapo-totp.5109
-rw-r--r--contrib/slapd-modules/ppm/CHANGELOG.md38
-rw-r--r--contrib/slapd-modules/ppm/CONTRIBUTIONS.md5
-rw-r--r--contrib/slapd-modules/ppm/INSTALL.md51
-rw-r--r--contrib/slapd-modules/ppm/LICENSE50
-rw-r--r--contrib/slapd-modules/ppm/Makefile97
-rw-r--r--contrib/slapd-modules/ppm/README.md1
-rw-r--r--contrib/slapd-modules/ppm/ppm.c684
-rw-r--r--contrib/slapd-modules/ppm/ppm.example85
-rw-r--r--contrib/slapd-modules/ppm/ppm.h125
-rw-r--r--contrib/slapd-modules/ppm/ppm.md343
-rw-r--r--contrib/slapd-modules/ppm/ppm_test.c66
-rw-r--r--contrib/slapd-modules/ppm/slapm-ppm.5360
-rwxr-xr-xcontrib/slapd-modules/ppm/unit_tests.sh118
-rw-r--r--contrib/slapd-modules/proxyOld/Makefile58
-rw-r--r--contrib/slapd-modules/proxyOld/README31
-rw-r--r--contrib/slapd-modules/proxyOld/proxyOld.c128
-rwxr-xr-xcontrib/slapd-modules/rbac/Makefile63
-rw-r--r--contrib/slapd-modules/rbac/init.c324
-rw-r--r--contrib/slapd-modules/rbac/jts.c198
-rw-r--r--contrib/slapd-modules/rbac/ldap_rbac.h55
-rw-r--r--contrib/slapd-modules/rbac/rbac.c2169
-rw-r--r--contrib/slapd-modules/rbac/rbac.h402
-rw-r--r--contrib/slapd-modules/rbac/rbacacl.c37
-rw-r--r--contrib/slapd-modules/rbac/rbacaudit.c233
-rw-r--r--contrib/slapd-modules/rbac/rbacperm.c233
-rw-r--r--contrib/slapd-modules/rbac/rbacreq.c89
-rw-r--r--contrib/slapd-modules/rbac/rbacsess.c999
-rw-r--r--contrib/slapd-modules/rbac/rbacuser.c620
-rw-r--r--contrib/slapd-modules/rbac/slapo-rbac.5157
-rw-r--r--contrib/slapd-modules/rbac/util.c531
-rw-r--r--contrib/slapd-modules/samba4/Makefile68
-rw-r--r--contrib/slapd-modules/samba4/README72
-rw-r--r--contrib/slapd-modules/samba4/pguid.c460
-rw-r--r--contrib/slapd-modules/samba4/rdnval.c657
-rw-r--r--contrib/slapd-modules/samba4/vernum.c459
-rw-r--r--contrib/slapd-modules/smbk5pwd/Makefile77
-rw-r--r--contrib/slapd-modules/smbk5pwd/README94
-rw-r--r--contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5177
-rw-r--r--contrib/slapd-modules/smbk5pwd/smbk5pwd.c1084
-rw-r--r--contrib/slapd-modules/trace/Makefile46
-rw-r--r--contrib/slapd-modules/trace/trace.c256
-rw-r--r--contrib/slapd-modules/usn/Makefile46
-rw-r--r--contrib/slapd-modules/usn/README44
-rw-r--r--contrib/slapd-modules/usn/usn.c330
-rw-r--r--contrib/slapd-modules/variant/Makefile77
-rw-r--r--contrib/slapd-modules/variant/slapo-variant.5472
-rw-r--r--contrib/slapd-modules/variant/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/variant/tests/data/additional-config.ldif23
-rw-r--r--contrib/slapd-modules/variant/tests/data/config.ldif89
-rw-r--r--contrib/slapd-modules/variant/tests/data/hidden.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif6
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif16
-rw-r--r--contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif7
-rw-r--r--contrib/slapd-modules/variant/tests/data/test003-out.ldif124
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-changes.ldif35
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-out.ldif206
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test006-config.ldif61
-rw-r--r--contrib/slapd-modules/variant/tests/data/test006-out.ldif151
-rw-r--r--contrib/slapd-modules/variant/tests/data/test007-out.ldif6
-rw-r--r--contrib/slapd-modules/variant/tests/data/test010-out.ldif52
-rw-r--r--contrib/slapd-modules/variant/tests/data/test011-out.ldif10
-rw-r--r--contrib/slapd-modules/variant/tests/data/test012-data.ldif13
-rw-r--r--contrib/slapd-modules/variant/tests/data/test012-out.ldif9
-rw-r--r--contrib/slapd-modules/variant/tests/data/variant.conf17
-rwxr-xr-xcontrib/slapd-modules/variant/tests/run229
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/all102
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/common.sh115
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test001-config209
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test002-add-delete113
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test003-search113
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test004-compare63
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test005-modify120
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test006-acl323
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test007-subtypes67
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test008-variant-replication194
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test009-ignored-replication227
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test010-limits99
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test011-referral169
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test012-crossdb90
-rw-r--r--contrib/slapd-modules/variant/variant.c1424
-rw-r--r--contrib/slapd-modules/vc/Makefile58
-rw-r--r--contrib/slapd-modules/vc/vc.c439
246 files changed, 59053 insertions, 0 deletions
diff --git a/contrib/slapd-modules/README b/contrib/slapd-modules/README
new file mode 100644
index 0000000..136f720
--- /dev/null
+++ b/contrib/slapd-modules/README
@@ -0,0 +1,64 @@
+Copyright 2008-2022 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.
+
+This directory contains native-API slapd modules (overlays etc):
+
+acl (plugins)
+ Plugins implementing access rules. Currently one plugin
+ which implements access control based on posixGroup membership.
+
+addpartial (overlay)
+ Treat Add requests as Modify requests if the entry exists.
+
+allop (overlay)
+ Return operational attributes for root DSE even when not
+ requested, since some clients expect this.
+
+autogroup (overlay)
+ Automated updates of group memberships.
+
+cloak (overlay)
+ Hide specific attributes unless explicitly requested
+
+comp_match (plugin)
+ Component Matching rules (RFC 3687).
+
+denyop (overlay)
+ Deny selected operations, returning unwillingToPerform.
+
+dsaschema (plugin)
+ Permit loading DSA-specific schema, including operational attrs.
+
+lastbind (overlay)
+ Record the last successful authentication on an entry.
+
+lastmod (overlay)
+ Track the time of the last write operation to a database.
+
+nops (overlay)
+ Remove null operations, e.g. changing a value to same as before.
+
+nssov (listener overlay)
+ Handle NSS lookup requests through a local Unix Domain socket.
+
+passwd (plugins)
+ Support additional password mechanisms.
+ Currently Kerberos, Netscape MTA-MD5 and RADIUS.
+
+proxyOld (plugin)
+ Proxy Authorization compatibility with obsolete internet-draft.
+
+smbk5pwd (overlay)
+ Make the PasswordModify Extended Operation update Kerberos
+ keys and Samba password hashes as well as userPassword.
+
+trace (overlay)
+ Trace overlay invocation.
+
+usn (overlay)
+ Maintain usnCreated and usnChanged attrs similar to Microsoft AD.
+
+$OpenLDAP$
diff --git a/contrib/slapd-modules/acl/Makefile b/contrib/slapd-modules/acl/Makefile
new file mode 100644
index 0000000..090575d
--- /dev/null
+++ b/contrib/slapd-modules/acl/Makefile
@@ -0,0 +1,50 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = posixgroup.la gssacl.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+posixgroup.la: posixgroup.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+gssacl.la: gssacl.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/acl/README.gssacl b/contrib/slapd-modules/acl/README.gssacl
new file mode 100644
index 0000000..368b178
--- /dev/null
+++ b/contrib/slapd-modules/acl/README.gssacl
@@ -0,0 +1,32 @@
+This directory contains native slapd plugins that implement access rules.
+
+gssacl.c contains a simple example that implements access control
+based on GSS naming extensions attributes.
+
+To use the acl-gssacl plugin, add:
+
+moduleload acl-gssacl.so
+
+to your slapd configuration file.
+It is configured using
+
+access to <what>
+ by dynacl/gss/<attribute>.[.{base,regex,expand}]=<valpat> {<level>|<priv(s)>}
+
+The default is "exact"; in case of "expand", "<valpat>" results from
+the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>"
+describe the level of privilege this rule can assume.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o acl-gssacl.so gssacl.c
+
+
+---
+Copyright 2011 PADL Software Pty Ltd. 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.
+
diff --git a/contrib/slapd-modules/acl/README.posixgroup b/contrib/slapd-modules/acl/README.posixgroup
new file mode 100644
index 0000000..5e0460d
--- /dev/null
+++ b/contrib/slapd-modules/acl/README.posixgroup
@@ -0,0 +1,35 @@
+This directory contains native slapd plugins that implement access rules.
+
+posixgroup.c contains a simple example that implements access control
+based on posixGroup membership, loosely inspired by ITS#3849. It should
+be made clear that this access control policy does not reflect any
+standard track model of handling access control, and should be
+essentially viewed as an illustration of the use of the dynamic
+extension of access control within slapd.
+
+To use the acl-posixgroup plugin, add:
+
+moduleload acl-posixgroup.so
+
+to your slapd configuration file; it requires "nis.schema" to be loaded.
+It is configured using
+
+access to <what>
+ by dynacl/posixGroup[.{exact,expand}]=<dnpat> {<level>|<priv(s)}
+
+The default is "exact"; in case of "expand", "<dnpat>" results from
+the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>"
+describe the level of privilege this rule can assume.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o acl-posixgroup.so posixgroup.c
+
+---
+Copyright 2005-2022 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.
+
diff --git a/contrib/slapd-modules/acl/gssacl.c b/contrib/slapd-modules/acl/gssacl.c
new file mode 100644
index 0000000..12d3b9a
--- /dev/null
+++ b/contrib/slapd-modules/acl/gssacl.c
@@ -0,0 +1,316 @@
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2011 PADL Software Pty Ltd.
+ * 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>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <slap.h>
+#include <lutil.h>
+
+#include <sasl/sasl.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+#define ACL_BUF_SIZE 1024
+
+typedef struct gssattr_t {
+ slap_style_t gssattr_style;
+ struct berval gssattr_name; /* asserted name */
+ struct berval gssattr_value; /* asserted value */
+} gssattr_t;
+
+static int gssattr_dynacl_destroy( void *priv );
+
+static int
+regex_matches(
+ struct berval *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ struct berval *dn_matches, /* buffer with $N expansion variables from DN */
+ struct berval *val_matches, /* buffer with $N expansion variables from val */
+ AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
+);
+
+static int
+gssattr_dynacl_parse(
+ const char *fname,
+ int lineno,
+ const char *opts,
+ slap_style_t style,
+ const char *pattern,
+ void **privp )
+{
+ gssattr_t *gssattr;
+
+ gssattr = (gssattr_t *)ch_calloc( 1, sizeof( gssattr_t ) );
+
+ if ( opts == NULL || opts[0] == '\0' ) {
+ fprintf( stderr, "%s line %d: GSS ACL: no attribute specified.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ if ( pattern == NULL || pattern[0] == '\0' ) {
+ fprintf( stderr, "%s line %d: GSS ACL: no attribute value specified.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ gssattr->gssattr_style = style;
+
+ switch ( gssattr->gssattr_style ) {
+ case ACL_STYLE_BASE:
+ case ACL_STYLE_REGEX:
+ case ACL_STYLE_EXPAND:
+ break;
+ default:
+ fprintf( stderr, "%s line %d: GSS ACL: unsupported style \"%s\".\n",
+ fname, lineno, style_strings[style] );
+ goto cleanup;
+ break;
+ }
+
+ ber_str2bv( opts, 0, 1, &gssattr->gssattr_name );
+ ber_str2bv( pattern, 0, 1, &gssattr->gssattr_value );
+
+ *privp = (void *)gssattr;
+ return 0;
+
+cleanup:
+ (void)gssattr_dynacl_destroy( (void *)gssattr );
+
+ return 1;
+}
+
+static int
+gssattr_dynacl_unparse(
+ void *priv,
+ struct berval *bv )
+{
+ gssattr_t *gssattr = (gssattr_t *)priv;
+ char *ptr;
+
+ bv->bv_len = STRLENOF( " dynacl/gss/.expand=" ) +
+ gssattr->gssattr_name.bv_len +
+ gssattr->gssattr_value.bv_len;
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+
+ ptr = lutil_strcopy( bv->bv_val, " dynacl/gss/" );
+ ptr = lutil_strncopy( ptr, gssattr->gssattr_name.bv_val,
+ gssattr->gssattr_name.bv_len );
+ switch ( gssattr->gssattr_style ) {
+ case ACL_STYLE_BASE:
+ ptr = lutil_strcopy( ptr, ".exact=" );
+ break;
+ case ACL_STYLE_REGEX:
+ ptr = lutil_strcopy( ptr, ".regex=" );
+ break;
+ case ACL_STYLE_EXPAND:
+ ptr = lutil_strcopy( ptr, ".expand=" );
+ break;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ptr = lutil_strncopy( ptr, gssattr->gssattr_value.bv_val,
+ gssattr->gssattr_value.bv_len );
+
+ ptr[ 0 ] = '\0';
+
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+gssattr_dynacl_mask(
+ void *priv,
+ Operation *op,
+ Entry *target,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ gssattr_t *gssattr = (gssattr_t *)priv;
+ sasl_conn_t *sasl_ctx = op->o_conn->c_sasl_authctx;
+ gss_name_t gss_name = GSS_C_NO_NAME;
+ OM_uint32 major, minor;
+ int more = -1;
+ int authenticated, complete;
+ gss_buffer_desc attr = GSS_C_EMPTY_BUFFER;
+ int granted = 0;
+
+ ACL_INVALIDATE( *deny );
+
+ if ( sasl_ctx == NULL ||
+ sasl_getprop( sasl_ctx, SASL_GSS_PEER_NAME, (const void **)&gss_name) != 0 ||
+ gss_name == GSS_C_NO_NAME ) {
+ return 0;
+ }
+
+ attr.length = gssattr->gssattr_name.bv_len;
+ attr.value = gssattr->gssattr_name.bv_val;
+
+ while ( more != 0 ) {
+ AclRegexMatches amatches = { 0 };
+ gss_buffer_desc gss_value = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gss_display_value = GSS_C_EMPTY_BUFFER;
+ struct berval bv_value;
+
+ major = gss_get_name_attribute( &minor, gss_name, &attr,
+ &authenticated, &complete,
+ &gss_value, &gss_display_value, &more );
+ if ( GSS_ERROR( major ) ) {
+ break;
+ } else if ( authenticated == 0 ) {
+ gss_release_buffer( &minor, &gss_value );
+ gss_release_buffer( &minor, &gss_display_value );
+ continue;
+ }
+
+ bv_value.bv_len = gss_value.length;
+ bv_value.bv_val = (char *)gss_value.value;
+
+ if ( !ber_bvccmp( &gssattr->gssattr_value, '*' ) ) {
+ if ( gssattr->gssattr_style != ACL_STYLE_BASE ) {
+ amatches.dn_count = nmatch;
+ AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
+ }
+
+ switch ( gssattr->gssattr_style ) {
+ case ACL_STYLE_REGEX:
+ /* XXX assumes value NUL terminated */
+ granted = regex_matches( &gssattr->gssattr_value, bv_value.bv_val,
+ &target->e_nname, val, &amatches );
+ break;
+ case ACL_STYLE_EXPAND: {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ granted = ( acl_string_expand( &bv, &gssattr->gssattr_value,
+ &target->e_nname, val,
+ &amatches ) == 0 ) &&
+ ( ber_bvstrcmp( &bv, &bv_value) == 0 );
+ break;
+ }
+ case ACL_STYLE_BASE:
+ granted = ( ber_bvstrcmp( &gssattr->gssattr_value, &bv_value ) == 0 );
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ } else {
+ granted = 1;
+ }
+
+ gss_release_buffer( &minor, &gss_value );
+ gss_release_buffer( &minor, &gss_display_value );
+
+ if ( granted ) {
+ break;
+ }
+ }
+
+ if ( granted ) {
+ ACL_LVL_ASSIGN_WRITE( *grant );
+ }
+
+ return 0;
+}
+
+static int
+gssattr_dynacl_destroy(
+ void *priv )
+{
+ gssattr_t *gssattr = (gssattr_t *)priv;
+
+ if ( gssattr != NULL ) {
+ if ( !BER_BVISNULL( &gssattr->gssattr_name ) ) {
+ ber_memfree( gssattr->gssattr_name.bv_val );
+ }
+ if ( !BER_BVISNULL( &gssattr->gssattr_value ) ) {
+ ber_memfree( gssattr->gssattr_value.bv_val );
+ }
+ ch_free( gssattr );
+ }
+
+ return 0;
+}
+
+static struct slap_dynacl_t gssattr_dynacl = {
+ "gss",
+ gssattr_dynacl_parse,
+ gssattr_dynacl_unparse,
+ gssattr_dynacl_mask,
+ gssattr_dynacl_destroy
+};
+
+int
+init_module( int argc, char *argv[] )
+{
+ return slap_dynacl_register( &gssattr_dynacl );
+}
+
+
+static int
+regex_matches(
+ struct berval *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ struct berval *dn_matches, /* buffer with $N expansion variables from DN */
+ struct berval *val_matches, /* buffer with $N expansion variables from val */
+ AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
+)
+{
+ regex_t re;
+ char newbuf[ACL_BUF_SIZE];
+ struct berval bv;
+ int rc;
+
+ bv.bv_len = sizeof( newbuf ) - 1;
+ bv.bv_val = newbuf;
+
+ if (str == NULL) {
+ str = "";
+ };
+
+ acl_string_expand( &bv, pat, dn_matches, val_matches, matches );
+ rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
+ if ( rc ) {
+ char error[ACL_BUF_SIZE];
+ regerror( rc, &re, error, sizeof( error ) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "compile( \"%s\", \"%s\") failed %s\n",
+ pat->bv_val, str, error );
+ return( 0 );
+ }
+
+ rc = regexec( &re, str, 0, NULL, 0 );
+ regfree( &re );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: string: %s\n", str );
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: rc: %d %s\n",
+ rc, !rc ? "matches" : "no matches" );
+ return( !rc );
+}
+
diff --git a/contrib/slapd-modules/acl/posixgroup.c b/contrib/slapd-modules/acl/posixgroup.c
new file mode 100644
index 0000000..9a9a5a8
--- /dev/null
+++ b/contrib/slapd-modules/acl/posixgroup.c
@@ -0,0 +1,329 @@
+/* posixgroup.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 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>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <slap.h>
+#include <lutil.h>
+
+/* Need dynacl... */
+
+#ifdef SLAP_DYNACL
+
+typedef struct pg_t {
+ slap_style_t pg_style;
+ struct berval pg_pat;
+} pg_t;
+
+static ObjectClass *pg_posixGroup;
+static AttributeDescription *pg_memberUid;
+static ObjectClass *pg_posixAccount;
+static AttributeDescription *pg_uidNumber;
+
+static int pg_dynacl_destroy( void *priv );
+
+static int
+pg_dynacl_parse(
+ const char *fname,
+ int lineno,
+ const char *opts,
+ slap_style_t style,
+ const char *pattern,
+ void **privp )
+{
+ pg_t *pg;
+ int rc;
+ const char *text = NULL;
+ struct berval pat;
+
+ ber_str2bv( pattern, 0, 0, &pat );
+
+ pg = ch_calloc( 1, sizeof( pg_t ) );
+
+ pg->pg_style = style;
+
+ switch ( pg->pg_style ) {
+ case ACL_STYLE_BASE:
+ rc = dnNormalize( 0, NULL, NULL, &pat, &pg->pg_pat, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to normalize DN \"%s\".\n",
+ fname, lineno, pattern );
+ goto cleanup;
+ }
+ break;
+
+ case ACL_STYLE_EXPAND:
+ ber_dupbv( &pg->pg_pat, &pat );
+ break;
+
+ default:
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unsupported style \"%s\".\n",
+ fname, lineno, style_strings[ pg->pg_style ] );
+ goto cleanup;
+ }
+
+ /* TODO: use opts to allow the use of different
+ * group objects and member attributes */
+ if ( pg_posixGroup == NULL ) {
+ pg_posixGroup = oc_find( "posixGroup" );
+ if ( pg_posixGroup == NULL ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"posixGroup\" "
+ "objectClass.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ pg_posixAccount = oc_find( "posixAccount" );
+ if ( pg_posixGroup == NULL ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"posixAccount\" "
+ "objectClass.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ rc = slap_str2ad( "memberUid", &pg_memberUid, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"memberUid\" "
+ "attributeDescription (%d: %s).\n",
+ fname, lineno, rc, text );
+ goto cleanup;
+ }
+
+ rc = slap_str2ad( "uidNumber", &pg_uidNumber, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"uidNumber\" "
+ "attributeDescription (%d: %s).\n",
+ fname, lineno, rc, text );
+ goto cleanup;
+ }
+ }
+
+ *privp = (void *)pg;
+ return 0;
+
+cleanup:
+ (void)pg_dynacl_destroy( (void *)pg );
+
+ return 1;
+}
+
+static int
+pg_dynacl_unparse(
+ void *priv,
+ struct berval *bv )
+{
+ pg_t *pg = (pg_t *)priv;
+ char *ptr;
+
+ bv->bv_len = STRLENOF( " dynacl/posixGroup.expand=" ) + pg->pg_pat.bv_len;
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+
+ ptr = lutil_strcopy( bv->bv_val, " dynacl/posixGroup" );
+
+ switch ( pg->pg_style ) {
+ case ACL_STYLE_BASE:
+ ptr = lutil_strcopy( ptr, ".exact=" );
+ break;
+
+ case ACL_STYLE_EXPAND:
+ ptr = lutil_strcopy( ptr, ".expand=" );
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ ptr = lutil_strncopy( ptr, pg->pg_pat.bv_val, pg->pg_pat.bv_len );
+ ptr[ 0 ] = '\0';
+
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+pg_dynacl_mask(
+ void *priv,
+ Operation *op,
+ Entry *target,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ pg_t *pg = (pg_t *)priv;
+ Entry *group = NULL,
+ *user = NULL;
+ int rc;
+ Backend *be = op->o_bd,
+ *group_be = NULL,
+ *user_be = NULL;
+ struct berval group_ndn;
+
+ ACL_INVALIDATE( *deny );
+
+ /* get user */
+ if ( target && dn_match( &target->e_nname, &op->o_ndn ) ) {
+ user = target;
+ rc = LDAP_SUCCESS;
+
+ } else {
+ user_be = op->o_bd = select_backend( &op->o_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = be;
+ return 0;
+ }
+ rc = be_entry_get_rw( op, &op->o_ndn, pg_posixAccount, pg_uidNumber, 0, &user );
+ }
+
+ if ( rc != LDAP_SUCCESS || user == NULL ) {
+ op->o_bd = be;
+ return 0;
+ }
+
+ /* get target */
+ if ( pg->pg_style == ACL_STYLE_EXPAND ) {
+ char buf[ 1024 ];
+ struct berval bv;
+ AclRegexMatches amatches = { 0 };
+
+ amatches.dn_count = nmatch;
+ AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ if ( acl_string_expand( &bv, &pg->pg_pat,
+ &target->e_nname,
+ NULL, &amatches ) )
+ {
+ goto cleanup;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bv, &group_ndn,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ /* did not expand to a valid dn */
+ goto cleanup;
+ }
+
+ } else {
+ group_ndn = pg->pg_pat;
+ }
+
+ if ( target && dn_match( &target->e_nname, &group_ndn ) ) {
+ group = target;
+ rc = LDAP_SUCCESS;
+
+ } else {
+ group_be = op->o_bd = select_backend( &group_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ goto cleanup;
+ }
+ rc = be_entry_get_rw( op, &group_ndn, pg_posixGroup, pg_memberUid, 0, &group );
+ }
+
+ if ( group_ndn.bv_val != pg->pg_pat.bv_val ) {
+ op->o_tmpfree( group_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( rc == LDAP_SUCCESS && group != NULL ) {
+ Attribute *a_uid,
+ *a_member;
+
+ a_uid = attr_find( user->e_attrs, pg_uidNumber );
+ if ( !a_uid || !BER_BVISNULL( &a_uid->a_nvals[ 1 ] ) ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ a_member = attr_find( group->e_attrs, pg_memberUid );
+ if ( !a_member ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ rc = value_find_ex( pg_memberUid,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a_member->a_nvals, &a_uid->a_nvals[ 0 ],
+ op->o_tmpmemctx );
+ }
+ }
+
+ } else {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+
+
+ if ( rc == LDAP_SUCCESS ) {
+ ACL_LVL_ASSIGN_WRITE( *grant );
+ }
+
+cleanup:;
+ if ( group != NULL && group != target ) {
+ op->o_bd = group_be;
+ be_entry_release_r( op, group );
+ op->o_bd = be;
+ }
+
+ if ( user != NULL && user != target ) {
+ op->o_bd = user_be;
+ be_entry_release_r( op, user );
+ op->o_bd = be;
+ }
+
+ return 0;
+}
+
+static int
+pg_dynacl_destroy(
+ void *priv )
+{
+ pg_t *pg = (pg_t *)priv;
+
+ if ( pg != NULL ) {
+ if ( !BER_BVISNULL( &pg->pg_pat ) ) {
+ ber_memfree( pg->pg_pat.bv_val );
+ }
+ ch_free( pg );
+ }
+
+ return 0;
+}
+
+static struct slap_dynacl_t pg_dynacl = {
+ "posixGroup",
+ pg_dynacl_parse,
+ pg_dynacl_unparse,
+ pg_dynacl_mask,
+ pg_dynacl_destroy
+};
+
+int
+init_module( int argc, char *argv[] )
+{
+ return slap_dynacl_register( &pg_dynacl );
+}
+
+#endif /* SLAP_DYNACL */
diff --git a/contrib/slapd-modules/addpartial/Makefile b/contrib/slapd-modules/addpartial/Makefile
new file mode 100644
index 0000000..ecb7cd0
--- /dev/null
+++ b/contrib/slapd-modules/addpartial/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = addpartial.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+addpartial.la: addpartial-overlay.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/addpartial/README b/contrib/slapd-modules/addpartial/README
new file mode 100644
index 0000000..968cdd8
--- /dev/null
+++ b/contrib/slapd-modules/addpartial/README
@@ -0,0 +1,72 @@
+addpartial Overlay README
+
+DESCRIPTION
+ This package contains an OpenLDAP overlay called "addpartial" that
+ intercepts add requests, determines if the entry exists, determines what
+ attributes, if any, have changed, and modifies those attributes. If the
+ entry does not exist, the add request falls through and proceeds normally.
+ If the entry exists but no changes have been detected, the client receives
+ LDAP_SUCCESS (I suppose it is debatable what to do in this case, but this is
+ the most clean for my use. The LDAP_SUCCESS lets me know that the entry I
+ sent slapd == the entry already in my slapd DB. Perhaps this behavior
+ should be configurable in the future).
+
+ When a change is found, the addpartial overlay will replace all values for
+ the attribute (if an attribute does not exist in the new entry but exists
+ in the entry in the slapd DB, a replace will be done with an empty list of
+ values).
+
+ Once a modify takes place, the syncprov overlay will properly process the
+ change, provided that addpartial is the first overlay to run. Please see
+ the CAVEATS for more specifics about this.
+
+ The addpartial overlay makes it easy to replicate full entries to a slapd
+ instance without worrying about the differences between entries or even if
+ the entry exists. Using ldapadd to add entries, the addpartial overlay can
+ compare about 500 records per second. The intent of the addpartial overlay
+ is to make it easy to replicate records from a source that is not an LDAP
+ instance, such as a database. The overlay is also useful in places where it
+ is easier to create full entries rather than comparing an entry with an
+ entry that must be retrieved (with ldapsearch or similar) from an existing
+ slapd DB to find changes.
+
+ The addpartial overlay has been used in production since August 2004 and has
+ processed millions of records without incident.
+
+BUILDING
+ A Makefile is included, please set your LDAP_SRC directory properly.
+
+INSTALLATION
+ After compiling the addpartial overlay, add the following to your
+ slapd.conf:
+
+ ### slapd.conf
+ ...
+ moduleload addpartial.so
+ ...
+ # after database directive...
+ # this overlay should be the last overlay in the config file to ensure that
+ # it properly intercepts the add request
+ overlay addpartial
+ ...
+ ### end slapd.conf
+
+CAVEATS
+ - In order to ensure that addpartial does what it needs to do, it should be
+ the last overlay configured so it will run before the other overlays.
+ This is especially important if you are using syncrepl, as the modify that
+ addpartial does will muck with the locking that takes place in the
+ syncprov overlay.
+
+---
+Copyright 2004-2022 The OpenLDAP Foundation.
+Portions Copyright (C) Virginia Tech, David Hawes.
+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 file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+http://www.OpenLDAP.org/license.html.
diff --git a/contrib/slapd-modules/addpartial/addpartial-overlay.c b/contrib/slapd-modules/addpartial/addpartial-overlay.c
new file mode 100644
index 0000000..b1d637b
--- /dev/null
+++ b/contrib/slapd-modules/addpartial/addpartial-overlay.c
@@ -0,0 +1,349 @@
+/* addpartial-overlay.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright (C) 2004 Virginia Tech, David Hawes.
+ * 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 file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * http://www.OpenLDAP.org/license.html.
+ */
+/* ACKNOLEDGEDMENTS:
+ * This work was initially developed by David Hawes of Virginia Tech
+ * for inclusion in OpenLDAP Software.
+ */
+/* addpartial-overlay
+ *
+ * This is an OpenLDAP overlay that intercepts ADD requests, determines if a
+ * change has actually taken place for that record, and then performs a modify
+ * request for those values that have changed (modified, added, deleted). If
+ * the record has not changed in any way, it is ignored. If the record does not
+ * exist, the record falls through to the normal add mechanism. This overlay is
+ * useful for replicating from sources that are not LDAPs where it is easier to
+ * build entire records than to determine the changes (i.e. a database).
+ */
+
+#include "portable.h"
+#include "slap.h"
+
+static int collect_error_msg_cb( Operation *op, SlapReply *rs);
+
+static slap_overinst addpartial;
+
+/**
+ * The meat of the overlay. Search for the record, determine changes, take
+ * action or fall through.
+ */
+static int addpartial_add( Operation *op, SlapReply *rs)
+{
+ Operation nop = *op;
+ Entry *toAdd = NULL;
+ Entry *found = NULL;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ toAdd = op->oq_add.rs_e;
+
+ Debug(LDAP_DEBUG_TRACE, "%s: toAdd->e_nname.bv_val: %s\n",
+ addpartial.on_bi.bi_type, toAdd->e_nname.bv_val );
+
+ /* if the user doesn't have access, fall through to the normal ADD */
+ if(!access_allowed(op, toAdd, slap_schema.si_ad_entry,
+ NULL, ACL_WRITE, NULL))
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = overlay_entry_get_ov(&nop, &nop.o_req_ndn, NULL, NULL, 0, &found, on);
+
+ if(rc != LDAP_SUCCESS)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: no entry found, falling through to normal add\n",
+ addpartial.on_bi.bi_type );
+ return SLAP_CB_CONTINUE;
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: found the dn\n", addpartial.on_bi.bi_type );
+
+ if(found)
+ {
+ Attribute *attr = NULL;
+ Attribute *at = NULL;
+ int ret;
+ Modifications *mods = NULL;
+ Modifications **modtail = &mods;
+ Modifications *mod = NULL;
+
+ Debug(LDAP_DEBUG_TRACE, "%s: have an entry!\n",
+ addpartial.on_bi.bi_type );
+
+ /* determine if the changes are in the found entry */
+ for(attr = toAdd->e_attrs; attr; attr = attr->a_next)
+ {
+ if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue;
+
+ at = attr_find(found->e_attrs, attr->a_desc);
+ if(!at)
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s not found!\n",
+ addpartial.on_bi.bi_type,
+ attr->a_desc->ad_cname.bv_val );
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ mod->sml_op &= LDAP_MOD_OP;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = attr->a_vals;
+ mod->sml_nvalues = attr->a_nvals;
+ mod->sml_numvals = attr->a_numvals;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ else
+ {
+ MatchingRule *mr = attr->a_desc->ad_type->sat_equality;
+ struct berval *bv;
+ const char *text;
+ int acount , bcount;
+ Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s found\n",
+ addpartial.on_bi.bi_type,
+ attr->a_desc->ad_cname.bv_val );
+
+ for(bv = attr->a_vals, acount = 0; bv->bv_val != NULL;
+ bv++, acount++)
+ {
+ /* count num values for attr */
+ }
+ for(bv = at->a_vals, bcount = 0; bv->bv_val != NULL;
+ bv++, bcount++)
+ {
+ /* count num values for attr */
+ }
+ if(acount != bcount)
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: acount != bcount, %s\n",
+ addpartial.on_bi.bi_type,
+ "replace all" );
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ mod->sml_op &= LDAP_MOD_OP;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = attr->a_vals;
+ mod->sml_nvalues = attr->a_nvals;
+ mod->sml_numvals = attr->a_numvals;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ continue;
+ }
+
+ for(bv = attr->a_vals; bv->bv_val != NULL; bv++)
+ {
+ struct berval *v;
+ ret = -1;
+
+ for(v = at->a_vals; v->bv_val != NULL; v++)
+ {
+ int r;
+ if(mr && ((r = value_match(&ret, attr->a_desc, mr,
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ bv, v, &text)) == 0))
+ {
+ if(ret == 0)
+ break;
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: \tvalue DNE, r: %d \n",
+ addpartial.on_bi.bi_type,
+ r );
+ ret = strcmp(bv->bv_val, v->bv_val);
+ if(ret == 0)
+ break;
+ }
+ }
+
+ if(ret == 0)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: \tvalue %s exists, ret: %d\n",
+ addpartial.on_bi.bi_type, bv->bv_val, ret);
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: \tvalue %s DNE, ret: %d\n",
+ addpartial.on_bi.bi_type, bv->bv_val, ret);
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ mod->sml_op &= LDAP_MOD_OP;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = attr->a_vals;
+ mod->sml_nvalues = attr->a_nvals;
+ mod->sml_numvals = attr->a_numvals;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ break;
+ }
+ }
+ }
+ }
+
+ /* determine if any attributes were deleted */
+ for(attr = found->e_attrs; attr; attr = attr->a_next)
+ {
+ if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue;
+
+ at = NULL;
+ at = attr_find(toAdd->e_attrs, attr->a_desc);
+ if(!at)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: Attribute %s not found in new entry!!!\n",
+ addpartial.on_bi.bi_type,
+ attr->a_desc->ad_cname.bv_val );
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 0;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: Attribute %s found in new entry\n",
+ addpartial.on_bi.bi_type,
+ at->a_desc->ad_cname.bv_val );
+ }
+ }
+
+ overlay_entry_release_ov(&nop, found, 0, on);
+
+ if(mods)
+ {
+ Modifications *m = NULL;
+ Modifications *toDel;
+ int modcount;
+ slap_callback nullcb = { NULL, collect_error_msg_cb,
+ NULL, NULL };
+
+ Debug(LDAP_DEBUG_TRACE, "%s: mods to do...\n",
+ addpartial.on_bi.bi_type );
+
+ nop.o_tag = LDAP_REQ_MODIFY;
+ nop.orm_modlist = mods;
+ nop.orm_no_opattrs = 0;
+ nop.o_callback = &nullcb;
+ nop.o_bd->bd_info = (BackendInfo *) on->on_info;
+
+ for(m = mods, modcount = 0; m; m = m->sml_next,
+ modcount++)
+ {
+ /* count number of mods */
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "%s: number of mods: %d\n",
+ addpartial.on_bi.bi_type, modcount );
+
+ if(nop.o_bd->be_modify)
+ {
+ SlapReply nrs = { REP_RESULT };
+ rc = (nop.o_bd->be_modify)(&nop, &nrs);
+ }
+
+ if(rc == LDAP_SUCCESS)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: modify successful\n",
+ addpartial.on_bi.bi_type );
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: modify unsuccessful: %d\n",
+ addpartial.on_bi.bi_type, rc );
+ rs->sr_err = rc;
+ if(nullcb.sc_private)
+ {
+ rs->sr_text = nullcb.sc_private;
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "%s: freeing mods...\n",
+ addpartial.on_bi.bi_type );
+
+ for(toDel = mods; toDel; toDel = mods)
+ {
+ mods = mods->sml_next;
+ ch_free(toDel);
+ }
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: no mods to process\n",
+ addpartial.on_bi.bi_type );
+ }
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: no entry!\n",
+ addpartial.on_bi.bi_type );
+ }
+
+ op->o_callback = NULL;
+ send_ldap_result( op, rs );
+ ch_free((void *)rs->sr_text);
+ rs->sr_text = NULL;
+
+ return LDAP_SUCCESS;
+ }
+}
+
+static int collect_error_msg_cb( Operation *op, SlapReply *rs)
+{
+ if(rs->sr_text)
+ {
+ op->o_callback->sc_private = (void *) ch_strdup(rs->sr_text);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int addpartial_init()
+{
+ addpartial.on_bi.bi_type = "addpartial";
+ addpartial.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ addpartial.on_bi.bi_op_add = addpartial_add;
+
+ return (overlay_register(&addpartial));
+}
+
+int init_module(int argc, char *argv[])
+{
+ return addpartial_init();
+}
diff --git a/contrib/slapd-modules/adremap/Makefile b/contrib/slapd-modules/adremap/Makefile
new file mode 100644
index 0000000..b008eab
--- /dev/null
+++ b/contrib/slapd-modules/adremap/Makefile
@@ -0,0 +1,68 @@
+# $OpenLDAP$
+# Copyright 2015 Howard Chu <hyc@symas.com>
+# 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_ADREMAP=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = adremap.la
+MANPAGES = slapo-adremap.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+adremap.la: adremap.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/adremap/adremap.c b/contrib/slapd-modules/adremap/adremap.c
new file mode 100644
index 0000000..5a7b8f1
--- /dev/null
+++ b/contrib/slapd-modules/adremap/adremap.c
@@ -0,0 +1,652 @@
+/* adremap.c - Case-folding and DN-value remapping for AD proxies */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2015 Howard Chu <hyc@symas.com>.
+ * 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>.
+ */
+
+#include "portable.h"
+
+/*
+ * This file implements an overlay that performs two remapping functions
+ * to allow older POSIX clients to use Microsoft AD:
+ * 1: downcase the values of a configurable list of attributes
+ * 2: dereference some DN-valued attributes and convert to their simple names
+ * e.g. generate memberUid based on member
+ */
+
+#ifdef SLAPD_OVER_ADREMAP
+
+#include <ldap.h>
+#include "lutil.h"
+#include "slap.h"
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "slap-config.h"
+
+typedef struct adremap_dnv {
+ struct adremap_dnv *ad_next;
+ AttributeDescription *ad_dnattr; /* DN-valued attr to deref */
+ AttributeDescription *ad_deref; /* target attr's value to retrieve */
+ AttributeDescription *ad_newattr; /* New attr to collect new values */
+ ObjectClass *ad_group; /* group objectclass on target */
+ ObjectClass *ad_mapgrp; /* group objectclass to map */
+ ObjectClass *ad_refgrp; /* objectclass of target DN */
+ struct berval ad_refbase; /* base DN of target entries */
+} adremap_dnv;
+/* example: member uid memberUid */
+
+typedef struct adremap_case {
+ struct adremap_case *ac_next;
+ AttributeDescription *ac_attr;
+} adremap_case;
+
+/* Per-instance configuration information */
+typedef struct adremap_info {
+ adremap_case *ai_case; /* attrs to downcase */
+ adremap_dnv *ai_dnv; /* DN attrs to remap */
+} adremap_info;
+
+enum {
+ ADREMAP_CASE = 1,
+ ADREMAP_DNV
+};
+
+static ConfigDriver adremap_cf_case;
+static ConfigDriver adremap_cf_dnv;
+
+/* configuration attribute and objectclass */
+static ConfigTable adremapcfg[] = {
+ { "adremap-downcase", "attrs", 2, 0, 0,
+ ARG_MAGIC|ADREMAP_CASE, adremap_cf_case,
+ "( OLcfgCtAt:6.1 "
+ "NAME 'olcADremapDowncase' "
+ "DESC 'List of attributes to casefold to lower case' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "adremap-dnmap", "dnattr targetattr newattr remoteOC localOC targetOC baseDN", 8, 8, 0,
+ ARG_MAGIC|ADREMAP_DNV, adremap_cf_dnv,
+ "( OLcfgCtAt:6.2 "
+ "NAME 'olcADremapDNmap' "
+ "DESC 'DN attr to map, attr from target to use, attr to generate, objectclass of remote"
+ " group, objectclass mapped group, objectclass of target entry, base DN of target entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs adremapocs[] = {
+ { "( OLcfgCtOc:6.1 "
+ "NAME 'olcADremapConfig' "
+ "DESC 'AD remap configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcADremapDowncase $ olcADremapDNmap ) )",
+ Cft_Overlay, adremapcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static int
+adremap_cf_case(ConfigArgs *c)
+{
+ BackendDB *be = (BackendDB *)c->be;
+ slap_overinst *on = (slap_overinst *)c->bi;
+ adremap_info *ai = on->on_bi.bi_private;
+ adremap_case *ac, **a2;
+ int rc = ARG_BAD_CONF;
+
+ switch(c->op) {
+ case SLAP_CONFIG_EMIT:
+ for (ac = ai->ai_case; ac; ac=ac->ac_next) {
+ rc = value_add_one(&c->rvalue_vals, &ac->ac_attr->ad_cname);
+ if (rc) break;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if (c->valx < 0) {
+ for (ac = ai->ai_case; ac; ac=ai->ai_case) {
+ ai->ai_case = ac->ac_next;
+ ch_free(ac);
+ }
+ } else {
+ int i;
+ for (i=0, a2 = &ai->ai_case; i<c->valx; i++, a2 = &(*a2)->ac_next);
+ ac = *a2;
+ *a2 = ac->ac_next;
+ ch_free(ac);
+ }
+ rc = 0;
+ break;
+ default: {
+ const char *text;
+ adremap_case ad;
+ ad.ac_attr = NULL;
+ rc = slap_str2ad(c->argv[1], &ad.ac_attr, &text);
+ if (rc) break;
+ for (a2 = &ai->ai_case; *a2; a2 = &(*a2)->ac_next);
+ ac = ch_malloc(sizeof(adremap_case));
+ ac->ac_next = NULL;
+ ac->ac_attr = ad.ac_attr;
+ *a2 = ac;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int
+adremap_cf_dnv(ConfigArgs *c)
+{
+ BackendDB *be = (BackendDB *)c->be;
+ slap_overinst *on = (slap_overinst *)c->bi;
+ adremap_info *ai = on->on_bi.bi_private;
+ adremap_dnv *ad, **a2;
+ int rc = ARG_BAD_CONF;
+
+ switch(c->op) {
+ case SLAP_CONFIG_EMIT:
+ for (ad = ai->ai_dnv; ad; ad=ad->ad_next) {
+ char *ptr;
+ struct berval bv;
+ bv.bv_len = ad->ad_dnattr->ad_cname.bv_len + ad->ad_deref->ad_cname.bv_len + ad->ad_newattr->ad_cname.bv_len + 2;
+ bv.bv_len += ad->ad_group->soc_cname.bv_len + ad->ad_mapgrp->soc_cname.bv_len + ad->ad_refgrp->soc_cname.bv_len + 3;
+ bv.bv_len += ad->ad_refbase.bv_len + 3;
+ bv.bv_val = ch_malloc(bv.bv_len + 1);
+ ptr = lutil_strcopy(bv.bv_val, ad->ad_dnattr->ad_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_deref->ad_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_newattr->ad_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_group->soc_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_mapgrp->soc_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_refgrp->soc_cname.bv_val);
+ *ptr++ = ' ';
+ *ptr++ = '"';
+ ptr = lutil_strcopy(ptr, ad->ad_refbase.bv_val);
+ *ptr++ = '"';
+ *ptr = '\0';
+ ber_bvarray_add(&c->rvalue_vals, &bv);
+ }
+ if (ai->ai_dnv) rc = 0;
+ break;
+ case LDAP_MOD_DELETE:
+ if (c->valx < 0) {
+ for (ad = ai->ai_dnv; ad; ad=ai->ai_dnv) {
+ ai->ai_dnv = ad->ad_next;
+ ch_free(ad);
+ }
+ } else {
+ int i;
+ for (i=0, a2 = &ai->ai_dnv; i<c->valx; i++, a2 = &(*a2)->ad_next);
+ ad = *a2;
+ *a2 = ad->ad_next;
+ ch_free(ad);
+ }
+ rc = 0;
+ break;
+ default: {
+ const char *text;
+ adremap_dnv av = {0};
+ struct berval dn;
+ rc = slap_str2ad(c->argv[1], &av.ad_dnattr, &text);
+ if (rc) break;
+ if (av.ad_dnattr->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName) {
+ rc = 1;
+ snprintf(c->cr_msg, sizeof(c->cr_msg), "<%s> not a DN-valued attribute",
+ c->argv[0]);
+ Debug(LDAP_DEBUG_ANY, "%s: %s(%s)\n", c->log, c->cr_msg, c->argv[1]);
+ break;
+ }
+ rc = slap_str2ad(c->argv[2], &av.ad_deref, &text);
+ if (rc) break;
+ rc = slap_str2ad(c->argv[3], &av.ad_newattr, &text);
+ if (rc) break;
+ av.ad_group = oc_find(c->argv[4]);
+ if (!av.ad_group) {
+ rc = 1;
+ break;
+ }
+ av.ad_mapgrp = oc_find(c->argv[5]);
+ if (!av.ad_mapgrp) {
+ rc = 1;
+ break;
+ }
+ av.ad_refgrp = oc_find(c->argv[6]);
+ if (!av.ad_refgrp) {
+ rc = 1;
+ break;
+ }
+ ber_str2bv(c->argv[7], 0, 0, &dn);
+ rc = dnNormalize(0, NULL, NULL, &dn, &av.ad_refbase, NULL);
+ if (rc) break;
+
+ for (a2 = &ai->ai_dnv; *a2; a2 = &(*a2)->ad_next);
+ ad = ch_malloc(sizeof(adremap_dnv));
+ ad->ad_next = NULL;
+ ad->ad_dnattr = av.ad_dnattr;
+ ad->ad_deref = av.ad_deref;
+ ad->ad_newattr = av.ad_newattr;
+ ad->ad_group = av.ad_group;
+ ad->ad_mapgrp = av.ad_mapgrp;
+ ad->ad_refgrp = av.ad_refgrp;
+ ad->ad_refbase = av.ad_refbase;
+ *a2 = ad;
+ break;
+ }
+ }
+ return rc;
+}
+
+typedef struct adremap_ctx {
+ slap_overinst *on;
+ AttributeName an;
+ AttributeDescription *ad;
+ int an_swap;
+} adremap_ctx;
+
+static int
+adremap_search_resp(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ adremap_ctx *ctx = op->o_callback->sc_private;
+ slap_overinst *on = ctx->on;
+ adremap_info *ai = on->on_bi.bi_private;
+ adremap_case *ac;
+ adremap_dnv *ad;
+ Attribute *a;
+ Entry *e;
+
+ if (rs->sr_type != REP_SEARCH)
+ return SLAP_CB_CONTINUE;
+
+ /* we munged the attr list, restore it to original */
+ if (ctx->an_swap) {
+ int i;
+ ctx->an_swap = 0;
+ for (i=0; rs->sr_attrs[i].an_name.bv_val; i++) {
+ if (rs->sr_attrs[i].an_desc == ctx->ad) {
+ rs->sr_attrs[i] = ctx->an;
+ break;
+ }
+ }
+ /* Usually rs->sr_attrs is just op->ors_attrs, but
+ * overlays like rwm may make a new copy. Fix both
+ * if needed.
+ */
+ if (op->ors_attrs != rs->sr_attrs) {
+ for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
+ if (op->ors_attrs[i].an_desc == ctx->ad) {
+ op->ors_attrs[i] = ctx->an;
+ break;
+ }
+ }
+ }
+ }
+ e = rs->sr_entry;
+ for (ac = ai->ai_case; ac; ac = ac->ac_next) {
+ a = attr_find(e->e_attrs, ac->ac_attr);
+ if (a) {
+ int i, j;
+ if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
+ e = entry_dup(e);
+ rs_replace_entry(op, rs, on, e);
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+ a = attr_find(e->e_attrs, ac->ac_attr);
+ }
+ for (i=0; i<a->a_numvals; i++) {
+ unsigned char *c = a->a_vals[i].bv_val;
+ for (j=0; j<a->a_vals[i].bv_len; j++)
+ if (isupper(c[j]))
+ c[j] = tolower(c[j]);
+ }
+ }
+ }
+ for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
+ a = attr_find(e->e_attrs, ad->ad_dnattr);
+ if (a) {
+ Entry *n;
+ Attribute *dr;
+ int i, rc;
+ if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
+ e = entry_dup(e);
+ rs_replace_entry(op, rs, on, e);
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+ a = attr_find(e->e_attrs, ad->ad_dnattr);
+ }
+ for (i=0; i<a->a_numvals; i++) {
+ struct berval dv;
+ dv = ad->ad_deref->ad_cname;
+ /* If the RDN uses the deref attr, just use it directly */
+ if (a->a_nvals[i].bv_val[dv.bv_len] == '=' &&
+ !memcmp(a->a_nvals[i].bv_val, dv.bv_val, dv.bv_len)) {
+ struct berval bv, nv;
+ char *ptr;
+ bv = a->a_vals[i];
+ nv = a->a_nvals[i];
+ bv.bv_val += dv.bv_len + 1;
+ ptr = strchr(bv.bv_val, ',');
+ if (ptr)
+ bv.bv_len = ptr - bv.bv_val;
+ else
+ bv.bv_len -= dv.bv_len+1;
+ nv.bv_val += dv.bv_len + 1;
+ ptr = strchr(nv.bv_val, ',');
+ if (ptr)
+ nv.bv_len = ptr - nv.bv_val;
+ else
+ nv.bv_len -= dv.bv_len+1;
+ attr_merge_one(e, ad->ad_newattr, &bv, &nv);
+ } else {
+ /* otherwise look up the deref attr */
+ n = NULL;
+ rc = be_entry_get_rw(op, &a->a_nvals[i], NULL, ad->ad_deref, 0, &n);
+ if (!rc && n) {
+ dr = attr_find(n->e_attrs, ad->ad_deref);
+ if (dr)
+ attr_merge_one(e, ad->ad_newattr, dr->a_vals, dr->a_nvals);
+ be_entry_release_r(op, n);
+ }
+ }
+ }
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int adremap_refsearch(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ if (rs->sr_type == REP_SEARCH) {
+ slap_callback *sc = op->o_callback;
+ struct berval *dn = sc->sc_private;
+ ber_dupbv_x(dn, &rs->sr_entry->e_nname, op->o_tmpmemctx);
+ return LDAP_SUCCESS;
+ }
+ return rs->sr_err;
+}
+
+static adremap_dnv *adremap_filter(
+ Operation *op,
+ adremap_info *ai
+)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Filter *f = op->ors_filter, *fn = NULL;
+ adremap_dnv *ad = NULL;
+ struct berval bv;
+ int fextra = 0;
+
+ /* Do we need to munge the filter? First see if it's of
+ * the form (objectClass=<mapgrp>)
+ * or form (&(objectClass=<mapgrp>)...)
+ * or form (&(&(objectClass=<mapgrp>)...)...)
+ */
+ if (f->f_choice == LDAP_FILTER_AND && f->f_and) {
+ fextra = 1;
+ f = f->f_and;
+ fn = f->f_next;
+ }
+ if (f->f_choice == LDAP_FILTER_AND && f->f_and) {
+ fextra = 2;
+ f = f->f_and;
+ }
+ if (f->f_choice == LDAP_FILTER_EQUALITY &&
+ f->f_av_desc == slap_schema.si_ad_objectClass) {
+ struct berval bv = f->f_av_value;
+
+ for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
+ if (!ber_bvstrcasecmp( &bv, &ad->ad_mapgrp->soc_cname )) {
+ /* Now check to see if next element is (<newattr>=foo) */
+ Filter *fnew;
+ if (fn && fn->f_choice == LDAP_FILTER_EQUALITY &&
+ fn->f_av_desc == ad->ad_newattr) {
+ Filter fr[3];
+ AttributeAssertion aa[2] = {0};
+ Operation op2;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ struct berval dn = BER_BVNULL;
+
+ /* It's a match, setup a search with filter
+ * (&(objectclass=<refgrp>)(<deref>=foo))
+ */
+ fr[0].f_choice = LDAP_FILTER_AND;
+ fr[0].f_and = &fr[1];
+ fr[0].f_next = NULL;
+
+ fr[1].f_choice = LDAP_FILTER_EQUALITY;
+ fr[1].f_ava = &aa[0];
+ fr[1].f_av_desc = slap_schema.si_ad_objectClass;
+ fr[1].f_av_value = ad->ad_refgrp->soc_cname;
+ fr[1].f_next = &fr[2];
+
+ fr[2].f_choice = LDAP_FILTER_EQUALITY;
+ fr[2].f_ava = &aa[1];
+ fr[2].f_av_desc = ad->ad_deref;
+ fr[2].f_av_value = fn->f_av_value;
+ fr[2].f_next = NULL;
+
+ /* Search with this filter to retrieve target DN */
+ op2 = *op;
+ op2.o_callback = &cb;
+ cb.sc_response = adremap_refsearch;
+ cb.sc_private = &dn;
+ op2.o_req_dn = ad->ad_refbase;
+ op2.o_req_ndn = ad->ad_refbase;
+ op2.ors_filter = fr;
+ filter2bv_x(op, fr, &op2.ors_filterstr);
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_attrsonly = 1;
+ op2.o_no_schema_check = 1;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ op2.o_bd->be_search(&op2, &rs);
+ op2.o_bd->bd_info = (BackendInfo *)on;
+ op->o_tmpfree(op2.ors_filterstr.bv_val, op->o_tmpmemctx);
+
+ if (!dn.bv_len) { /* no match was found */
+ ad = NULL;
+ break;
+ }
+
+ if (rs.sr_err) { /* sizelimit exceeded, etc.: invalid name */
+ op->o_tmpfree(dn.bv_val, op->o_tmpmemctx);
+ ad = NULL;
+ break;
+ }
+
+ /* Build a new filter of form
+ * (&(objectclass=<group>)(<dnattr>=foo-DN)...)
+ */
+ f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f->f_choice = LDAP_FILTER_AND;
+ fnew = f;
+ f->f_next = NULL;
+
+ f->f_and = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f = f->f_and;
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
+ f->f_av_desc = slap_schema.si_ad_objectClass;
+ ber_dupbv_x(&f->f_av_value, &ad->ad_group->soc_cname, op->o_tmpmemctx);
+
+ f->f_next = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f = f->f_next;
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
+ f->f_av_desc = ad->ad_dnattr;
+ f->f_av_value = dn;
+
+ f->f_next = fn->f_next;
+ fn->f_next = NULL;
+ } else {
+ /* Build a new filter of form
+ * (objectclass=<group>)
+ */
+ f->f_next = NULL; /* disconnect old chain */
+
+ f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
+ f->f_av_desc = slap_schema.si_ad_objectClass;
+ ber_dupbv_x(&f->f_av_value, &ad->ad_group->soc_cname, op->o_tmpmemctx);
+
+ /* If there was a wrapping (&), attach it. */
+ if (fextra) {
+ fnew = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ fnew->f_choice = LDAP_FILTER_AND;
+ fnew->f_and = f;
+ fnew->f_next = NULL;
+ f->f_next = fn;
+ } else {
+ fnew = f;
+ f->f_next = NULL;
+ }
+ }
+ if (fextra > 1) {
+ f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f->f_choice = LDAP_FILTER_AND;
+ f->f_and = fnew->f_and;
+ f->f_next = f->f_and->f_next;
+ f->f_and->f_next = op->ors_filter->f_and->f_and->f_next;
+ op->ors_filter->f_and->f_and->f_next = NULL;
+ fnew->f_and = f;
+ }
+ filter_free_x(op, op->ors_filter, 1);
+ op->o_tmpfree(op->ors_filterstr.bv_val, op->o_tmpmemctx);
+ op->ors_filter = fnew;
+ filter2bv_x(op, op->ors_filter, &op->ors_filterstr);
+ break;
+ }
+ }
+ }
+ return ad;
+}
+
+static int
+adremap_search(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
+ adremap_ctx *ctx;
+ adremap_dnv *ad = NULL;
+ slap_callback *cb;
+
+ /* Is this our own internal search? Ignore it */
+ if (op->o_no_schema_check)
+ return SLAP_CB_CONTINUE;
+
+ if (ai->ai_dnv)
+ /* check for filter match, fallthru if none */
+ ad = adremap_filter(op, ai);
+
+ cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(adremap_ctx), op->o_tmpmemctx);
+ cb->sc_response = adremap_search_resp;
+ cb->sc_private = cb+1;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ ctx = cb->sc_private;
+ ctx->on = on;
+ if (ad && op->ors_attrs) { /* see if we need to remap a search attr */
+ int i;
+ for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
+ if (op->ors_attrs[i].an_desc == ad->ad_newattr) {
+ ctx->an_swap = 1;
+ ctx->ad = ad->ad_dnattr;
+ ctx->an = op->ors_attrs[i];
+ op->ors_attrs[i].an_desc = ad->ad_dnattr;
+ op->ors_attrs[i].an_name = ad->ad_dnattr->ad_cname;
+ break;
+ }
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+adremap_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ /* initialize private structure to store configuration */
+ on->on_bi.bi_private = ch_calloc( 1, sizeof(adremap_info) );
+
+ return 0;
+}
+
+static int
+adremap_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
+ adremap_case *ac;
+ adremap_dnv *ad;
+
+ /* free config */
+ for (ac = ai->ai_case; ac; ac = ai->ai_case) {
+ ai->ai_case = ac->ac_next;
+ ch_free(ac);
+ }
+ for (ad = ai->ai_dnv; ad; ad = ai->ai_dnv) {
+ ai->ai_dnv = ad->ad_next;
+ ch_free(ad);
+ }
+ free( ai );
+
+ return 0;
+}
+
+static slap_overinst adremap;
+
+int adremap_initialize()
+{
+ int i, code;
+
+ adremap.on_bi.bi_type = "adremap";
+ adremap.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ adremap.on_bi.bi_db_init = adremap_db_init;
+ adremap.on_bi.bi_db_destroy = adremap_db_destroy;
+ adremap.on_bi.bi_op_search = adremap_search;
+
+ /* register configuration directives */
+ adremap.on_bi.bi_cf_ocs = adremapocs;
+ code = config_register_schema( adremapcfg, adremapocs );
+ if ( code ) return code;
+
+ return overlay_register( &adremap );
+}
+
+#if SLAPD_OVER_ADREMAP == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return adremap_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_ADREMAP) */
diff --git a/contrib/slapd-modules/adremap/slapo-adremap.5 b/contrib/slapd-modules/adremap/slapo-adremap.5
new file mode 100644
index 0000000..8b1fa45
--- /dev/null
+++ b/contrib/slapd-modules/adremap/slapo-adremap.5
@@ -0,0 +1,104 @@
+.TH SLAPO-ADREMAP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015 Howard Chu, All Rights Reserved.
+.\" $OpenLDAP$
+.SH NAME
+slapo-adremap \- AD Remap overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B adremap
+overlay to
+.BR slapd (8)
+remaps some attribute values for compatibility between Microsoft AD
+and older POSIX systems' PAM/NSS clients. It can be configured to
+convert values of given attributes to lower case, and it can be
+configured to generate RFC2307-compliant group memberships based
+on RFC2307bis groups. All mapping is only performed on entries
+returned as search responses.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B adremap
+overlay must be prefixed by
+.BR adremap\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay adremap
+This directive adds the
+.B adremap
+overlay to the current database, see
+.BR slapd.conf (5)
+for details.
+
+.LP
+These
+.B slapd.conf
+configuration options are defined for the adremap overlay. They must
+appear after the
+.B overlay
+directive. They can each be specified multiple times:
+.TP
+.B adremap-downcase <attr>
+Specify an attributeType whose values will all be mapped to lowercase
+when returned in search responses.
+.TP
+.B adremap-dnmap <dnattr> <targetattr> <newattr> <remoteOC> <localOC> <targetOC> <baseDN>
+Specify a DN-valued attributeType whose values will be dereferenced. The
+.B <targetattr>
+of the target entry will be retrieved and its value will be added to the
+.B <newattr>
+in the entry. In addition, searches using a filter of the form
+.B (&(objectClass=<localOC>)(<newattr>=xxx))
+will be rewritten into the form
+.BR (&(objectClass=<remoteOC>)(<dnattr>=xxx-DN)) .
+This rewrite will accomplished by performing an additional internal search,
+with subtree scope, using the specified baseDN and a filter of the form
+.BR (&(objectClass=<targetOC>)(<targetattr>=xxx)) .
+
+
+.SH EXAMPLE
+This example configures the
+.B adremap
+overlay to map all
+.B uid
+attributes to lowercase, and create
+.B memberUid
+values for group entries. The mapping will turn requests for posixGroup
+entries into requests for groupOfNames entries, and the internal search
+will use inetOrgPerson entries under the ou=People,dc=example,dc=com subtree.
+
+Add the following to
+.BR slapd.conf (5):
+
+.LP
+.nf
+ database <database>
+ # ...
+
+ overlay adremap
+ adremap-downcase uid
+ adremap-dnmap member uid memberUid groupOfNames posixGroup inetOrgPerson ou=people,dc=example,dc=com
+.fi
+.LP
+.B slapd
+must also load
+.B adremap.la,
+if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-adremap (5)
+overlay supports dynamic configuration via
+.BR back-config.
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2015 by Howard Chu.
diff --git a/contrib/slapd-modules/allop/Makefile b/contrib/slapd-modules/allop/Makefile
new file mode 100644
index 0000000..053f377
--- /dev/null
+++ b/contrib/slapd-modules/allop/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = allop.la
+MANPAGES = slapo-allop.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+allop.la: allop.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/allop/README b/contrib/slapd-modules/allop/README
new file mode 100644
index 0000000..3768e6a
--- /dev/null
+++ b/contrib/slapd-modules/allop/README
@@ -0,0 +1,26 @@
+This directory contains a slapd overlay, allop.
+The intended usage is as a global overlay for use with those clients
+that do not make use of the RFC3673 allOp ("+") in the requested
+attribute list, but expect all operational attributes to be returned.
+Usage: add to slapd.conf(5)
+
+moduleload path/to/allop.so
+
+overlay allop
+allop-URI <ldapURI>
+
+if the allop-URI is not given, the rootDSE, i.e. "ldap:///??base",
+is assumed.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o allop.so allop.c
+
+---
+Copyright 2004-2022 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.
+
diff --git a/contrib/slapd-modules/allop/allop.c b/contrib/slapd-modules/allop/allop.c
new file mode 100644
index 0000000..52fab3a
--- /dev/null
+++ b/contrib/slapd-modules/allop/allop.c
@@ -0,0 +1,262 @@
+/* allop.c - returns all operational attributes when appropriate */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * The intended usage is as a global overlay for use with those clients
+ * that do not make use of the RFC3673 allOp ("+") in the requested
+ * attribute list, but expect all operational attributes to be returned.
+ * Usage: add
+ *
+
+overlay allop
+allop-URI <ldapURI>
+
+ *
+ * if the allop-URI is not given, the rootDSE, i.e. "ldap:///??base",
+ * is assumed.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+#define SLAP_OVER_VERSION_REQUIRE(major,minor,patch) \
+ ( \
+ ( LDAP_VENDOR_VERSION_MAJOR == X || LDAP_VENDOR_VERSION_MAJOR >= (major) ) \
+ && ( LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR >= (minor) ) \
+ && ( LDAP_VENDOR_VERSION_PATCH == X || LDAP_VENDOR_VERSION_PATCH >= (patch) ) \
+ )
+
+#if !SLAP_OVER_VERSION_REQUIRE(2,3,0)
+#error "version mismatch"
+#endif
+
+typedef struct allop_t {
+ struct berval ao_ndn;
+ int ao_scope;
+} allop_t;
+
+static int
+allop_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ allop_t *ao = (allop_t *)on->on_bi.bi_private;
+
+ if ( strcasecmp( argv[ 0 ], "allop-uri" ) == 0 ) {
+ LDAPURLDesc *lud;
+ struct berval dn,
+ ndn;
+ int scope,
+ rc = LDAP_SUCCESS;
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "%s line %d: "
+ "need exactly 1 arg "
+ "in \"allop-uri <ldapURI>\" "
+ "directive.\n",
+ fname, lineno );
+ return 1;
+ }
+
+ if ( ldap_url_parse( argv[ 1 ], &lud ) != LDAP_URL_SUCCESS ) {
+ return -1;
+ }
+
+ scope = lud->lud_scope;
+ if ( scope == LDAP_SCOPE_DEFAULT ) {
+ scope = LDAP_SCOPE_BASE;
+ }
+
+ if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
+ if ( scope == LDAP_SCOPE_BASE ) {
+ BER_BVZERO( &ndn );
+
+ } else {
+ ber_str2bv( "", 0, 1, &ndn );
+ }
+
+ } else {
+
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ }
+
+ ldap_free_urldesc( lud );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* rootDSE */
+ if ( ao != NULL ) {
+ ch_free( ao->ao_ndn.bv_val );
+ ch_free( ao );
+ on->on_bi.bi_private = NULL;
+ }
+
+ } else {
+ if ( ao == NULL ) {
+ ao = ch_calloc( 1, sizeof( allop_t ) );
+ on->on_bi.bi_private = (void *)ao;
+
+ } else {
+ ch_free( ao->ao_ndn.bv_val );
+ }
+
+ ao->ao_ndn = ndn;
+ ao->ao_scope = scope;
+ }
+
+ } else {
+ return SLAP_CONF_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static int
+allop_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ allop_t *ao = (allop_t *)on->on_bi.bi_private;
+
+ if ( ao != NULL ) {
+ assert( !BER_BVISNULL( &ao->ao_ndn ) );
+
+ ch_free( ao->ao_ndn.bv_val );
+ ch_free( ao );
+ on->on_bi.bi_private = NULL;
+ }
+
+ return 0;
+}
+
+static int
+allop_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ allop_t *ao = (allop_t *)on->on_bi.bi_private;
+
+ slap_mask_t mask;
+ int i,
+ add_allUser = 0;
+
+ if ( ao == NULL ) {
+ if ( !BER_BVISEMPTY( &op->o_req_ndn )
+ || op->ors_scope != LDAP_SCOPE_BASE )
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ } else {
+ if ( !dnIsSuffix( &op->o_req_ndn, &ao->ao_ndn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ switch ( ao->ao_scope ) {
+ case LDAP_SCOPE_BASE:
+ if ( op->o_req_ndn.bv_len != ao->ao_ndn.bv_len ) {
+ return SLAP_CB_CONTINUE;
+ }
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+ struct berval rdn = op->o_req_ndn;
+
+ rdn.bv_len -= ao->ao_ndn.bv_len + STRLENOF( "," );
+ if ( !dnIsOneLevelRDN( &rdn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ break;
+ }
+ return SLAP_CB_CONTINUE;
+
+ case LDAP_SCOPE_SUBTREE:
+ break;
+ }
+ }
+
+ mask = slap_attr_flags( op->ors_attrs );
+ if ( SLAP_OPATTRS( mask ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !SLAP_USERATTRS( mask ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ i = 0;
+ if ( op->ors_attrs == NULL ) {
+ add_allUser = 1;
+
+ } else {
+ for ( ; !BER_BVISNULL( &op->ors_attrs[ i ].an_name ); i++ )
+ ;
+ }
+
+ op->ors_attrs = op->o_tmprealloc( op->ors_attrs,
+ sizeof( AttributeName ) * ( i + add_allUser + 2 ),
+ op->o_tmpmemctx );
+
+ if ( add_allUser ) {
+ op->ors_attrs[ i ] = slap_anlist_all_user_attributes[ 0 ];
+ i++;
+ }
+
+ op->ors_attrs[ i ] = slap_anlist_all_operational_attributes[ 0 ];
+
+ BER_BVZERO( &op->ors_attrs[ i + 1 ].an_name );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst allop;
+
+int
+allop_init()
+{
+ allop.on_bi.bi_type = "allop";
+
+ allop.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ allop.on_bi.bi_db_config = allop_db_config;
+ allop.on_bi.bi_db_destroy = allop_db_destroy;
+
+ allop.on_bi.bi_op_search = allop_op_search;
+
+ return overlay_register( &allop );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return allop_init();
+}
+
diff --git a/contrib/slapd-modules/allop/slapo-allop.5 b/contrib/slapd-modules/allop/slapo-allop.5
new file mode 100644
index 0000000..9e7fdc9
--- /dev/null
+++ b/contrib/slapd-modules/allop/slapo-allop.5
@@ -0,0 +1,63 @@
+.TH SLAPO-ALLOP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-allop \- All Operational Attributes overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The All Operational Attributes overlay is designed to allow slapd to
+interoperate with dumb clients that expect all attributes, including
+operational ones, to be returned when "*" or an empty attribute list
+is requested, as opposed to RFC2251 and RFC3673.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the All Operational overlay.
+They should appear after the
+.B overlay
+directive and before any subsequent
+.B database
+directive.
+.TP
+.B allop-URI <ldapURI>
+Specify the base and the scope of search operations that trigger the overlay.
+By default, it is "ldap:///??base", i.e. it only applies to the rootDSE.
+This requires the overlay to be instantiated as global.
+
+.SH EXAMPLES
+.LP
+default behavior: only affects requests to the rootDSE
+.nf
+ # global
+ overlay allop
+.fi
+.LP
+affects all requests
+.nf
+ # global
+ overlay allop
+ allop-URI "ldap:///??sub"
+.fi
+.LP
+affects only requests directed to the suffix of a database
+.nf
+ # per database
+ database mdb
+ suffix "dc=example,dc=com"
+ # database specific directives ...
+ overlay allop
+ allop-URI "ldap:///dc=example,dc=com??base"
+.fi
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2005 by Pierangelo Masarati for SysNet s.n.c.
diff --git a/contrib/slapd-modules/allowed/Makefile b/contrib/slapd-modules/allowed/Makefile
new file mode 100644
index 0000000..32cb432
--- /dev/null
+++ b/contrib/slapd-modules/allowed/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_ALLOWED=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = allowed.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+allowed.la: allowed.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/allowed/README b/contrib/slapd-modules/allowed/README
new file mode 100644
index 0000000..a1267cf
--- /dev/null
+++ b/contrib/slapd-modules/allowed/README
@@ -0,0 +1,73 @@
+This directory contains a slapd overlay, "allowed".
+
+ --- o --- o --- o ---
+
+It adds to entries returned by search operations the value of attributes
+
+"allowedAttributes"
+ <http://msdn.microsoft.com/en-us/library/ms675217(VS.85).aspx>
+
+"allowedAttributesEffective"
+ <http://msdn.microsoft.com/en-us/library/ms675218(VS.85).aspx>
+
+"allowedChildClasses"
+ <http://msdn.microsoft.com/en-us/library/ms675219(VS.85).aspx>
+
+"allowedChildClassesEffective"
+ <http://msdn.microsoft.com/en-us/library/ms675220(VS.85).aspx>
+
+No other use is made of those attributes: they cannot be compared,
+they cannot be used in search filters, they cannot be used in ACLs, ...
+
+ --- o --- o --- o ---
+
+Usage: add to slapd.conf(5)
+
+
+moduleload path/to/allowed.so
+overlay allowed
+
+or add
+
+dn: olcOverlay={0}allowed,olcDatabase={1}bdb,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}allowed
+
+as a child of the database that's intended to support this feature
+(replace "olcDatabase={1}bdb,cn=config" with the appropriate parent);
+or use
+
+dn: olcOverlay={0}allowed,olcDatabase={-1}frontend,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}allowed
+
+if it's supposed to be global.
+
+ --- o --- o --- o ---
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o allowed.so allowed.c
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2006-2022 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.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+ACKNOWLEDGEMENTS:
+This work was initially developed by Pierangelo Masarati for inclusion in
+OpenLDAP Software.
+
diff --git a/contrib/slapd-modules/allowed/allowed.c b/contrib/slapd-modules/allowed/allowed.c
new file mode 100644
index 0000000..26e3106
--- /dev/null
+++ b/contrib/slapd-modules/allowed/allowed.c
@@ -0,0 +1,504 @@
+/* allowed.c - add allowed attributes based on ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * Rationale: return in allowedAttributes the attributes required/allowed
+ * by the objectClasses that are currently present in an object; return
+ * in allowedAttributesEffective the subset of the above that can be written
+ * by the identity that performs the search.
+ *
+ * Caveats:
+ * - right now, the overlay assumes that all values of the objectClass
+ * attribute will be returned in rs->sr_entry; this may not be true
+ * in general, but it usually is for back-mdb. To generalize,
+ * the search request should be analyzed, and if allowedAttributes or
+ * allowedAttributesEffective are requested, add objectClass to the
+ * requested attributes
+ * - it assumes that there is no difference between write-add and
+ * write-delete
+ * - it assumes that access rules do not depend on the values of the
+ * attributes or on the contents of the entry (attr/val, filter, ...)
+ * allowedAttributes and allowedAttributesEffective cannot be used
+ * in filters or in compare
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_ALLOWED=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_ALLOWED
+
+#include "slap.h"
+
+/*
+ * NOTE: part of the schema definition reported below is taken
+ * from Microsoft schema definitions (OID, NAME, SYNTAX);
+ *
+ * EQUALITY is taken from
+ * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
+ * (posted by Andrew Bartlett)
+ *
+ * The rest is guessed. Specifically
+ *
+ * DESC briefly describes the purpose
+ *
+ * NO-USER-MODIFICATION is added to make attributes operational
+ *
+ * USAGE is set to "dSAOperation" as per ITS#7493,
+ * to prevent replication, since this information
+ * is generated (based on ACL and identity of request)
+ * and not stored.
+ */
+
+#define AA_SCHEMA_AT "1.2.840.113556.1.4"
+
+static AttributeDescription
+ *ad_allowedChildClasses,
+ *ad_allowedChildClassesEffective,
+ *ad_allowedAttributes,
+ *ad_allowedAttributesEffective;
+
+static struct {
+ char *at;
+ AttributeDescription **ad;
+} aa_attrs[] = {
+ { "( " AA_SCHEMA_AT ".911 "
+ "NAME 'allowedChildClasses' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Child classes allowed for a given object' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedChildClasses },
+ { "( " AA_SCHEMA_AT ".912 "
+ "NAME 'allowedChildClassesEffective' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Child classes allowed for a given object according to ACLs' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedChildClassesEffective },
+ { "( " AA_SCHEMA_AT ".913 "
+ "NAME 'allowedAttributes' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Attributes allowed for a given object' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedAttributes },
+ { "( " AA_SCHEMA_AT ".914 "
+ "NAME 'allowedAttributesEffective' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Attributes allowed for a given object according to ACLs' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedAttributesEffective },
+
+ /* TODO: add objectClass stuff? */
+
+ { NULL, NULL }
+};
+
+static int
+aa_add_at( AttributeType *at, AttributeType ***atpp )
+{
+ int i = 0;
+
+ if ( *atpp ) {
+ for ( i = 0; (*atpp)[ i ] != NULL; i++ ) {
+ if ( (*atpp)[ i ] == at ) {
+ break;
+ }
+ }
+
+ if ( (*atpp)[ i ] != NULL ) {
+ return 0;
+ }
+ }
+
+ *atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) );
+ (*atpp)[ i ] = at;
+ (*atpp)[ i + 1 ] = NULL;
+
+ return 0;
+}
+
+static int
+aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp )
+{
+ int i = 0;
+
+ if ( *ocpp ) {
+ for ( ; (*ocpp)[ i ] != NULL; i++ ) {
+ if ( (*ocpp)[ i ] == oc ) {
+ break;
+ }
+ }
+
+ if ( (*ocpp)[ i ] != NULL ) {
+ return 0;
+ }
+ }
+
+ *ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) );
+ (*ocpp)[ i ] = oc;
+ (*ocpp)[ i + 1 ] = NULL;
+
+ if ( oc->soc_required ) {
+ int i;
+
+ for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
+ aa_add_at( oc->soc_required[ i ], atpp );
+ }
+ }
+
+ if ( oc->soc_allowed ) {
+ int i;
+
+ for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) {
+ aa_add_at( oc->soc_allowed[ i ], atpp );
+ }
+ }
+
+ return 0;
+}
+
+static int
+aa_operational( Operation *op, SlapReply *rs )
+{
+ Attribute *a, **ap;
+ AccessControlState acl_state = ACL_STATE_INIT;
+ struct berval *v;
+ AttributeType **atp = NULL;
+ ObjectClass **ocp = NULL;
+
+#define GOT_NONE (0x0U)
+#define GOT_C (0x1U)
+#define GOT_CE (0x2U)
+#define GOT_A (0x4U)
+#define GOT_AE (0x8U)
+#define GOT_ALL (GOT_C|GOT_CE|GOT_A|GOT_AE)
+ int got = GOT_NONE;
+
+ /* only add if requested */
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) {
+ got = GOT_ALL;
+
+ } else {
+ if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) {
+ got |= GOT_C;
+ }
+
+ if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) {
+ got |= GOT_CE;
+ }
+
+ if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) {
+ got |= GOT_A;
+ }
+
+ if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) {
+ got |= GOT_AE;
+ }
+ }
+
+ if ( got == GOT_NONE ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* shouldn't be called without an entry; please check */
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* go to last */ ;
+
+ /* see caveats; this is not guaranteed for all backends */
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
+ if ( a == NULL ) {
+ goto do_oc;
+ }
+
+ /* if client has no access to objectClass attribute; don't compute */
+ if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass,
+ NULL, ACL_READ, &acl_state ) )
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) {
+ ObjectClass *oc = oc_bvfind( v );
+
+ assert( oc != NULL );
+
+ /* if client has no access to specific value, don't compute */
+ if ( !access_allowed( op, rs->sr_entry,
+ slap_schema.si_ad_objectClass,
+ &oc->soc_cname, ACL_READ, &acl_state ) )
+ {
+ continue;
+ }
+
+ aa_add_oc( oc, &ocp, &atp );
+
+ if ( oc->soc_sups ) {
+ int i;
+
+ for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
+ aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
+ }
+ }
+ }
+
+ ch_free( ocp );
+
+ if ( atp != NULL ) {
+ BerVarray bv_allowed = NULL,
+ bv_effective = NULL;
+ int i, ja = 0, je = 0;
+
+ for ( i = 0; atp[ i ] != NULL; i++ )
+ /* just count */ ;
+
+ if ( got & GOT_A ) {
+ bv_allowed = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+ if ( got & GOT_AE ) {
+ bv_effective = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+
+ for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) {
+ if ( got & GOT_A ) {
+ ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname );
+ ja++;
+ }
+
+ if ( got & GOT_AE ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) {
+ /* log? */
+ continue;
+ }
+
+ if ( access_allowed( op, rs->sr_entry,
+ ad, NULL, ACL_WRITE, NULL ) )
+ {
+ ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname );
+ je++;
+ }
+ }
+ }
+
+ ch_free( atp );
+
+ if ( ( got & GOT_A ) && ja > 0 ) {
+ *ap = attr_alloc( ad_allowedAttributes );
+ (*ap)->a_vals = bv_allowed;
+ (*ap)->a_nvals = bv_allowed;
+ (*ap)->a_numvals = ja;
+ ap = &(*ap)->a_next;
+ }
+
+ if ( ( got & GOT_AE ) && je > 0 ) {
+ *ap = attr_alloc( ad_allowedAttributesEffective );
+ (*ap)->a_vals = bv_effective;
+ (*ap)->a_nvals = bv_effective;
+ (*ap)->a_numvals = je;
+ ap = &(*ap)->a_next;
+ }
+
+ *ap = NULL;
+ }
+
+do_oc:;
+ if ( ( got & GOT_C ) || ( got & GOT_CE ) ) {
+ BerVarray bv_allowed = NULL,
+ bv_effective = NULL;
+ int i, ja = 0, je = 0;
+
+ ObjectClass *oc;
+
+ for ( i = 0, oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
+ /* we can only add AUXILIARY objectClasses */
+ if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ continue;
+ }
+
+ i++;
+ }
+
+ if ( got & GOT_C ) {
+ bv_allowed = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+ if ( got & GOT_CE ) {
+ bv_effective = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+
+ for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
+ /* we can only add AUXILIARY objectClasses */
+ if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ continue;
+ }
+
+ if ( got & GOT_C ) {
+ ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname );
+ ja++;
+ }
+
+ if ( got & GOT_CE ) {
+ if ( !access_allowed( op, rs->sr_entry,
+ slap_schema.si_ad_objectClass,
+ &oc->soc_cname, ACL_WRITE, NULL ) )
+ {
+ goto done_ce;
+ }
+
+ if ( oc->soc_required ) {
+ for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) {
+ /* log? */
+ continue;
+ }
+
+ if ( !access_allowed( op, rs->sr_entry,
+ ad, NULL, ACL_WRITE, NULL ) )
+ {
+ goto done_ce;
+ }
+ }
+ }
+
+ ber_dupbv( &bv_effective[ je ], &oc->soc_cname );
+ je++;
+ }
+done_ce:;
+ }
+
+ if ( ( got & GOT_C ) && ja > 0 ) {
+ *ap = attr_alloc( ad_allowedChildClasses );
+ (*ap)->a_vals = bv_allowed;
+ (*ap)->a_nvals = bv_allowed;
+ (*ap)->a_numvals = ja;
+ ap = &(*ap)->a_next;
+ }
+
+ if ( ( got & GOT_CE ) && je > 0 ) {
+ *ap = attr_alloc( ad_allowedChildClassesEffective );
+ (*ap)->a_vals = bv_effective;
+ (*ap)->a_nvals = bv_effective;
+ (*ap)->a_numvals = je;
+ ap = &(*ap)->a_next;
+ }
+
+ *ap = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst aa;
+
+#if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3
+/* backport register_at() from HEAD, to allow building with OL <= 2.3 */
+static int
+register_at( char *def, AttributeDescription **rad, int dupok )
+{
+ LDAPAttributeType *at;
+ int code, freeit = 0;
+ const char *err;
+ AttributeDescription *ad = NULL;
+
+ at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !at ) {
+ Debug( LDAP_DEBUG_ANY,
+ "register_at: AttributeType \"%s\": %s, %s\n",
+ def, ldap_scherr2str(code), err );
+ return code;
+ }
+
+ code = at_add( at, 0, NULL, &err );
+ if ( code ) {
+ if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
+ freeit = 1;
+
+ } else {
+ ldap_attributetype_free( at );
+ Debug( LDAP_DEBUG_ANY,
+ "register_at: AttributeType \"%s\": %s, %s\n",
+ def, scherr2str(code), err );
+ return code;
+ }
+ }
+ code = slap_str2ad( at->at_names[0], &ad, &err );
+ if ( freeit || code ) {
+ ldap_attributetype_free( at );
+ } else {
+ ldap_memfree( at );
+ }
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
+ def, err );
+ }
+ if ( rad ) *rad = ad;
+ return code;
+}
+#endif
+
+#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
+int
+aa_initialize( void )
+{
+ int i;
+
+ aa.on_bi.bi_type = "allowed";
+
+ aa.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ aa.on_bi.bi_operational = aa_operational;
+
+ /* aa schema integration */
+ for ( i = 0; aa_attrs[i].at; i++ ) {
+ int code;
+
+ code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "aa_initialize: register_at failed\n" );
+ return -1;
+ }
+ }
+
+ return overlay_register( &aa );
+}
+
+#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return aa_initialize();
+}
+#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_ALLOWED */
diff --git a/contrib/slapd-modules/authzid/Makefile b/contrib/slapd-modules/authzid/Makefile
new file mode 100644
index 0000000..ef9c4bc
--- /dev/null
+++ b/contrib/slapd-modules/authzid/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = authzid.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+authzid.la: authzid.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/authzid/authzid.c b/contrib/slapd-modules/authzid/authzid.c
new file mode 100644
index 0000000..37264bf
--- /dev/null
+++ b/contrib/slapd-modules/authzid/authzid.c
@@ -0,0 +1,390 @@
+/* authzid.c - RFC 3829 Authzid Control */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * RFC 3829 Authzid
+ *
+ * must be instantiated as a global overlay
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ac/string.h"
+
+typedef struct authzid_conn_t {
+ Connection *conn;
+ int refcnt;
+ char authzid_flag;
+} authzid_conn_t;
+
+static ldap_pvt_thread_mutex_t authzid_mutex;
+static Avlnode *authzid_tree;
+
+static int
+authzid_conn_cmp( const void *c1, const void *c2 )
+{
+ const authzid_conn_t *ac1 = (const authzid_conn_t *)c1;
+ const authzid_conn_t *ac2 = (const authzid_conn_t *)c2;
+
+ return SLAP_PTRCMP( ac1->conn, ac2->conn );
+}
+
+static int
+authzid_conn_dup( void *c1, void *c2 )
+{
+ authzid_conn_t *ac1 = (authzid_conn_t *)c1;
+ authzid_conn_t *ac2 = (authzid_conn_t *)c2;
+
+ if ( ac1->conn == ac2->conn ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int authzid_cid;
+static slap_overinst authzid;
+
+static authzid_conn_t *
+authzid_conn_find( Connection *c )
+{
+ authzid_conn_t *ac = NULL, tmp = { 0 };
+
+ tmp.conn = c;
+ ac = (authzid_conn_t *)ldap_avl_find( authzid_tree, (caddr_t)&tmp, authzid_conn_cmp );
+ if ( ac == NULL || ( ac != NULL && ac->refcnt != 0 ) ) {
+ ac = NULL;
+ }
+ if ( ac ) {
+ ac->refcnt++;
+ }
+
+ return ac;
+}
+
+static authzid_conn_t *
+authzid_conn_get( Connection *c )
+{
+ authzid_conn_t *ac = NULL;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( ac && ac->refcnt ) ac = NULL;
+ if ( ac ) ac->refcnt++;
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ return ac;
+}
+
+static void
+authzid_conn_release( authzid_conn_t *ac )
+{
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac->refcnt--;
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+}
+
+static int
+authzid_conn_insert( Connection *c, char flag )
+{
+ authzid_conn_t *ac;
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( ac ) {
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+ return -1;
+ }
+
+ ac = ch_malloc( sizeof( authzid_conn_t ) );
+ ac->conn = c;
+ ac->refcnt = 0;
+ ac->authzid_flag = flag;
+ rc = ldap_avl_insert( &authzid_tree, (caddr_t)ac,
+ authzid_conn_cmp, authzid_conn_dup );
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ return rc;
+}
+
+static int
+authzid_conn_remove( Connection *c )
+{
+ authzid_conn_t *ac, *tmp;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( !ac ) {
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+ return -1;
+ }
+ tmp = ldap_avl_delete( &authzid_tree, (caddr_t)ac, authzid_conn_cmp );
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ assert( tmp == ac );
+ ch_free( ac );
+
+ return 0;
+}
+
+static int
+authzid_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ LDAPControl **ctrls;
+ struct berval edn = BER_BVNULL;
+ ber_len_t len = 0;
+ int n = 0;
+
+ assert( rs->sr_tag = LDAP_RES_BIND );
+
+ if ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS ) {
+ authzid_conn_t *ac = op->o_controls[ authzid_cid ];
+ if ( ac ) {
+ authzid_conn_release( ac );
+ } else {
+ (void)authzid_conn_insert( op->o_conn, op->o_ctrlflag[ authzid_cid ] );
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ (void)authzid_conn_remove( op->o_conn );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !BER_BVISEMPTY( &op->orb_edn ) ) {
+ edn = op->orb_edn;
+
+ } else if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
+ edn = op->o_conn->c_dn;
+ }
+
+ if ( !BER_BVISEMPTY( &edn ) ) {
+ ber_tag_t save_tag = op->o_tag;
+ struct berval save_dn = op->o_dn;
+ struct berval save_ndn = op->o_ndn;
+ int rc;
+
+ /* pretend it's an extop without data,
+ * so it is treated as a generic write
+ */
+ op->o_tag = LDAP_REQ_EXTENDED;
+ op->o_dn = edn;
+ op->o_ndn = edn;
+ rc = backend_check_restrictions( op, rs, NULL );
+ op->o_tag = save_tag;
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_CONFIDENTIALITY_REQUIRED;
+ return SLAP_CB_CONTINUE;
+ }
+
+ len = STRLENOF("dn:") + edn.bv_len;
+ }
+
+ /* save original controls in sc_private;
+ * will be restored by sc_cleanup
+ */
+ if ( rs->sr_ctrls != NULL ) {
+ op->o_callback->sc_private = rs->sr_ctrls;
+ for ( ; rs->sr_ctrls[n] != NULL; n++ )
+ ;
+ }
+
+ ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( n + 2 ), op->o_tmpmemctx );
+ n = 0;
+ if ( rs->sr_ctrls ) {
+ for ( ; rs->sr_ctrls[n] != NULL; n++ ) {
+ ctrls[n] = rs->sr_ctrls[n];
+ }
+ }
+
+ /* anonymous: "", otherwise "dn:<dn>" */
+ ctrls[n] = op->o_tmpalloc( sizeof( LDAPControl ) + len + 1, op->o_tmpmemctx );
+ ctrls[n]->ldctl_oid = LDAP_CONTROL_AUTHZID_RESPONSE;
+ ctrls[n]->ldctl_iscritical = 0;
+ ctrls[n]->ldctl_value.bv_len = len;
+ ctrls[n]->ldctl_value.bv_val = (char *)&ctrls[n][1];
+ if ( len ) {
+ char *ptr;
+
+ ptr = lutil_strcopy( ctrls[n]->ldctl_value.bv_val, "dn:" );
+ ptr = lutil_strncopy( ptr, edn.bv_val, edn.bv_len );
+ }
+ ctrls[n]->ldctl_value.bv_val[len] = '\0';
+ ctrls[n + 1] = NULL;
+
+ rs->sr_ctrls = ctrls;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+authzid_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ if ( rs->sr_ctrls ) {
+ LDAPControl *ctrl;
+
+ /* if ours, cleanup */
+ ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE, rs->sr_ctrls, NULL );
+ if ( ctrl ) {
+ op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( op->o_callback->sc_private != NULL ) {
+ rs->sr_ctrls = (LDAPControl **)op->o_callback->sc_private;
+ op->o_callback->sc_private = NULL;
+ }
+ }
+
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+authzid_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *sc;
+
+ if ( op->o_ctrlflag[ authzid_cid ] <= SLAP_CONTROL_IGNORED ) {
+ authzid_conn_t *ac = authzid_conn_get( op->o_conn );
+ if ( ac ) {
+ op->o_ctrlflag[ authzid_cid ] = ac->authzid_flag;
+ op->o_controls[ authzid_cid] = ac;
+ }
+ }
+
+ if ( op->o_ctrlflag[ authzid_cid ] > SLAP_CONTROL_IGNORED ) {
+ sc = op->o_callback;
+ op->o_callback = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+ op->o_callback->sc_response = authzid_response;
+ op->o_callback->sc_cleanup = authzid_cleanup;
+ op->o_callback->sc_private = NULL;
+ op->o_callback->sc_writewait = NULL;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+parse_authzid_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_ctrlflag[ authzid_cid ] != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "authzid control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "authzid control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* drop ongoing requests */
+ (void)authzid_conn_remove( op->o_conn );
+
+ op->o_ctrlflag[ authzid_cid ] = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+authzid_db_init( BackendDB *be, ConfigReply *cr )
+{
+ if ( !SLAP_ISGLOBALOVERLAY( be ) ) {
+ /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
+ if ( cr ) {
+ snprintf( cr->msg, sizeof(cr->msg),
+ "slapo-authzid must be global" );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ }
+ return 1;
+ }
+
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_AUTHZID_REQUEST,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_BIND|SLAP_CTRL_HIDE, NULL,
+ parse_authzid_ctrl, &authzid_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "authzid_initialize: Failed to register control '%s' (%d)\n",
+ LDAP_CONTROL_AUTHZID_REQUEST, rc );
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Almost pointless, by now, since this overlay needs to be global,
+ * and global overlays deletion is not supported yet.
+ */
+static int
+authzid_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_AUTHZID_REQUEST );
+#endif /* SLAP_CONFIG_DELETE */
+
+ unregister_supported_control( LDAP_CONTROL_AUTHZID_REQUEST );
+
+ return 0;
+}
+
+static int
+authzid_initialize( void )
+{
+ ldap_pvt_thread_mutex_init( &authzid_mutex );
+
+ authzid.on_bi.bi_type = "authzid";
+
+ authzid.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ authzid.on_bi.bi_db_init = authzid_db_init;
+ authzid.on_bi.bi_db_destroy = authzid_db_destroy;
+ authzid.on_bi.bi_op_bind = authzid_op_bind;
+
+ return overlay_register( &authzid );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return authzid_initialize();
+}
+
diff --git a/contrib/slapd-modules/autogroup/Makefile b/contrib/slapd-modules/autogroup/Makefile
new file mode 100644
index 0000000..7dd6613
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = autogroup.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+autogroup.la: autogroup.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/autogroup/README b/contrib/slapd-modules/autogroup/README
new file mode 100644
index 0000000..b68dd75
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/README
@@ -0,0 +1,120 @@
+autogroup overlay Readme
+
+DESCRIPTION
+ The autogroup overlay allows automated updates of group memberships which
+ meet the requirements of any filter contained in the group definition.
+ The filters are built from LDAP URI-valued attributes. Any time an object
+ is added/deleted/updated, it is tested for compliance with the filters,
+ and its membership is accordingly updated. For searches and compares
+ it behaves like a static group.
+ If the attribute part of the URI is filled, the group entry is populated
+ by the values of this attribute in the entries resulting from the search.
+
+BUILDING
+ A Makefile is included.
+
+CONFIGURATION
+ # dyngroup.schema:
+ The dyngroup schema must be modified, adding the 'member' attribute
+ to the MAY clause of the groupOfURLs object class, i.e.:
+
+ objectClass ( NetscapeLDAPobjectClass:33
+ NAME 'groupOfURLs'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( memberURL $ businessCategory $ description $ o $ ou $
+ owner $ seeAlso $ member) )
+
+
+ # slapd.conf:
+
+ moduleload /path/to/autogroup.so
+ Loads the overlay (OpenLDAP must be built with --enable-modules).
+
+ overlay autogroup
+ This directive adds the autogroup overlay to the current database.
+
+ autogroup-attrset <group-oc> <URL-ad> <member-ad>
+ This configuration option is defined for the autogroup overlay.
+ It may have multiple occurrences, and it must appear after the
+ overlay directive.
+
+ The value <group-oc> is the name of the objectClass that represents
+ the group.
+
+ The value <URL-ad> is the name of the attributeDescription that
+ contains the URI that is converted to the filters. If no URI is
+ present, there will be no members in that group. It must be a subtype
+ of labeledURI.
+
+ The value <member-ad> is the name of the attributeDescription that
+ specifies the member attribute. User modification of this attribute
+ is disabled for consistency.
+
+ autogroup-memberof-ad <memberof-ad>
+ This configuration option is defined for the autogroup overlay.
+
+ It defines the attribute that is used by the memberOf overlay
+ to store the names of groups that an entry is member of; it must be
+ DN-valued. It should be set to the same value as
+ memberof-memberof-ad. It defaults to 'memberOf'.
+
+
+EXAMPLE
+ ### slapd.conf
+ include /path/to/dyngroup.schema
+ # ...
+ moduleload /path/to/autogroup.so
+ # ...
+
+ database <database>
+ # ...
+
+ overlay autogroup
+ autogroup-attrset groupOfURLs memberURL member
+ ### end slapd.conf
+
+ ### slapd.conf
+ include /path/to/dyngroup.schema
+ # ...
+ moduleload /path/to/autogroup.so
+ moduleload /path/to/memberof.so
+ # ...
+
+ database <database>
+ #...
+
+ overlay memberof
+ memberof-memberof-ad foo
+
+ overlay autogroup
+ autogroup-attrset groupOfURLs memberURL member
+ autogroup-memberof-ad foo
+ ### end slapd.conf
+
+CAVEATS
+ As with static groups, update operations on groups with a large number
+ of members may be slow.
+ If the attribute part of the URI is specified, modify and delete operations
+ are more difficult to handle. In these cases the overlay will try to detect
+ if groups have been modified and then simply refresh them. This can cause
+ performance hits if the search specified by the URI deals with a significant
+ number of entries.
+
+ACKNOWLEDGEMENTS
+ This module was originally written in 2007 by Michał Szulczyński. Further
+ enhancements were contributed by Howard Chu, Raphael Ouazana,
+ Norbert Pueschel, and Christian Manal.
+
+---
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright (C) 2007 Michał Szulczyński.
+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 file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+http://www.OpenLDAP.org/license.html.
diff --git a/contrib/slapd-modules/autogroup/autogroup.c b/contrib/slapd-modules/autogroup/autogroup.c
new file mode 100644
index 0000000..cbcedfe
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/autogroup.c
@@ -0,0 +1,2236 @@
+/* autogroup.c - automatic group overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2007 Michał Szulczyński.
+ * Portions Copyright 2009 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Michał Szulczyński for inclusion in
+ * OpenLDAP Software. Additional significant contributors include:
+ * Howard Chu
+ * Raphael Ouazana
+ * Norbert Pueschel
+ * Christian Manal
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#ifndef SLAPD_MEMBEROF_ATTR
+#define SLAPD_MEMBEROF_ATTR "memberOf"
+#endif
+
+static slap_overinst autogroup;
+
+/* Filter represents the memberURL of a group. */
+typedef struct autogroup_filter_t {
+ struct berval agf_dn; /* The base DN in memberURL */
+ struct berval agf_ndn;
+ struct berval agf_filterstr;
+ Filter *agf_filter;
+ int agf_scope;
+ AttributeName *agf_anlist;
+ struct autogroup_filter_t *agf_next;
+} autogroup_filter_t;
+
+/* Description of group attributes. */
+typedef struct autogroup_def_t {
+ ObjectClass *agd_oc;
+ AttributeDescription *agd_member_url_ad;
+ AttributeDescription *agd_member_ad;
+ struct autogroup_def_t *agd_next;
+} autogroup_def_t;
+
+/* Represents the group entry. */
+typedef struct autogroup_entry_t {
+ BerValue age_dn;
+ BerValue age_ndn;
+ autogroup_filter_t *age_filter; /* List of filters made from memberURLs */
+ autogroup_def_t *age_def; /* Attribute definition */
+ ldap_pvt_thread_mutex_t age_mutex;
+ int age_mustrefresh; /* Defined in request to refresh in response */
+ int age_modrdn_olddnmodified; /* Defined in request to refresh in response */
+ struct autogroup_entry_t *age_next;
+} autogroup_entry_t;
+
+/* Holds pointers to attribute definitions and groups. */
+typedef struct autogroup_info_t {
+ autogroup_def_t *agi_def; /* Group attributes definitions. */
+ autogroup_entry_t *agi_entry; /* Group entries. */
+ AttributeDescription *agi_memberof_ad; /* memberOf attribute description */
+ ldap_pvt_thread_mutex_t agi_mutex;
+} autogroup_info_t;
+
+/* Search callback for adding groups initially. */
+typedef struct autogroup_sc_t {
+ autogroup_info_t *ags_info; /* Group definitions and entries. */
+ autogroup_def_t *ags_def; /* Attributes definition of the group being added. */
+} autogroup_sc_t;
+
+/* Used for adding members, found when searching, to a group. */
+typedef struct autogroup_ga_t {
+ autogroup_entry_t *agg_group; /* The group to which the members will be added. */
+ autogroup_filter_t *agg_filter; /* Current filter */
+ Entry *agg_entry; /* Used in autogroup_member_search_cb to modify
+ this entry with the search results. */
+
+ Modifications *agg_mod; /* Used in autogroup_member_search_modify_cb to hold the
+ search results which will be added to the group. */
+
+ Modifications *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
+ have to search for the last mod added. */
+} autogroup_ga_t;
+
+
+/*
+** dn, ndn - the DN of the member to add
+** age - the group to which the member DN will be added
+*/
+static int
+autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+ SlapReply sreply = {REP_RESULT};
+ BerValue *vals, *nvals;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ assert( dn != NULL );
+ assert( ndn != NULL );
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ ber_dupbv( vals, dn );
+ BER_BVZERO( &vals[ 1 ] );
+ ber_dupbv( nvals, ndn );
+ BER_BVZERO( &nvals[ 1 ] );
+
+ modlist->sml_op = LDAP_MOD_ADD;
+ modlist->sml_desc = age->age_def->agd_member_ad;
+ modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist->sml_values = vals;
+ modlist->sml_nvalues = nvals;
+ modlist->sml_numvals = 1;
+ modlist->sml_flags = SLAP_MOD_INTERNAL;
+ modlist->sml_next = NULL;
+
+ o.o_opid = 0; /* shared with op, saved above */
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.o_callback = &cb;
+ o.orm_modlist = modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ slap_mods_free( modlist, 1 );
+ op->o_opid = opid;
+
+ return sreply.sr_err;
+}
+
+/*
+** e - the entry where to get the attribute values
+** age - the group to which the values will be added
+*/
+static int
+autogroup_add_member_values_to_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications modlist;
+ SlapReply sreply = {REP_RESULT};
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ modlist.sml_op = LDAP_MOD_ADD;
+ modlist.sml_desc = age->age_def->agd_member_ad;
+ modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist.sml_values = attr->a_vals;
+ modlist.sml_nvalues = attr->a_nvals;
+ modlist.sml_numvals = attr->a_numvals;
+ modlist.sml_flags = SLAP_MOD_INTERNAL;
+ modlist.sml_next = NULL;
+
+ o.o_opid = 0;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.o_callback = &cb;
+ o.orm_modlist = &modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+ op->o_opid = opid;
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ return sreply.sr_err;
+}
+
+/*
+** dn,ndn - the DN to be deleted
+** age - the group from which the DN will be deleted
+** If we pass a NULL dn and ndn, all members are deleted from the group.
+*/
+static int
+autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+ SlapReply sreply = {REP_RESULT};
+ BerValue *vals, *nvals;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ if ( dn == NULL || ndn == NULL ) {
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
+ age->age_dn.bv_val );
+
+ modlist->sml_values = NULL;
+ modlist->sml_nvalues = NULL;
+ modlist->sml_numvals = 0;
+ } else {
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ ber_dupbv( vals, dn );
+ BER_BVZERO( &vals[ 1 ] );
+ ber_dupbv( nvals, ndn );
+ BER_BVZERO( &nvals[ 1 ] );
+
+ modlist->sml_values = vals;
+ modlist->sml_nvalues = nvals;
+ modlist->sml_numvals = 1;
+ }
+
+
+ modlist->sml_op = LDAP_MOD_DELETE;
+ modlist->sml_desc = age->age_def->agd_member_ad;
+ modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist->sml_flags = SLAP_MOD_INTERNAL;
+ modlist->sml_next = NULL;
+
+ o.o_opid = 0;
+ o.o_callback = &cb;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.orm_modlist = modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ slap_mods_free( modlist, 1 );
+
+ op->o_opid = opid;
+ return sreply.sr_err;
+}
+
+/*
+** e - the entry where to get the attribute values
+** age - the group from which the values will be deleted
+*/
+static int
+autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications modlist;
+ SlapReply sreply = {REP_RESULT};
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ modlist.sml_op = LDAP_MOD_DELETE;
+ modlist.sml_desc = age->age_def->agd_member_ad;
+ modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist.sml_values = attr->a_vals;
+ modlist.sml_nvalues = attr->a_nvals;
+ modlist.sml_numvals = attr->a_numvals;
+ modlist.sml_flags = SLAP_MOD_INTERNAL;
+ modlist.sml_next = NULL;
+
+ o.o_opid = 0;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.o_callback = &cb;
+ o.orm_modlist = &modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+ op->o_opid = opid;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ return sreply.sr_err;
+}
+
+/*
+** Callback used to add entries to a group,
+** which are going to be written in the database
+** (used in bi_op_add)
+** The group is passed in autogroup_ga_t->agg_group
+*/
+static int
+autogroup_member_search_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
+ autogroup_entry_t *age = agg->agg_group;
+ autogroup_filter_t *agf = agg->agg_filter;
+ Modification mod;
+ const char *text = NULL;
+ char textbuf[1024];
+ struct berval *vals, *nvals;
+ struct berval lvals[ 2 ], lnvals[ 2 ];
+ int numvals;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ if ( agf->agf_anlist ) {
+ Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
+ if (attr) {
+ vals = attr->a_vals;
+ nvals = attr->a_nvals;
+ numvals = attr->a_numvals;
+ } else {
+ // Nothing to add
+ return 0;
+ }
+ } else {
+ lvals[ 0 ] = rs->sr_entry->e_name;
+ BER_BVZERO( &lvals[ 1 ] );
+ lnvals[ 0 ] = rs->sr_entry->e_nname;
+ BER_BVZERO( &lnvals[ 1 ] );
+ vals = lvals;
+ nvals = lnvals;
+ numvals = 1;
+ }
+
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = age->age_def->agd_member_ad;
+ mod.sm_type = age->age_def->agd_member_ad->ad_cname;
+ mod.sm_values = vals;
+ mod.sm_nvalues = nvals;
+ mod.sm_numvals = numvals;
+
+ modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
+ }
+
+ return 0;
+}
+
+/*
+** Callback used to add entries to a group, which is already in the database.
+** (used in on_response)
+** The group is passed in autogroup_ga_t->agg_group
+** NOTE: Very slow.
+*/
+static int
+autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
+ autogroup_entry_t *age = agg->agg_group;
+ autogroup_filter_t *agf = agg->agg_filter;
+ Modifications *modlist;
+ struct berval *vals, *nvals;
+ struct berval lvals[ 2 ], lnvals[ 2 ];
+ int numvals;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ if ( agf->agf_anlist ) {
+ Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
+ if (attr) {
+ vals = attr->a_vals;
+ nvals = attr->a_nvals;
+ numvals = attr->a_numvals;
+ } else {
+ // Nothing to add
+ return 0;
+ }
+ } else {
+ lvals[ 0 ] = rs->sr_entry->e_name;
+ BER_BVZERO( &lvals[ 1 ] );
+ lnvals[ 0 ] = rs->sr_entry->e_nname;
+ BER_BVZERO( &lnvals[ 1 ] );
+ vals = lvals;
+ nvals = lnvals;
+ numvals = 1;
+ }
+
+ if ( numvals ) {
+ modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+
+ modlist->sml_op = LDAP_MOD_ADD;
+ modlist->sml_desc = age->age_def->agd_member_ad;
+ modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+
+ ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
+ ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
+ modlist->sml_numvals = numvals;
+
+ modlist->sml_flags = SLAP_MOD_INTERNAL;
+ modlist->sml_next = NULL;
+
+ if ( agg->agg_mod == NULL ) {
+ agg->agg_mod = modlist;
+ agg->agg_mod_last = modlist;
+ } else {
+ agg->agg_mod_last->sml_next = modlist;
+ agg->agg_mod_last = modlist;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+
+/*
+** Adds all entries matching the passed filter to the specified group.
+** If modify == 1, then we modify the group's entry in the database using be_modify.
+** If modify == 0, then, we must supply a rw entry for the group,
+** because we only modify the entry, without calling be_modify.
+** e - the group entry, to which the members will be added
+** age - the group
+** agf - the filter
+*/
+static int
+autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Operation o = *op;
+ SlapReply rs = { REP_SEARCH };
+ slap_callback cb = { 0 };
+ slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
+ autogroup_ga_t agg;
+ OpExtra oex;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
+ age->age_dn.bv_val );
+
+ o.ors_attrsonly = 0;
+ o.o_tag = LDAP_REQ_SEARCH;
+
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = agf->agf_dn;
+ o.o_req_ndn = agf->agf_ndn;
+
+ o.ors_filterstr = agf->agf_filterstr;
+ o.ors_filter = agf->agf_filter;
+
+ o.ors_scope = agf->agf_scope;
+ o.ors_deref = LDAP_DEREF_NEVER;
+ o.ors_limit = NULL;
+ o.ors_tlimit = SLAP_NO_LIMIT;
+ o.ors_slimit = SLAP_NO_LIMIT;
+ o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
+ o.o_do_not_cache = 1;
+
+ agg.agg_group = age;
+ agg.agg_filter = agf;
+ agg.agg_mod = NULL;
+ agg.agg_mod_last = NULL;
+ agg.agg_entry = e;
+ cb.sc_private = &agg;
+
+ if ( modify == 1 ) {
+ cb.sc_response = autogroup_member_search_modify_cb;
+ } else {
+ cb.sc_response = autogroup_member_search_cb;
+ }
+
+ cb.sc_cleanup = NULL;
+ cb.sc_next = NULL;
+
+ o.o_callback = &cb;
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ op->o_bd->be_search( &o, &rs );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ if ( modify == 1 && agg.agg_mod ) {
+ unsigned long opid = op->o_opid;
+
+ rs_reinit( &rs, REP_RESULT );
+
+ o = *op;
+ o.o_opid = 0;
+ o.o_callback = &null_cb;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.orm_modlist = agg.agg_mod;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+ o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &rs );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ slap_mods_free(agg.agg_mod, 1);
+ op->o_opid = opid;
+ }
+
+ return 0;
+}
+
+/*
+** Adds a group to the internal list from the passed entry.
+** scan specifies whether to add all matching members to the group.
+** modify specifies whether to modify the given group entry (when modify == 0),
+** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
+** agi - pointer to the groups and the attribute definitions
+** agd - the attribute definition of the added group
+** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
+** ndn - the DN of the group, can be NULL if we give a non-NULL e
+*/
+static int
+autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
+{
+ autogroup_entry_t **agep = &agi->agi_entry;
+ autogroup_filter_t *agf, *agf_prev = NULL;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ LDAPURLDesc *lud = NULL;
+ Attribute *a;
+ BerValue *bv, dn;
+ int rc = 0, match = 1, null_entry = 0;
+
+ if ( e == NULL ) {
+ if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val );
+ return 1;
+ }
+
+ null_entry = 1;
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
+ e->e_name.bv_val );
+
+ if ( agi->agi_entry != NULL ) {
+ for ( ; *agep ; agep = &(*agep)->age_next ) {
+ dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
+ if ( match == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val );
+ return 1;
+ }
+ /* goto last */;
+ }
+ }
+
+
+ *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
+ ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
+ (*agep)->age_def = agd;
+ (*agep)->age_filter = NULL;
+ (*agep)->age_mustrefresh = 0;
+ (*agep)->age_modrdn_olddnmodified = 0;
+
+ ber_dupbv( &(*agep)->age_dn, &e->e_name );
+ ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
+
+ a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
+
+ if ( null_entry == 1 ) {
+ a = attrs_dup( a );
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ if( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n" );
+ } else {
+ for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
+
+ agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
+
+ if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val );
+ /* FIXME: error? */
+ ch_free( agf );
+ continue;
+ }
+
+ agf->agf_scope = lud->lud_scope;
+
+ if ( lud->lud_dn == NULL ) {
+ BER_BVSTR( &dn, "" );
+ } else {
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ }
+
+ rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val );
+ /* FIXME: error? */
+ goto cleanup;
+ }
+
+ if ( lud->lud_filter != NULL ) {
+ ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
+ agf->agf_filter = str2filter( lud->lud_filter );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val );
+ /* FIXME: error? */
+ goto cleanup;
+ }
+
+ if ( lud->lud_attrs != NULL ) {
+ int i;
+
+ for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
+ /* Just counting */;
+ }
+
+ if ( i > 1 ) {
+ Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
+ bv->bv_val );
+ /* FIXME: error? */
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ ldap_free_urldesc( lud );
+ ch_free( agf );
+ continue;
+ }
+
+ agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
+
+ if ( agf->agf_anlist == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
+ lud->lud_attrs[0] );
+ /* FIXME: error? */
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ ldap_free_urldesc( lud );
+ ch_free( agf );
+ continue;
+ }
+ }
+
+ agf->agf_next = NULL;
+
+ if( (*agep)->age_filter == NULL ) {
+ (*agep)->age_filter = agf;
+ }
+
+ if( agf_prev != NULL ) {
+ agf_prev->agf_next = agf;
+ }
+
+ agf_prev = agf;
+
+ if ( scan == 1 ){
+ autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
+ agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val );
+
+ ldap_free_urldesc( lud );
+
+ continue;
+
+
+cleanup:;
+
+ ch_free( agf->agf_ndn.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ldap_free_urldesc( lud );
+ ch_free( agf );
+ }
+ }
+
+ if ( null_entry == 1 ) {
+ attrs_free( a );
+ }
+ return rc;
+}
+
+/*
+** Used when opening the database to add all existing
+** groups from the database to our internal list.
+*/
+static int
+autogroup_group_add_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
+ }
+
+ return 0;
+}
+
+typedef struct ag_addinfo {
+ slap_overinst *on;
+ Entry *e;
+ autogroup_def_t *agd;
+} ag_addinfo;
+
+static int
+autogroup_add_entry_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ ag_addinfo *aa = sc->sc_private;
+ slap_overinst *on = aa->on;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ BackendInfo *bi = op->o_bd->bd_info;
+
+ if ( rs->sr_err != LDAP_SUCCESS )
+ goto done;
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+ if ( aa->agd ) {
+ autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
+ } else {
+ autogroup_entry_t *age;
+ autogroup_filter_t *agf;
+ struct berval odn, ondn;
+ int rc;
+
+ /* must use rootdn when calling test_filter */
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( age = agi->agi_entry; age ; age = age->age_next ) {
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ /* Check if any of the filters are the suffix to the entry DN.
+ If yes, we can test that filter against the entry. */
+
+ for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ rc = test_filter( op, aa->e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ if ( agf->agf_anlist ) {
+ Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
+ if ( a )
+ autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
+ } else {
+ autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
+ }
+ break;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ }
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+
+ op->o_bd->bd_info = bi;
+
+done:
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** When adding a group, we first strip any existing members,
+** and add all which match the filters ourselves.
+*/
+static int
+autogroup_add_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def;
+ slap_callback *sc = NULL;
+ ag_addinfo *aa = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
+ op->ora_e->e_name.bv_val );
+
+ sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
+ sc->sc_private = (sc+1);
+ sc->sc_response = autogroup_add_entry_cb;
+ aa = sc->sc_private;
+ aa->on = on;
+ aa->e = op->ora_e;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+ /* Check if it's a group. */
+ for ( ; agd ; agd = agd->agd_next ) {
+ if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
+ Modification mod;
+ const char *text = NULL;
+ char textbuf[1024];
+
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_desc = agd->agd_member_ad;
+ mod.sm_type = agd->agd_member_ad->ad_cname;
+ mod.sm_values = NULL;
+ mod.sm_nvalues = NULL;
+
+ /* We don't want any member attributes added by the user. */
+ modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
+
+ aa->agd = agd;
+
+ break;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** agi - internal group and attribute definitions list
+** e - the group to remove from the internal list
+*/
+static int
+autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
+{
+ autogroup_entry_t *age = agi->agi_entry,
+ *age_prev = NULL,
+ *age_next;
+ int rc = 1;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
+ age->age_dn.bv_val );
+
+ for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
+ age_next = age->age_next;
+
+ if ( age == e ) {
+ autogroup_filter_t *agf = age->age_filter,
+ *agf_next;
+
+ if ( age_prev != NULL ) {
+ age_prev->age_next = age_next;
+ } else {
+ agi->agi_entry = NULL;
+ }
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ for( agf_next = agf ; agf_next ; agf = agf_next ){
+ agf_next = agf->agf_next;
+
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ ldap_pvt_thread_mutex_destroy( &age->age_mutex );
+ ch_free( age );
+
+ rc = 0;
+ return rc;
+
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val );
+
+ return rc;
+
+}
+
+static int
+autogroup_delete_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_entry_t *age, *age_prev, *age_next;
+ autogroup_filter_t *agf;
+ Entry *e;
+ int matched_group = 0, rc = 0;
+ struct berval odn, ondn;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val );
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* Check if the entry to be deleted is one of our groups. */
+ for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
+ age = age_next;
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+ age_next = age->age_next;
+
+ if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
+ int match = 1;
+
+ matched_group = 1;
+
+ dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
+
+ if ( match == 0 ) {
+ autogroup_delete_group( agi, age );
+ break;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ if ( matched_group == 1 ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* Check if the entry matches any of the groups.
+ If yes, we can delete the entry from that group. */
+
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ rc = test_filter( op, e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ /* If the attribute is retrieved from the entry, we don't know what to delete
+ ** So the group must be entirely refreshed
+ ** But the refresh can't be done now because the entry is not deleted
+ ** So the group is marked as mustrefresh
+ */
+ if ( agf->agf_anlist ) {
+ age->age_mustrefresh = 1;
+ } else {
+ autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
+ }
+ break;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+autogroup_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def;
+ autogroup_entry_t *age;
+ autogroup_filter_t *agf;
+ BerValue new_dn, new_ndn, pdn;
+ Entry *e, *group;
+ Attribute *a, *ea, *attrs;
+ int is_olddn, is_newdn, is_value_refresh, dn_equal;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ break;
+ }
+
+ /* Handle all cases where a refresh of the group is needed */
+ if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
+ if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ /* Request detected that the group must be refreshed */
+
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ if ( age->age_mustrefresh ) {
+ autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ }
+ } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
+ if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val );
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( op->oq_modrdn.rs_newSup ) {
+ pdn = *op->oq_modrdn.rs_newSup;
+ } else {
+ dnParent( &op->o_req_dn, &pdn );
+ }
+ build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
+
+ if ( op->oq_modrdn.rs_nnewSup ) {
+ pdn = *op->oq_modrdn.rs_nnewSup;
+ } else {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+ build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val );
+
+ dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
+
+ if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
+
+
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val );
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+
+ /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
+ for ( ; agd; agd = agd->agd_next ) {
+
+ if ( value_find_ex( slap_schema.si_ad_objectClass,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &agd->agd_oc->soc_cname,
+ op->o_tmpmemctx ) == 0 )
+ {
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ int match = 1;
+
+ dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
+ if ( match == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val );
+ ber_dupbv( &age->age_dn, &new_dn );
+ ber_dupbv( &age->age_ndn, &new_ndn );
+
+ op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ }
+ }
+
+ /* For each group:
+ 1. check if the original entry's DN is in the group.
+ 2. check if the any of the group filter's base DN is a suffix of the new DN
+
+ If 1 and 2 are both false, we do nothing.
+ If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
+ If 1 is false, and 2 is true, we check the entry against the group's filters,
+ and add it's DN to the group.
+ If 1 is true, and 2 is false, we delete the entry's DN from the group.
+ */
+ attrs = attrs_dup( e->e_attrs );
+ overlay_entry_release_ov( op, e, 0, on );
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ is_olddn = 0;
+ is_newdn = 0;
+ is_value_refresh = 0;
+
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ if ( age->age_filter && age->age_filter->agf_anlist ) {
+ ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
+ }
+ else {
+ ea = NULL;
+ }
+
+ if ( age->age_modrdn_olddnmodified ) {
+ /* Request already marked this group to be updated */
+ is_olddn = 1;
+ is_value_refresh = 1;
+ age->age_modrdn_olddnmodified = 0;
+ } else {
+
+ if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
+ LDAP_SUCCESS || group == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val );
+
+ op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+
+ attrs_free( attrs );
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
+
+ if ( a != NULL ) {
+ if ( value_find_ex( age->age_def->agd_member_ad,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
+ {
+ is_olddn = 1;
+ }
+
+ }
+
+ overlay_entry_release_ov( op, group, 0, on );
+
+ }
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
+ /* TODO: should retest filter as it could imply conditions on the dn */
+ is_newdn = 1;
+ break;
+ }
+ }
+
+
+ if ( is_value_refresh ) {
+ if ( is_olddn != is_newdn ) {
+ /* group refresh */
+ autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ continue;
+ }
+ if ( is_olddn == 1 && is_newdn == 0 ) {
+ if ( ea )
+ autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
+ else
+ autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ } else
+ if ( is_olddn == 0 && is_newdn == 1 ) {
+ Entry etmp;
+ struct berval odn, ondn;
+ etmp.e_name = op->o_req_dn;
+ etmp.e_nname = op->o_req_ndn;
+ etmp.e_attrs = attrs;
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
+ if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
+ if ( ea ) {
+ autogroup_add_member_values_to_group( op, &new_dn, age, ea );
+ } else
+ autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+ break;
+ }
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ } else
+ if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
+ if ( ea ) {
+ /* group refresh */
+ autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+ }
+ }
+ else {
+ autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+
+ attrs_free( attrs );
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ }
+ }
+
+ if ( op->o_tag == LDAP_REQ_MODIFY ) {
+ if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
+ Entry etmp;
+ struct berval odn, ondn;
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val );
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
+
+
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val );
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* If we modify a group's memberURL, we have to delete all of it's members,
+ and add them anew, because we cannot tell from which memberURL a member was added. */
+ for ( ; agd; agd = agd->agd_next ) {
+
+ if ( value_find_ex( slap_schema.si_ad_objectClass,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &agd->agd_oc->soc_cname,
+ op->o_tmpmemctx ) == 0 )
+ {
+ Modifications *m;
+ int match = 1;
+
+ m = op->orm_modlist;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
+
+ if ( match == 0 ) {
+ for ( ; m ; m = m->sml_next ) {
+ if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
+ autogroup_def_t *group_agd = age->age_def;
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
+ op->o_req_dn.bv_val );
+
+ overlay_entry_release_ov( op, e, 0, on );
+
+ autogroup_delete_member_from_group( op, NULL, NULL, age );
+ autogroup_delete_group( agi, age );
+
+ autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ /* When modifying any of the attributes of an entry, we must
+ check if the entry is in any of our groups, and if
+ the modified entry matches any of the filters of that group.
+
+ If the entry exists in a group, but the modified attributes do
+ not match any of the group's filters, we delete the entry from that group.
+ If the entry doesn't exist in a group, but matches a filter,
+ we add it to that group.
+ */
+ attrs = attrs_dup( e->e_attrs );
+ overlay_entry_release_ov( op, e, 0, on );
+ etmp.e_name = op->o_req_dn;
+ etmp.e_nname = op->o_req_ndn;
+ etmp.e_attrs = attrs;
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ is_olddn = 0;
+ is_newdn = 0;
+
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ if ( age->age_filter && age->age_filter->agf_anlist ) {
+ ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
+ }
+ else {
+ ea = NULL;
+ }
+
+ if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
+ LDAP_SUCCESS || group == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
+ age->age_dn.bv_val );
+
+ attrs_free( attrs );
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
+
+ if ( a != NULL ) {
+ if ( value_find_ex( age->age_def->agd_member_ad,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
+ {
+ is_olddn = 1;
+ }
+
+ }
+
+ overlay_entry_release_ov( op, group, 0, on );
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
+ is_newdn = 1;
+ break;
+ }
+ }
+ }
+
+ if ( is_olddn == 1 && is_newdn == 0 ) {
+ if(ea)
+ autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
+ else
+ autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ } else
+ if ( is_olddn == 0 && is_newdn == 1 ) {
+ if(ea)
+ autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
+ else
+ autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ attrs_free( attrs );
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** Detect if filter contains a memberOf check for dn
+*/
+static int
+autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
+{
+ int result = 0;
+ if ( f == NULL ) return 0;
+
+ switch ( f->f_choice & SLAPD_FILTER_MASK ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
+ result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
+ }
+ break;
+ case LDAP_FILTER_EQUALITY:
+ result = ( f->f_ava->aa_desc == memberof_ad &&
+ ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/*
+** When modifying a group, we must deny any modifications to the member attribute,
+** because the group would be inconsistent.
+*/
+static int
+autogroup_modify_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def;
+ autogroup_entry_t *age;
+ Entry *e;
+ Attribute *a;
+ struct berval odn, ondn;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
+ for ( age = agi->agi_entry; age ; age = age->age_next ) {
+ autogroup_filter_t *agf;
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( agf->agf_anlist ) {
+ Modifications *m;
+ for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
+ if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ int rc = test_filter( op, e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ age->age_mustrefresh = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
+ age->age_mustrefresh = 1;
+ }
+ }
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+
+ a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
+
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+
+ for ( ; agd; agd = agd->agd_next ) {
+
+ if ( value_find_ex( slap_schema.si_ad_objectClass,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &agd->agd_oc->soc_cname,
+ op->o_tmpmemctx ) == 0 )
+ {
+ Modifications *m;
+ int match = 1;
+
+ m = op->orm_modlist;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
+
+ if ( match == 0 ) {
+ for ( ; m ; m = m->sml_next ) {
+ if ( m->sml_desc == age->age_def->agd_member_ad ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val );
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+ break;
+ }
+ }
+
+ /* an entry may only have one dynamic group class */
+ break;
+ }
+ }
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** Detect if the olddn is part of a group and so if the group should be refreshed
+*/
+static int
+autogroup_modrdn_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_entry_t *age;
+ Entry *e;
+ struct berval odn, ondn;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ /* Must check if a dn is modified */
+ for ( age = agi->agi_entry; age ; age = age->age_next ) {
+ autogroup_filter_t *agf;
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( agf->agf_anlist ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ int rc = test_filter( op, e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ age->age_modrdn_olddnmodified = 1;
+ }
+ }
+ }
+ }
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** Builds a filter for searching for the
+** group entries, according to the objectClass.
+*/
+static int
+autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
+{
+ char *ptr;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n" );
+
+ op->ors_filterstr.bv_len = STRLENOF( "(=)" )
+ + slap_schema.si_ad_objectClass->ad_cname.bv_len
+ + agd->agd_oc->soc_cname.bv_len;
+ ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ *ptr++ = '(';
+ ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
+ *ptr++ = '=';
+ ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
+ *ptr++ = ')';
+ *ptr = '\0';
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+
+ assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
+
+ return 0;
+}
+
+enum {
+ AG_ATTRSET = 1,
+ AG_MEMBER_OF_AD,
+ AG_LAST
+};
+
+static ConfigDriver ag_cfgen;
+
+static ConfigTable agcfg[] = {
+ { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
+ 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
+ "( OLcfgCtAt:2.1 NAME ( 'olcAutoGroupAttrSet' 'olcAGattrSet' ) "
+ "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "autogroup-memberof-ad", "memberOf attribute",
+ 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
+ "( OLcfgCtAt:2.2 NAME ( 'olcAutoGroupMemberOfAd' 'olcAGmemberOfAd' ) "
+ "DESC 'memberOf attribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs agocs[] = {
+ { "( OLcfgCtOc:2.1 "
+ "NAME ( 'olcAutoGroupConfig' 'olcAutomaticGroups' ) "
+ "DESC 'Automatic groups configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcAutoGroupAttrSet "
+ "$ olcAutoGroupMemberOfAd "
+ ")"
+ ")",
+ Cft_Overlay, agcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+
+static int
+ag_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd;
+ autogroup_entry_t *age;
+
+ int rc = 0, i;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n" );
+
+ if( agi == NULL ) {
+ agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
+ ldap_pvt_thread_mutex_init( &agi->agi_mutex );
+ agi->agi_def = NULL;
+ agi->agi_entry = NULL;
+ on->on_bi.bi_private = (void *)agi;
+ }
+
+ agd = agi->agi_def;
+ age = agi->agi_entry;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+
+ switch( c->type ){
+ case AG_ATTRSET:
+ for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
+ struct berval bv;
+ char *ptr = c->cr_msg;
+
+ assert(agd->agd_oc != NULL);
+ assert(agd->agd_member_url_ad != NULL);
+ assert(agd->agd_member_ad != NULL);
+
+ ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s %s %s", i,
+ agd->agd_oc->soc_cname.bv_val,
+ agd->agd_member_url_ad->ad_cname.bv_val,
+ agd->agd_member_ad->ad_cname.bv_val );
+
+ bv.bv_val = c->cr_msg;
+ bv.bv_len = ptr - bv.bv_val;
+ value_add_one ( &c->rvalue_vals, &bv );
+
+ }
+ break;
+
+ case AG_MEMBER_OF_AD:
+ if ( agi->agi_memberof_ad != NULL ){
+ value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+
+ }else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0) {
+ autogroup_def_t *agd_next;
+ autogroup_entry_t *age_next;
+ autogroup_filter_t *agf = age->age_filter,
+ *agf_next;
+
+ for ( agd_next = agd; agd_next; agd = agd_next ) {
+ agd_next = agd->agd_next;
+
+ ch_free( agd );
+ }
+
+ for ( age_next = age ; age_next ; age = age_next ) {
+ age_next = age->age_next;
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ for( agf_next = agf ; agf_next ; agf = agf_next ){
+ agf_next = agf->agf_next;
+
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_init( &age->age_mutex );
+ ch_free( age );
+ }
+
+ ch_free( agi );
+ on->on_bi.bi_private = NULL;
+
+ } else {
+ autogroup_def_t **agdp;
+ autogroup_entry_t *age_next, *age_prev;
+ autogroup_filter_t *agf,
+ *agf_next;
+
+ for ( i = 0, agdp = &agi->agi_def;
+ i < c->valx; i++ )
+ {
+ if ( *agdp == NULL) {
+ return 1;
+ }
+ agdp = &(*agdp)->agd_next;
+ }
+
+ agd = *agdp;
+ *agdp = agd->agd_next;
+
+ for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
+ age_next = age->age_next;
+
+ if( age->age_def == agd ) {
+ agf = age->age_filter;
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ for ( agf_next = agf; agf_next ; agf = agf_next ) {
+ agf_next = agf->agf_next;
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &age->age_mutex );
+ ch_free( age );
+
+ age = age_prev;
+
+ if( age_prev != NULL ) {
+ age_prev->age_next = age_next;
+ }
+ }
+ }
+
+ ch_free( agd );
+ agd = agi->agi_def;
+
+ }
+
+ return rc;
+ }
+
+ switch(c->type){
+ case AG_ATTRSET: {
+ autogroup_def_t **agdp,
+ *agd_next = NULL;
+ ObjectClass *oc = NULL;
+ AttributeDescription *member_url_ad = NULL,
+ *member_ad = NULL;
+ const char *text;
+
+
+ oc = oc_find( c->argv[ 1 ] );
+ if( oc == NULL ){
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "unable to find ObjectClass \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+
+ rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "AttributeDescription \"%s\" ",
+ "must be of a subtype \"labeledURI\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ rc = slap_str2ad( c->argv[3], &member_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 3 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
+ /* The same URL attribute / member attribute pair
+ * cannot be repeated */
+
+ if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "URL attributeDescription \"%s\" already mapped",
+ member_ad->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+/* return 1; //warning*/
+ }
+ }
+
+ if ( c->valx >= 0 ) {
+ int i;
+
+ for ( i = 0, agdp = &agi->agi_def ;
+ i < c->valx; i++ )
+ {
+ if ( *agdp == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "invalid index {%d}",
+ c->valx );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+
+ return 1;
+ }
+ agdp = &(*agdp)->agd_next;
+ }
+ agd_next = *agdp;
+
+ } else {
+ for ( agdp = &agi->agi_def; *agdp;
+ agdp = &(*agdp)->agd_next )
+ /* goto last */;
+ }
+
+ *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
+
+ (*agdp)->agd_oc = oc;
+ (*agdp)->agd_member_url_ad = member_url_ad;
+ (*agdp)->agd_member_ad = member_ad;
+ (*agdp)->agd_next = agd_next;
+
+ } break;
+
+ case AG_MEMBER_OF_AD: {
+ AttributeDescription *memberof_ad = NULL;
+ const char *text;
+
+ rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-memberof-ad <memberof-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
+ && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "memberof attribute=\"%s\" must either "
+ "have DN (%s) or nameUID (%s) syntax",
+ c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ agi->agi_memberof_ad = memberof_ad;
+
+ } break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+extern int slapMode;
+
+/*
+** Do a search for all the groups in the
+** database, and add them to out internal list.
+*/
+static int
+autogroup_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ autogroup_info_t *agi = on->on_bi.bi_private;
+ autogroup_def_t *agd;
+ autogroup_sc_t ags;
+ Operation *op;
+ slap_callback cb = { 0 };
+
+ void *thrctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n" );
+
+ if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
+ return 0;
+ }
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->ors_attrsonly = 0;
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+
+ op->o_req_dn = be->be_suffix[0];
+ op->o_req_ndn = be->be_nsuffix[0];
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_limit = NULL;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->o_do_not_cache = 1;
+
+ op->o_bd = be;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+ ags.ags_info = agi;
+ cb.sc_private = &ags;
+ cb.sc_response = autogroup_group_add_cb;
+ cb.sc_cleanup = NULL;
+ cb.sc_next = NULL;
+
+ op->o_callback = &cb;
+
+ for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
+ SlapReply rs = { REP_RESULT };
+
+ autogroup_build_def_filter(agd, op);
+
+ ags.ags_def = agd;
+
+ op->o_bd->be_search( op, &rs );
+
+ filter_free_x( op, op->ors_filter, 1 );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ if( ! agi->agi_memberof_ad ){
+ int rc;
+ const char *text = NULL;
+
+ rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
+ "unable to find attribute=\"%s\": %s (%d)\n",
+ SLAPD_MEMBEROF_ATTR, text, rc );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int
+autogroup_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n" );
+
+ if ( on->on_bi.bi_private ) {
+ autogroup_info_t *agi = on->on_bi.bi_private;
+ autogroup_entry_t *age = agi->agi_entry,
+ *age_next;
+ autogroup_filter_t *agf, *agf_next;
+
+ for ( age_next = age; age_next; age = age_next ) {
+ age_next = age->age_next;
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ agf = age->age_filter;
+
+ for ( agf_next = agf; agf_next; agf = agf_next ) {
+ agf_next = agf->agf_next;
+
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &age->age_mutex );
+ ch_free( age );
+ }
+ }
+
+ return 0;
+}
+
+static int
+autogroup_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n" );
+
+ if ( on->on_bi.bi_private ) {
+ autogroup_info_t *agi = on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def,
+ *agd_next;
+
+ for ( agd_next = agd; agd_next; agd = agd_next ) {
+ agd_next = agd->agd_next;
+
+ ch_free( agd );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
+ ch_free( agi );
+ }
+
+ return 0;
+}
+
+static
+int
+autogroup_initialize(void)
+{
+ int rc = 0;
+ autogroup.on_bi.bi_type = "autogroup";
+
+ autogroup.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ autogroup.on_bi.bi_db_open = autogroup_db_open;
+ autogroup.on_bi.bi_db_close = autogroup_db_close;
+ autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
+
+ autogroup.on_bi.bi_op_add = autogroup_add_entry;
+ autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
+ autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
+ autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
+
+ autogroup.on_response = autogroup_response;
+
+ autogroup.on_bi.bi_cf_ocs = agocs;
+
+ rc = config_register_schema( agcfg, agocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &autogroup );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return autogroup_initialize();
+}
diff --git a/contrib/slapd-modules/autogroup/slapo-autogroup.5 b/contrib/slapd-modules/autogroup/slapo-autogroup.5
new file mode 100644
index 0000000..4c6414d
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/slapo-autogroup.5
@@ -0,0 +1,116 @@
+.TH SLAPO-AUTOGROUP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Portions Copyright \[u00A9] 2007 Michał Szulczyński.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+\FCslapo-autogroup\FT \- automatic updates of group memberships which meet the
+requirements of any filter contained in the group definition.
+.SH SYNOPSIS
+In \FCslapd.conf\FT:
+ ...
+ \FCinclude ETCDIR/schema/dyngroup.schema\FT
+ ...
+ \FCmoduleload autogroup.so\FT
+ ...
+ \FCdatabase\FT ...
+ ...
+ \FCoverlay autogroup\FT
+ \FCautogroup-attrset groupOfURLs memberURL member\FT
+.SH DESCRIPTION
+The
+.B autogroup
+overlay to
+.BR slapd (8)
+allows automated updates of group memberships which meet the requirements
+of any filter contained in the group definition. The filters are built from
+LDAP URI-valued attributes. Any time an object is added/deleted/updated, it is
+tested for compliance with the filters, and its membership is accordingly
+updated. For searches and compares, it behaves like a static group.
+If the attribute part of the URI is filled, the group entry is populated by
+the values of this attribute in the entries resulting from the search.
+.SH CONFIGURATION
+Either
+.BR \FCslapd.conf\FT (5)
+or the \FCcn=config\FT methodology of
+.BR \FCslapd-config\FT (5)
+may be used for configuring autogroup. Both syntaxes are provided
+here for convenience:
+.TP
+.B \FCautogroup-attrset\FT <group-oc> <URL-ad> <member-ad>
+.TP
+.B \FColcAutoGroupAttrSet:\FT <group-oc> <URL-ad> <member-ad>
+This defines the objectclass-attribute-URI mappings defining the
+automatically managed groups, and may appear multiple times.
+
+The value <group-oc> is the name of the objectClass that represents
+the group.
+
+The value <URL-ad> is the name of the attributeDescription that
+contains the URI that is converted to the filters. If no URI is
+present, there will be no members in that group. It must be a subtype
+of labeledURI.
+
+The value <member-ad> is the name of the attributeDescription that
+specifies the member attribute. User modification of this attribute is
+disabled for consistency.
+.TP
+.B \FCautogroup-memberof-ad\FT <memberof-ad>
+.TP
+.B \FColcAutoGroupMemberOfAd\FT <memberof-ad>
+This defines the attribute that is used by the memberOf overlay to
+store the names of groups that an entry is member of; it must be
+DN-valued. It should be set to the same value as
+memberof-memberof-ad. It defaults to 'memberOf'.
+.SH EXAMPLES
+As above in SYNOPSIS, or with memberof:
+
+ ...
+ \FCinclude ETCDIR/schema/dyngroup.schema\FT
+ \FCinclude ETCDIR/schema/memberof.schema\FT
+ ...
+ \FCmoduleload autogroup.so\FT
+ \FCmoduleload memberof.so\FT
+ ...
+ \FCdatabase\FT ...
+ ...
+ \FCoverlay memberof\FT
+ \FCmemberof-memberof-ad\FT foo
+ ...
+ \FCoverlay autogroup\FT
+ \FCautogroup-attrset groupOfURLs memberURL member\FT
+ \FCautogroup-memberof-ad\FT foo
+.SH CAVEATS
+As with static groups, update operations on groups with a large number
+of members may be slow. If the attribute part of the URI is specified,
+modify and delete operations are more difficult to handle. In these
+cases the overlay will try to detect if groups have been modified and
+then simply refresh them. This can cause performance hits if the
+search specified by the URI deals with a significant number of
+entries.
+.SH BACKWARD COMPATIBILITY
+The autogroup overlay has been reworked with the 2.5 release to use
+a consistent namespace as with other overlays. As a side-effect the
+following cn=config parameters are deprecated and will be removed in
+a future release:
+.IP \[bu] 2
+.B olcAGattrSet
+is replaced with olcAutoGroupAttrSet
+.IP \[bu]
+.B olcAGmemberOfAd
+is replaced with olcAutoGroupMemberOfAd
+.IP \[bu]
+.B olcAutomaticGroups
+is replaced with olcAutoGroupConfig
+.SH ACKNOWLEDGEMENTS
+This module was originally written in 2007 by Michał
+Szulczyński. Further enhancements were contributed by Howard
+Chu, Raphael Ouazana, Norbert Pueschel, and Christian Manal. Manpage
+updates provided by Emily Backes.
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+.SH Copyrights
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright \[u00A9] 2007 Michał Szulczyński.
+All rights reserved.
diff --git a/contrib/slapd-modules/cloak/Makefile b/contrib/slapd-modules/cloak/Makefile
new file mode 100644
index 0000000..c54e6b1
--- /dev/null
+++ b/contrib/slapd-modules/cloak/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_CLOAK=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = cloak.la
+MANPAGES = slapo-cloak.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+cloak.la: cloak.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/cloak/cloak.c b/contrib/slapd-modules/cloak/cloak.c
new file mode 100644
index 0000000..ced7a80
--- /dev/null
+++ b/contrib/slapd-modules/cloak/cloak.c
@@ -0,0 +1,354 @@
+/* cloak.c - Overlay to hide some attribute except if explicitly requested */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Emmanuel Dreyfus
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the Emmanuel Dreyfus for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_CLOAK
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+enum { CLOAK_ATTR = 1 };
+
+typedef struct cloak_info_t {
+ ObjectClass *ci_oc;
+ AttributeDescription *ci_ad;
+ struct cloak_info_t *ci_next;
+} cloak_info_t;
+
+#define CLOAK_USAGE "\"cloak-attr <attr> [<class>]\": "
+
+static int
+cloak_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ cloak_info_t *ci = (cloak_info_t *)on->on_bi.bi_private;
+
+ int rc = 0, i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case CLOAK_ATTR:
+ for ( i = 0; ci; i++, ci = ci->ci_next ) {
+ struct berval bv;
+ int len;
+
+ assert( ci->ci_ad != NULL );
+
+ if ( ci->ci_oc != NULL )
+ len = snprintf( c->cr_msg,
+ sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s %s", i,
+ ci->ci_ad->ad_cname.bv_val,
+ ci->ci_oc->soc_cname.bv_val );
+ else
+ len = snprintf( c->cr_msg,
+ sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s", i,
+ ci->ci_ad->ad_cname.bv_val );
+
+ bv.bv_val = c->cr_msg;
+ bv.bv_len = len;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ cloak_info_t *ci_next;
+
+ switch( c->type ) {
+ case CLOAK_ATTR:
+ for ( ci_next = ci, i = 0;
+ ci_next, c->valx < 0 || i < c->valx;
+ ci = ci_next, i++ ){
+
+ ci_next = ci->ci_next;
+
+ ch_free ( ci->ci_ad );
+ if ( ci->ci_oc != NULL )
+ ch_free ( ci->ci_oc );
+
+ ch_free( ci );
+ }
+ ci = (cloak_info_t *)on->on_bi.bi_private;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ }
+
+ switch( c->type ) {
+ case CLOAK_ATTR: {
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL;
+ const char *text;
+ cloak_info_t **cip = NULL;
+ cloak_info_t *ci_next = NULL;
+
+ if ( c->argc == 3 ) {
+ oc = oc_find( c->argv[ 2 ] );
+ if ( oc == NULL ) {
+ snprintf( c->cr_msg,
+ sizeof( c->cr_msg ),
+ CLOAK_USAGE
+ "unable to find ObjectClass \"%s\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), CLOAK_USAGE
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( i = 0, cip = (cloak_info_t **)&on->on_bi.bi_private;
+ c->valx < 0 || i < c->valx, *cip;
+ i++, cip = &(*cip)->ci_next ) {
+ if ( c->valx >= 0 && *cip == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ CLOAK_USAGE
+ "invalid index {%d}\n",
+ c->valx );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ ci_next = *cip;
+ }
+
+ *cip = (cloak_info_t *)SLAP_CALLOC( 1, sizeof( cloak_info_t ) );
+ (*cip)->ci_oc = oc;
+ (*cip)->ci_ad = ad;
+ (*cip)->ci_next = ci_next;
+
+ rc = 0;
+ break;
+ }
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+cloak_search_response_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc;
+ cloak_info_t *ci;
+ Entry *e = NULL;
+ Entry *me = NULL;
+
+ assert( op && op->o_callback && rs );
+
+ if ( rs->sr_type != REP_SEARCH || !rs->sr_entry ) {
+ return ( SLAP_CB_CONTINUE );
+ }
+
+ sc = op->o_callback;
+ e = rs->sr_entry;
+
+ /*
+ * First perform a quick scan for an attribute to cloak
+ */
+ for ( ci = (cloak_info_t *)sc->sc_private; ci; ci = ci->ci_next ) {
+ Attribute *a;
+
+ if ( ci->ci_oc != NULL &&
+ !is_entry_objectclass_or_sub( e, ci->ci_oc ) )
+ continue;
+
+ for ( a = e->e_attrs; a; a = a->a_next )
+ if ( a->a_desc == ci->ci_ad )
+ break;
+
+ if ( a != NULL )
+ break;
+ }
+
+ /*
+ * Nothing found to cloak
+ */
+ if ( ci == NULL )
+ return ( SLAP_CB_CONTINUE );
+
+ /*
+ * We are now committed to cloak an attribute.
+ */
+ rs_entry2modifiable( op, rs, (slap_overinst *) op->o_bd->bd_info );
+ me = rs->sr_entry;
+
+ for ( ci = (cloak_info_t *)sc->sc_private; ci; ci = ci->ci_next ) {
+ Attribute *a;
+ Attribute *pa;
+
+ for ( pa = NULL, a = me->e_attrs;
+ a;
+ pa = a, a = a->a_next ) {
+
+ if ( a->a_desc != ci->ci_ad )
+ continue;
+
+ Debug( LDAP_DEBUG_TRACE, "cloak_search_response_cb: cloak %s\n",
+ a->a_desc->ad_cname.bv_val );
+
+ if ( pa != NULL )
+ pa->a_next = a->a_next;
+ else
+ me->e_attrs = a->a_next;
+
+ attr_clean( a );
+ }
+
+ }
+
+ return ( SLAP_CB_CONTINUE );
+}
+
+static int
+cloak_search_cleanup_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err != LDAP_SUCCESS ) {
+ slap_freeself_cb( op, rs );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+cloak_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cloak_info_t *ci = (cloak_info_t *)on->on_bi.bi_private;
+ slap_callback *sc;
+
+ if ( op->ors_attrsonly ||
+ op->ors_attrs ||
+ get_manageDSAit( op ) )
+ return SLAP_CB_CONTINUE;
+
+ sc = op->o_tmpcalloc( 1, sizeof( *sc ), op->o_tmpmemctx );
+ sc->sc_response = cloak_search_response_cb;
+ sc->sc_cleanup = cloak_search_cleanup_cb;
+ sc->sc_next = op->o_callback;
+ sc->sc_private = ci;
+ op->o_callback = sc;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst cloak_ovl;
+
+static ConfigTable cloakcfg[] = {
+ { "cloak-attr", "attribute [class]",
+ 2, 3, 0, ARG_MAGIC|CLOAK_ATTR, cloak_cfgen,
+ "( OLcfgCtAt:4.1 NAME 'olcCloakAttribute' "
+ "DESC 'Cloaked attribute: attribute [class]' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static int
+cloak_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cloak_info_t *ci = (cloak_info_t *)on->on_bi.bi_private;
+
+ for ( ; ci; ) {
+ cloak_info_t *tmp = ci;
+ ci = ci->ci_next;
+ SLAP_FREE( tmp );
+ }
+
+ on->on_bi.bi_private = NULL;
+
+ return 0;
+}
+
+static ConfigOCs cloakocs[] = {
+ { "( OLcfgCtOc:4.1 "
+ "NAME 'olcCloakConfig' "
+ "DESC 'Attribute cloak configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcCloakAttribute ) )",
+ Cft_Overlay, cloakcfg },
+ { NULL, 0, NULL }
+};
+
+#if SLAPD_OVER_CLOAK == SLAPD_MOD_DYNAMIC
+static
+#endif
+int
+cloak_initialize( void ) {
+ int rc;
+ cloak_ovl.on_bi.bi_type = "cloak";
+ cloak_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ cloak_ovl.on_bi.bi_db_destroy = cloak_db_destroy;
+ cloak_ovl.on_bi.bi_op_search = cloak_search;
+ cloak_ovl.on_bi.bi_cf_ocs = cloakocs;
+
+ rc = config_register_schema ( cloakcfg, cloakocs );
+ if ( rc )
+ return rc;
+
+ return overlay_register( &cloak_ovl );
+}
+
+#if SLAPD_OVER_CLOAK == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return cloak_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_CLOAK) */
+
diff --git a/contrib/slapd-modules/cloak/slapo-cloak.5 b/contrib/slapd-modules/cloak/slapo-cloak.5
new file mode 100644
index 0000000..2655d2e
--- /dev/null
+++ b/contrib/slapd-modules/cloak/slapo-cloak.5
@@ -0,0 +1,82 @@
+.TH SLAPO-CLOAK 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-cloak \- Attribute cloak overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B cloak
+overlay to
+.BR slapd (8)
+allows the server to hide specific attributes, unless explicitly requested
+by the client. This improve performance when a client requests all attributes
+and get a huge binary attribute that is of no interest for it.
+This behavior is disabled when the \fImanageDSAit\fP
+control (RFC 3296) is used.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B cloak
+overlay must be prefixed by
+.BR cloak\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay cloak
+This directive adds the cloak overlay to the current database,
+or to the frontend, if used before any database instantiation; see
+.BR slapd.conf (5)
+for details.
+
+.LP
+This
+.B slapd.conf
+configuration option is defined for the cloak overlay. It may have multiple
+occurrences, and it must appear after the
+.B overlay
+directive:
+.TP
+.B cloak-attr <attribute> [<class>]
+The value
+.B <attribute>
+is the name of the attribute that will be cloaked.
+
+The optional
+.B <class>
+restricts cloaking only to entries of the named
+.B <class>.
+
+.SH EXAMPLE
+This example hide the
+.B jpegPhoto
+attribute. Add the following to slapd.conf:
+
+.LP
+.nf
+ database <database>
+ # ...
+
+ overlay cloak
+ cloak-attr jpegPhoto
+.fi
+.LP
+and that slapd loads cloak.la, if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-cloak (5)
+overlay supports dynamic configuration via
+.BR back-config .
+.SH ACKNOWLEDGEMENTS
+.P
+This module was originally written in 2008 by Emmanuel Dreyfus.
diff --git a/contrib/slapd-modules/comp_match/Makefile b/contrib/slapd-modules/comp_match/Makefile
new file mode 100644
index 0000000..9b78c5c
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/Makefile
@@ -0,0 +1,69 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2003-2022 The OpenLDAP Foundation.
+# Portions Copyright 2004 by IBM Corporation.
+# All rights reserved.
+
+# Copyright 2004 Sang Seok Lim, IBM Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+SNACC_DIR = ../$(LDAP_SRC)/snacc
+SNACC_INC = -I$(SNACC_DIR) -I$(SNACC_DIR)/c-lib/inc
+SNACC_LIB = $(SNACC_DIR)/c-lib/libcasn1.a
+
+SSL_DIR = /usr/local
+SSL_INC = -I$(SSL_DIR)/include/openssl
+SSL_LIB = -lcrypto -L$(SSL_DIR)/lib
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DLDAP_COMPONENT
+INCS = $(LDAP_INC) $(SNACC_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(SNACC_LIB) $(SSL_LIB)
+
+PROGRAMS = compmatch.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+compmatch.la: componentlib.lo init.lo certificate.lo asn_to_syn_mr.lo authorityKeyIdentifier.lo crl.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/comp_match/README b/contrib/slapd-modules/comp_match/README
new file mode 100644
index 0000000..133757c
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/README
@@ -0,0 +1,127 @@
+Copyright 2004 Sang Seok Lim, IBM . 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>.
+
+This directory contains a Component Matching module and
+a X.509 Certificate example. In order to understand Component
+Matching, see RFC 3687 and
+http://www.openldap.org/conf/odd-sandiego-2004/Sangseok.pdf
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+A) Brief introduction about files in this directory
+%%%%%%%%%%55%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+1) init.c
+module_init() and functions which are dynamically linked
+into the main slapd codes.
+
+2) componentlib.c and componentlib.h
+GSER and BER decoder library of each primitive ASN.1 type.
+They use component representation to store ASN.1 values.
+
+3) certificate.c/.h authorityKeyIdentifier.c/.h
+eSNACC generated BER and GSER decoder routines of the X.509
+certificate specification and one of its extensions,
+authorityKeyIdentifier.
+
+4) asn_to_syn_mr.c asn.h
+An mapping table from ASN.1 types to corresponding Syntaxes,
+matching rules, and component description in slapd.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+B) How to use Component Matching on X.509 certificates
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+1) be sure to configure slapd with enable-modules on.
+2) install the GSER-support eSNACC compiler. You can find
+only in www.openldap.org. At least, you need the library
+(libcasn1.a) and header files for compiling this module.
+3) modify Makefile accordingly. then run make.
+you will get compmatch.la and other necessary files in ./libs
+4) modify slapd.conf to include the following module command
+ moduleload <path to>compmatch.la
+5) run slapd and perform search operations against
+the attribute, userCertificate. You need to read through
+RFC 3687 in order to understand how to compose component
+filters.
+Ex) component search filter examples
+"(userCertificate:componentFilterMatch:=item:{ component
+\"toBeSigned.serialNumber\", rule integerMatch, value 2 })"
+You can find more examples in "test031-component-filter"
+in the OpenLDAP source directory.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+C) How to add a new ASN.1 syntax
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+1) download and install the eSNACC compiler supporting
+Component Matching. You can find the compiler only in
+www.openldap.org. Before compiling, be sure to define
+the "LDAP_COMPONENT" macro to obtain component
+supported version of C library and back-ends of eSNACC.
+Otherwise compiled library will fail to be linked to
+the module.
+2) using eSNACC, compile your ASN.1 specifications and
+copy the generated .c and .h files to this directory
+Ex)
+$ esnacc -E BER_COMP -E GSER -t -d -f example.asn
+For Component Matching, set BOTH BER_COMP and GSER on.
+After compiling, you will get example.c and example.h
+3) modify example.c accordingly, seeing certificate.c
+and certificate.asn as a reference.
+- add init_module_xxx() located in generated .c file
+into init_module() in init.c.
+- modify the arguments of InstallOidDecoderMapping(...)
+accordingly
+- in the generated .c file, you need to write
+"DecComponentxxxTop(...)" function for yourself.
+You can copy BDecComponentCertificateTop in the
+generated .c file and modify it accordingly.
+4) register a new attribute syntax with a new OID
+in a schema file
+5) then goto 3) of B) section.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D) How to configure Component Indexing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+You can generate indices on each component of
+a given attribute whose values are in either GSER or
+BER. Currently primitive ASN.1 types, DN, and RDN
+can be indexed for equality matching in BDB.
+In order to generate indices, put following line
+in the slapd configuration file, slapd.conf.
+
+index [attribute name].[component reference] eq
+
+Ex)
+index userCertificate eq
+index userCertificate.toBeSigned.issuer.rdnSequence eq
+index userCertificate.toBeSigned.serialNumber eq
+index userCertificate.toBeSigned.version eq
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D) How to configure Attribute Alias
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+If your client is unable to use component filters,
+attribute aliasing can be used instead. Attribute
+Alias maps a virtual attribute type to an attribute
+component and a component matching rule.
+You can create your own aliases by following steps.
+
+1) register aliasing attributes in the schema file.
+Sample aliasing attributes are in test.schema.
+2) compose component filters for aliasing attributes
+and put them in "preprocessed_comp_filter" array
+in "init.c".
+3) add "add_aa_entry" function calls in
+"init_attribute_aliasing_table()" in "init.c"
+4) perform searching against the aliasing attribute
+Ex)
+"(x509CertificateIssuer:distinguishedNameMatch=
+cn=ray,L=yorktown,o=ibm,c=us)"
diff --git a/contrib/slapd-modules/comp_match/asn.h b/contrib/slapd-modules/comp_match/asn.h
new file mode 100644
index 0000000..a7d5b16
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/asn.h
@@ -0,0 +1,57 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+#ifndef _H_ASN_MODULE
+#define _H_ASN_MODULE
+
+typedef enum { BER, GSER } EncRulesType;
+
+typedef enum AsnTypeId {
+ BASICTYPE_BOOLEAN = 0,
+ BASICTYPE_INTEGER,
+ BASICTYPE_BITSTRING,
+ BASICTYPE_OCTETSTRING,
+ BASICTYPE_NULL,
+ BASICTYPE_OID,
+ BASICTYPE_REAL,
+ BASICTYPE_ENUMERATED,
+ BASICTYPE_NUMERIC_STR,
+ BASICTYPE_PRINTABLE_STR,
+ BASICTYPE_UNIVERSAL_STR,
+ BASICTYPE_IA5_STR,
+ BASICTYPE_BMP_STR,
+ BASICTYPE_UTF8_STR,
+ BASICTYPE_UTCTIME,
+ BASICTYPE_GENERALIZEDTIME,
+ BASICTYPE_GRAPHIC_STR,
+ BASICTYPE_VISIBLE_STR,
+ BASICTYPE_GENERAL_STR,
+ BASICTYPE_OBJECTDESCRIPTOR,
+ BASICTYPE_VIDEOTEX_STR,
+ BASICTYPE_T61_STR,
+ BASICTYPE_OCTETCONTAINING,
+ BASICTYPE_BITCONTAINING,
+ BASICTYPE_RELATIVE_OID, /* 25 */
+ BASICTYPE_ANY,
+ /* Embedded Composite Types*/
+ COMPOSITE_ASN1_TYPE,
+ /* A New ASN.1 types including type reference */
+ RDNSequence,
+ RelativeDistinguishedName,
+ TelephoneNumber,
+ FacsimileTelephoneNumber__telephoneNumber,
+ DirectoryString,
+ /* Newly Defined ASN.1 Type, Manually registered */
+ ASN_COMP_CERTIFICATE,
+ /* ASN.1 Type End */
+ ASNTYPE_END
+} AsnTypeId;
+
+#endif
diff --git a/contrib/slapd-modules/comp_match/asn_to_syn_mr.c b/contrib/slapd-modules/comp_match/asn_to_syn_mr.c
new file mode 100644
index 0000000..9c3f7e7
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/asn_to_syn_mr.c
@@ -0,0 +1,282 @@
+#include <component.h>
+#include "asn.h"
+#include "componentlib.h"
+#include "certificate.h"
+
+AsnTypetoMatchingRuleTable directory_component_matching_table[] = {
+ "1.2.36.79672281.1.13.7",
+{
+ { BASICTYPE_BOOLEAN,NULL,"1.3.6.1.4.1.1466.115.121.1.7", NULL },
+ { BASICTYPE_INTEGER,NULL ,"1.3.6.1.4.1.1466.115.121.1.27", NULL },
+ { BASICTYPE_BITSTRING,NULL ,"1.3.6.1.4.1.1466.115.121.1.6", NULL },
+ { BASICTYPE_OCTETSTRING,NULL , "1.3.6.1.4.1.1466.115.121.1.40", NULL },
+ { BASICTYPE_NULL,NULL , NULL, NULL },
+ { BASICTYPE_OID,NULL ,"1.3.6.1.4.1.1466.115.121.1.38", NULL },
+ { BASICTYPE_REAL,NULL , NULL, NULL },
+ { BASICTYPE_ENUMERATED,NULL , NULL, NULL },
+ { BASICTYPE_NUMERIC_STR, "numericStringMatch", "1.3.6.1.4.1.1466.115.121.1.36", NULL },
+ { BASICTYPE_PRINTABLE_STR, "caseIgnoreMatch", "1.3.6.1.4.1.1466.115.121.1.44", NULL },
+ { BASICTYPE_UNIVERSAL_STR, "caseIgnoreMatch" , NULL, NULL },
+ { BASICTYPE_IA5_STR, "caseIgnoreMatch", "1.3.6.1.4.1.1466.115.121.1.26", NULL },
+ { BASICTYPE_BMP_STR, "caseIgnoreMatch" , NULL, NULL },
+ { BASICTYPE_UTF8_STR, "caseIgnoreMatch" , NULL, NULL },
+ { BASICTYPE_UTCTIME, "uTCTimeMatch" , "1.3.6.1.4.1.1466.115.121.1.53", NULL },
+ { BASICTYPE_GENERALIZEDTIME, "generalizedTimeMatch" ,"1.3.6.1.4.1.1466.115.121.1.24", NULL },
+ { BASICTYPE_GRAPHIC_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_VISIBLE_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_GENERAL_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_OBJECTDESCRIPTOR, NULL , NULL, NULL },
+ { BASICTYPE_VIDEOTEX_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_T61_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_OCTETCONTAINING, NULL , NULL, NULL },
+ { BASICTYPE_BITCONTAINING, NULL , NULL, NULL },
+ { BASICTYPE_RELATIVE_OID, NULL, "1.2.36.79672281.1.5.0", NULL },
+ { RDNSequence, "distinguishedNameMatch" , NULL, NULL },
+ { RelativeDistinguishedName, NULL , NULL, NULL },
+ { TelephoneNumber, "telephoneNumberMatch" , "1.3.6.1.4.1.1466.115.121.1.50", NULL },
+ { FacsimileTelephoneNumber__telephoneNumber, "telephoneNumberMatch","1.3.6.1.4.1.1466.115.121.1.22", NULL },
+ { DirectoryString, "caseIgnoreMatch" ,"1.3.6.1.4.1.1466.115.121.1.15", NULL },
+ { ASN_COMP_CERTIFICATE, NULL , "1.2.36.79672281.1.5.2" , NULL },
+ { ASNTYPE_END , NULL , NULL, NULL }
+},
+ NULL
+};
+
+AsnTypetoSyntax asn_to_syntax_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN,"Boolean","1.3.6.1.4.1.1466.115.121.1.7", NULL },
+{ BASICTYPE_INTEGER,"Integer","1.3.6.1.4.1.1466.115.121.1.27", NULL },
+{ BASICTYPE_BITSTRING,"Bit String","1.3.6.1.4.1.1466.115.121.1.6", NULL },
+{ BASICTYPE_OCTETSTRING,"Octet String", "1.3.6.1.4.1.1466.115.121.1.40", NULL },
+{ BASICTYPE_NULL,NULL, NULL, NULL },
+{ BASICTYPE_OID,"OID","1.3.6.1.4.1.1466.115.121.1.38", NULL },
+{ BASICTYPE_REAL,NULL, NULL, NULL },
+{ BASICTYPE_ENUMERATED,"Integer", "1.3.6.1.4.1.1466.115.121.1.27", NULL },
+{ BASICTYPE_NUMERIC_STR, "Numeric String", "1.3.6.1.4.1.1466.115.121.1.36", NULL },
+{ BASICTYPE_PRINTABLE_STR, "Printable String", "1.3.6.1.4.1.1466.115.121.1.44", NULL },
+{ BASICTYPE_UNIVERSAL_STR, NULL , NULL, NULL },
+{ BASICTYPE_IA5_STR, "IA5 String", "1.3.6.1.4.1.1466.115.121.1.26", NULL },
+{ BASICTYPE_BMP_STR, NULL , NULL, NULL },
+{ BASICTYPE_UTF8_STR, "Directory String" , "1.3.6.1.4.1.1466.115.121.1.15", NULL },
+{ BASICTYPE_UTCTIME, "UTC Time" , "1.3.6.1.4.1.1466.115.121.1.53", NULL },
+{ BASICTYPE_GENERALIZEDTIME, "Generalized Time" ,"1.3.6.1.4.1.1466.115.121.1.24", NULL },
+{ BASICTYPE_GRAPHIC_STR, NULL, NULL, NULL },
+{ BASICTYPE_VISIBLE_STR, "Directory String", "1.3.6.1.4.1.1466.115.121.1.15", NULL },
+{ BASICTYPE_GENERAL_STR, NULL, NULL, NULL },
+{ BASICTYPE_OBJECTDESCRIPTOR, "Object Class Description", "1.3.6.1.4.1.1466.115.121.1.37", NULL },
+{ BASICTYPE_VIDEOTEX_STR, NULL, NULL, NULL },
+{ BASICTYPE_T61_STR, NULL, NULL, NULL },
+{ BASICTYPE_OCTETCONTAINING, NULL , NULL, NULL },
+{ BASICTYPE_BITCONTAINING, NULL , NULL, NULL },
+{ BASICTYPE_RELATIVE_OID, "OID", "1.3.6.1.4.1.1466.115.121.1.38", NULL },
+{ BASICTYPE_ANY, NULL, NULL, NULL },
+{ COMPOSITE_ASN1_TYPE, NULL , NULL, NULL },
+{ RDNSequence, "Distinguished Name" , "1.3.6.1.4.1.1466.115.121.1.12", NULL },
+{ RelativeDistinguishedName, "RDN", "1.2.36.79672281.1.5.0", NULL },
+{ TelephoneNumber, "Telephone Number" , "1.3.6.1.4.1.1466.115.121.1.50", NULL },
+{ FacsimileTelephoneNumber__telephoneNumber, "Facsimile Telephone Number","1.3.6.1.4.1.1466.115.121.1.22", NULL },
+{ DirectoryString, "Directory String" ,"1.3.6.1.4.1.1466.115.121.1.15", NULL },
+{ ASN_COMP_CERTIFICATE, "componentCertificate", "1.2.36.79672281.1.5.2" , NULL },
+{ ASNTYPE_END , NULL , NULL, NULL }
+};
+
+/*
+ * This table describes relationship between an ASN.1 type and its
+ * potential matching rules such as equality, approx, ordering, and substring
+ * Based on the description of this table, the following ComponentType
+ * table is initialized
+ */
+AsnTypetoCompMatchingRule asntype_to_compMR_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN, "booleanMatch", NULL, NULL, NULL },
+{ BASICTYPE_INTEGER, "integerMatch", NULL, "integerOrderingMatch", NULL },
+{ BASICTYPE_BITSTRING, "bitStringMatch", NULL, NULL, NULL },
+{ BASICTYPE_OCTETSTRING, "octetStringMatch", NULL, "octetStringOrderingMatch", NULL },
+{ BASICTYPE_NULL, NULL, NULL, NULL, NULL },
+{ BASICTYPE_OID, "objectIdentifierMatch", NULL, NULL, NULL },
+{ BASICTYPE_REAL, NULL, NULL, NULL, NULL },
+{ BASICTYPE_ENUMERATED, "integerMatch", NULL, "integerOrderingMatch", NULL },
+{ BASICTYPE_NUMERIC_STR, "numericStringMatch", NULL, "numericStringOrderingMatch", "numericStringSubstringsMatch"},
+{ BASICTYPE_PRINTABLE_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_UNIVERSAL_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_IA5_STR, "caseIgnoreMatch", "IA5StringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_BMP_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_UTF8_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_UTCTIME, NULL, NULL, NULL, NULL },
+{ BASICTYPE_GENERALIZEDTIME, NULL, NULL, NULL, NULL },
+{ BASICTYPE_GRAPHIC_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_VISIBLE_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_GENERAL_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_OBJECTDESCRIPTOR, "objectIdentifierFirstComponentMatch", NULL, NULL, NULL },
+{ BASICTYPE_VIDEOTEX_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_T61_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_OCTETCONTAINING, NULL, NULL, NULL, NULL },
+{ BASICTYPE_BITCONTAINING, NULL, NULL, NULL, NULL },
+{ BASICTYPE_RELATIVE_OID, "objectIdentifierFirstComponentMatch", NULL, NULL, NULL },
+{ BASICTYPE_ANY, NULL, NULL, NULL, NULL },
+{ COMPOSITE_ASN1_TYPE, NULL, NULL, NULL, NULL },
+{ RDNSequence, "distinguishedNameMatch", NULL, NULL, NULL },
+{ RelativeDistinguishedName, "rdnMatch" , NULL, NULL, NULL },
+{ TelephoneNumber, NULL, NULL, NULL, NULL },
+{ FacsimileTelephoneNumber__telephoneNumber, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ DirectoryString, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch"},
+{ ASN_COMP_CERTIFICATE, "componentFilterMatch", NULL, NULL, NULL },
+{ ASNTYPE_END, NULL, NULL, NULL, NULL }
+};
+
+/*
+ * This table maps an ASN type to a corresponding ComponentType which has
+ * equivalent contents of an existing AttributeType
+ */
+AsnTypetoCompType asntype_to_compType_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN,{}},
+{ BASICTYPE_INTEGER, {}},
+{ BASICTYPE_BITSTRING, {}},
+{ BASICTYPE_OCTETSTRING, {}},
+{ BASICTYPE_NULL, {}},
+{ BASICTYPE_OID, {}},
+{ BASICTYPE_REAL, {}},
+{ BASICTYPE_ENUMERATED, {}},
+{ BASICTYPE_NUMERIC_STR, {}},
+{ BASICTYPE_PRINTABLE_STR, {}},
+{ BASICTYPE_UNIVERSAL_STR, {}},
+{ BASICTYPE_IA5_STR, {}},
+{ BASICTYPE_BMP_STR, {}},
+{ BASICTYPE_UTF8_STR, {}},
+{ BASICTYPE_UTCTIME, {}},
+{ BASICTYPE_GENERALIZEDTIME, {}},
+{ BASICTYPE_GRAPHIC_STR, {}},
+{ BASICTYPE_VISIBLE_STR, {}},
+{ BASICTYPE_GENERAL_STR,{}},
+{ BASICTYPE_OBJECTDESCRIPTOR, {}},
+{ BASICTYPE_VIDEOTEX_STR, {}},
+{ BASICTYPE_T61_STR, {}},
+{ BASICTYPE_OCTETCONTAINING, {}},
+{ BASICTYPE_BITCONTAINING, {}},
+{ BASICTYPE_RELATIVE_OID, {}},
+{ BASICTYPE_ANY, {}},
+{ COMPOSITE_ASN1_TYPE, {}},
+{ RDNSequence, {}},
+{ RelativeDistinguishedName, {}},
+{ TelephoneNumber, {}},
+{ FacsimileTelephoneNumber__telephoneNumber, {}},
+{ DirectoryString, {}},
+{ ASN_COMP_CERTIFICATE, {}},
+{ ASNTYPE_END , {}}
+};
+
+AsnTypetoCompDesc asntype_to_compdesc_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BOOLEAN,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentBool,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentBool,(ber_decoder_func*)BDecComponentBool,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentBool}},
+{ BASICTYPE_INTEGER, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_INTEGER,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentInt,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentInt,(ber_decoder_func*)BDecComponentInt,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentInt}},
+{ BASICTYPE_BITSTRING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BITSTRING,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentBits,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentBits,(ber_decoder_func*)BDecComponentBits,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentBits}},
+{ BASICTYPE_OCTETSTRING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OCTETSTRING,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentOcts,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentOcts,(ber_decoder_func*)BDecComponentOcts,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentOcts}},
+{ BASICTYPE_NULL, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_NULL,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentNull,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentNull,(ber_decoder_func*)BDecComponentNull,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentNull}},
+{ BASICTYPE_OID, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OID,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentOid,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentOid,(ber_decoder_func*)BDecComponentOid,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentOid}},
+{ BASICTYPE_REAL, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_REAL,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentReal,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentReal,(ber_decoder_func*)BDecComponentReal,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentReal}},
+{ BASICTYPE_ENUMERATED, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_ENUMERATED,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentEnum,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentEnum,(ber_decoder_func*)BDecComponentEnum,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentEnum}},
+{ BASICTYPE_NUMERIC_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_NUMERIC_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentNumericString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentNumericString,(ber_decoder_func*)BDecComponentNumericString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentNumericString}},
+{ BASICTYPE_PRINTABLE_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_PRINTABLE_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentPrintableString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentPrintableString,(ber_decoder_func*)BDecComponentPrintableString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentPrintableString}},
+{ BASICTYPE_UNIVERSAL_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_UNIVERSAL_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUniversalString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUniversalString,(ber_decoder_func*)BDecComponentUniversalString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUniversalString}},
+{ BASICTYPE_IA5_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_IA5_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentIA5String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentIA5String,(ber_decoder_func*)BDecComponentIA5String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentIA5String}},
+{ BASICTYPE_BMP_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BMP_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentBMPString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentBMPString,(ber_decoder_func*)BDecComponentBMPString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentBMPString}},
+{ BASICTYPE_UTF8_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_UTF8_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_UTCTIME, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_UTCTIME,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTCTime,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTCTime,(ber_decoder_func*)BDecComponentUTCTime,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTCTime}},
+{ BASICTYPE_GENERALIZEDTIME, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_GENERALIZEDTIME,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTCTime,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTCTime,(ber_decoder_func*)BDecComponentUTCTime,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTCTime}},
+{ BASICTYPE_GRAPHIC_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_GRAPHIC_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentPrintableString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentPrintableString,(ber_decoder_func*)BDecComponentPrintableString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentPrintableString}},
+{ BASICTYPE_VISIBLE_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_VISIBLE_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentVisibleString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentVisibleString,(ber_decoder_func*)BDecComponentVisibleString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentVisibleString}},
+{ BASICTYPE_GENERAL_STR,{ -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_GENERAL_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_OBJECTDESCRIPTOR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OBJECTDESCRIPTOR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_VIDEOTEX_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_VIDEOTEX_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentTeletexString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentTeletexString,(ber_decoder_func*)BDecComponentTeletexString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentTeletexString}},
+{ BASICTYPE_T61_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_T61_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_OCTETCONTAINING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OCTETCONTAINING,
+ (encoder_func*)NULL,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)NULL,(ber_decoder_func*)NULL,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,NULL}},
+{ BASICTYPE_BITCONTAINING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BITCONTAINING,
+ (encoder_func*)NULL,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)NULL,(ber_decoder_func*)NULL,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,NULL}},
+{ BASICTYPE_RELATIVE_OID, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_RELATIVE_OID,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentRelativeOid,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentRelativeOid,(ber_decoder_func*)BDecComponentRelativeOid,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentRelativeOid}},
+{ BASICTYPE_ANY, {}},
+{ COMPOSITE_ASN1_TYPE, {}},
+{ RDNSequence, { -1, NULL, {},{},0,ASN_COMPOSITE,RDNSequence,
+ (encoder_func*)ConvertRDNSequence2RFC2253,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentRDNSequence,(ber_decoder_func*)BDecComponentRDNSequence,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentRDNSequence}},
+{ RelativeDistinguishedName, { -1, NULL, {},{},0,ASN_COMPOSITE,RDNSequence,
+ (encoder_func*)ConvertRDNSequence2RFC2253,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentRDNSequence,(ber_decoder_func*)BDecComponentRDNSequence,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentRDNSequence}},
+{ TelephoneNumber, {}},
+{ FacsimileTelephoneNumber__telephoneNumber, {}},
+{ DirectoryString, {}},
+{ ASN_COMP_CERTIFICATE, {}},
+{ ASNTYPE_END , {}}
+};
diff --git a/contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn
new file mode 100644
index 0000000..85ac92d
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn
@@ -0,0 +1,65 @@
+AuthorityKeyIdentifierDefinition DEFINITIONS ::=
+BEGIN
+AuthorityKeyIdentifier ::= SEQUENCE {
+ keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
+ authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
+ authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL }
+ -- authorityCertIssuer and authorityCertSerialNumber MUST both
+ -- be present or both be absent
+
+KeyIdentifier ::= OCTET STRING
+
+CertificateSerialNumber ::= INTEGER
+
+GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+GeneralName ::= CHOICE {
+ otherName [0] OtherName,
+ rfc822Name [1] IA5String,
+ dNSName [2] IA5String,
+ x400Address [3] ORAddress,
+ directoryName [4] Name,
+ ediPartyName [5] EDIPartyName,
+ uniformResourceIdentifier [6] IA5String,
+ iPAddress [7] OCTET STRING,
+ registeredID [8] OBJECT IDENTIFIER }
+
+OtherName ::= SEQUENCE {
+ type-id OBJECT IDENTIFIER,
+ value [0] EXPLICIT ANY DEFINED BY type-id }
+
+EDIPartyName ::= SEQUENCE {
+ nameAssigner [0] DirectoryString OPTIONAL,
+ partyName [1] DirectoryString }
+
+-- following ORAddress may not conform original def. in ASN.1
+ORAddress ::= SEQUENCE {
+-- built-in-standard-attributes BuiltInStandardAttributes,
+ type-id OBJECT IDENTIFIER,
+-- built-in-domain-defined-attributes
+ value ANY DEFINED BY type-id,
+-- BuiltInDomainDefinedAttributes OPTIONAL,
+-- see also teletex-domain-defined-attributes
+--extension-attributes ExtensionAttributes OPTIONAL }
+ extension OCTET STRING OPTIONAL }
+
+
+Name ::= CHOICE {
+ rdnSequence RDNSequence }
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+
+AttributeTypeAndValue ::= SEQUENCE {
+ type OBJECT IDENTIFIER,
+ value ANY DEFINED BY type}
+
+DirectoryString ::= CHOICE {
+ teletexString TeletexString (SIZE (1..MAX)),
+ printableString PrintableString (SIZE (1..MAX)),
+ universalString UniversalString (SIZE (1..MAX)),
+ utf8String UTF8String (SIZE (1..MAX)),
+ bmpString BMPString (SIZE (1..MAX)) }
+
+END
diff --git a/contrib/slapd-modules/comp_match/authorityKeyIdentifier.c b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.c
new file mode 100644
index 0000000..84e83b8
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.c
@@ -0,0 +1,2058 @@
+/*
+ * authorityKeyIdentifier.c
+ * "AuthorityKeyIdentifierDefinition" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Wed Dec 8 22:22:49 2004
+ * The generated files are supposed to be compiled as a module for OpenLDAP Software
+ */
+
+#include "authorityKeyIdentifier.h"
+
+BDecComponentAuthorityKeyIdentifierTop( void* mem_op, GenBuf* b, void *v, AsnLen* bytesDecoded,int mode) {
+ AsnTag tag;
+ AsnLen elmtLen;
+
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return BDecComponentAuthorityKeyIdentifier( mem_op, b, tag, elmtLen, ( ComponentAuthorityKeyIdentifier**)v, (AsnLen*)bytesDecoded, mode );
+}
+
+
+void init_module_AuthorityKeyIdentifierDefinition() {
+ InstallOidDecoderMapping( "2.5.29.35", NULL,
+ GDecComponentAuthorityKeyIdentifier,
+ BDecComponentAuthorityKeyIdentifierTop,
+ ExtractingComponentAuthorityKeyIdentifier,
+ MatchingComponentAuthorityKeyIdentifier );
+}
+
+int
+MatchingComponentOtherName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentOtherName*)csi_attr)->type_id, (ComponentSyntaxInfo*)&((ComponentOtherName*)csi_assert)->type_id );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentOtherName*)csi_attr)->value, (&((ComponentOtherName*)csi_attr)->type_id));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentOtherName*)csi_attr)->value, (ComponentAny*)&((ComponentOtherName*)csi_assert)->value);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentOtherName */
+
+void*
+ExtractingComponentOtherName ( void* mem_op, ComponentReference* cr, ComponentOtherName *comp )
+{
+
+ if ( ( comp->type_id.identifier.bv_val && strncmp(comp->type_id.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->type_id.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->type_id;
+ else
+ return NULL;
+ }
+ if ( ( comp->value.identifier.bv_val && strncmp(comp->value.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->value.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->value;
+ else
+ return NULL;
+ }
+ return NULL;
+} /* ExtractingComponentOtherName */
+
+int
+BDecComponentOtherName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentOtherName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentOtherName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->type_id), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = (&k->type_id)->id_buf;
+ (&k->type_id)->identifier.bv_len = strlen("type_id");
+ strcpy( (&k->type_id)->identifier.bv_val, "type_id");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->value), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = (&k->value)->id_buf;
+ (&k->value)->identifier.bv_len = strlen("value");
+ strcpy( (&k->value)->identifier.bv_val, "value");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentOtherName*) CompAlloc( mem_op, sizeof(ComponentOtherName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOtherName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOtherName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentOtherName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOtherName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecOtherName*/
+
+int
+GDecComponentOtherName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentOtherName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentOtherName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "type_id", strlen("type_id") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->type_id), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = peek_head;
+ (&k->type_id)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "value", strlen("value") ) == 0 ) {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->value), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = peek_head;
+ (&k->value)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentOtherName*) CompAlloc( mem_op, sizeof(ComponentOtherName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOtherName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOtherName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentOtherName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOtherName;
+ return LDAP_SUCCESS;
+} /* GDecOtherName*/
+
+
+int
+MatchingComponentORAddress ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_attr)->type_id, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_assert)->type_id );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentORAddress*)csi_attr)->value, (&((ComponentORAddress*)csi_attr)->type_id));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentORAddress*)csi_attr)->value, (ComponentAny*)&((ComponentORAddress*)csi_assert)->value);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentOcts ( oid, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_attr)->extension, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_assert)->extension );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentORAddress */
+
+void*
+ExtractingComponentORAddress ( void* mem_op, ComponentReference* cr, ComponentORAddress *comp )
+{
+
+ if ( ( comp->type_id.identifier.bv_val && strncmp(comp->type_id.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->type_id.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->type_id;
+ else
+ return NULL;
+ }
+ if ( ( comp->value.identifier.bv_val && strncmp(comp->value.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->value.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->value;
+ else
+ return NULL;
+ }
+ if ( ( comp->extension.identifier.bv_val && strncmp(comp->extension.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extension.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->extension;
+ else
+ return NULL;
+ }
+ return NULL;
+} /* ExtractingComponentORAddress */
+
+int
+BDecComponentORAddress PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentORAddress **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentORAddress *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->type_id), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = (&k->type_id)->id_buf;
+ (&k->type_id)->identifier.bv_len = strlen("type_id");
+ strcpy( (&k->type_id)->identifier.bv_val, "type_id");
+ }
+ else
+ return -1;
+
+
+
+ {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->value), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = (&k->value)->id_buf;
+ (&k->value)->identifier.bv_len = strlen("value");
+ strcpy( (&k->value)->identifier.bv_val, "value");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OCTETSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, OCTETSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOcts (mem_op, b, tagId1, elmtLen1, (&k->extension), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extension)->identifier.bv_val = (&k->extension)->id_buf;
+ (&k->extension)->identifier.bv_len = strlen("extension");
+ strcpy( (&k->extension)->identifier.bv_val, "extension");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentORAddress*) CompAlloc( mem_op, sizeof(ComponentORAddress) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentORAddress ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentORAddress ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentORAddress;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentORAddress;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecORAddress*/
+
+int
+GDecComponentORAddress PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentORAddress **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentORAddress *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "type_id", strlen("type_id") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->type_id), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = peek_head;
+ (&k->type_id)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "value", strlen("value") ) == 0 ) {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->value), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = peek_head;
+ (&k->value)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "extension", strlen("extension") ) == 0 ) {
+ rc = GDecComponentOcts (mem_op, b, (&k->extension), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extension)->identifier.bv_val = peek_head;
+ (&k->extension)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentORAddress*) CompAlloc( mem_op, sizeof(ComponentORAddress) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentORAddress ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentORAddress ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentORAddress;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentORAddress;
+ return LDAP_SUCCESS;
+} /* GDecORAddress*/
+
+
+int
+MatchingComponentDirectoryString ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentDirectoryString *v1, *v2;
+
+
+ v1 = (ComponentDirectoryString*)csi_attr;
+ v2 = (ComponentDirectoryString*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case DIRECTORYSTRING_TELETEXSTRING :
+ rc = MatchingComponentTeletexString ( oid, (ComponentSyntaxInfo*)(v1->a.teletexString), (ComponentSyntaxInfo*)(v2->a.teletexString) );
+ break;
+ case DIRECTORYSTRING_PRINTABLESTRING :
+ rc = MatchingComponentPrintableString ( oid, (ComponentSyntaxInfo*)(v1->a.printableString), (ComponentSyntaxInfo*)(v2->a.printableString) );
+ break;
+ case DIRECTORYSTRING_UNIVERSALSTRING :
+ rc = MatchingComponentUniversalString ( oid, (ComponentSyntaxInfo*)(v1->a.universalString), (ComponentSyntaxInfo*)(v2->a.universalString) );
+ break;
+ case DIRECTORYSTRING_UTF8STRING :
+ rc = MatchingComponentUTF8String ( oid, (ComponentSyntaxInfo*)(v1->a.utf8String), (ComponentSyntaxInfo*)(v2->a.utf8String) );
+ break;
+ case DIRECTORYSTRING_BMPSTRING :
+ rc = MatchingComponentBMPString ( oid, (ComponentSyntaxInfo*)(v1->a.bmpString), (ComponentSyntaxInfo*)(v2->a.bmpString) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentDirectoryStringContent */
+
+void*
+ExtractingComponentDirectoryString ( void* mem_op, ComponentReference* cr, ComponentDirectoryString *comp )
+{
+
+
+ if( (comp->choiceId) == DIRECTORYSTRING_TELETEXSTRING &&
+ (( comp->a.teletexString->identifier.bv_val && strncmp(comp->a.teletexString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.teletexString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.teletexString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTeletexString ( mem_op, cr, (comp->a.teletexString) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_PRINTABLESTRING &&
+ (( comp->a.printableString->identifier.bv_val && strncmp(comp->a.printableString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.printableString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.printableString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentPrintableString ( mem_op, cr, (comp->a.printableString) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_UNIVERSALSTRING &&
+ (( comp->a.universalString->identifier.bv_val && strncmp(comp->a.universalString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.universalString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.universalString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentUniversalString ( mem_op, cr, (comp->a.universalString) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_UTF8STRING &&
+ (( comp->a.utf8String->identifier.bv_val && strncmp(comp->a.utf8String->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.utf8String->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.utf8String);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentUTF8String ( mem_op, cr, (comp->a.utf8String) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_BMPSTRING &&
+ (( comp->a.bmpString->identifier.bv_val && strncmp(comp->a.bmpString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.bmpString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.bmpString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentBMPString ( mem_op, cr, (comp->a.bmpString) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentDirectoryString */
+
+int
+BDecComponentDirectoryString PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentDirectoryString **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentDirectoryString *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (UNIV, PRIM, TELETEXSTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, TELETEXSTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_TELETEXSTRING;
+ rc = BDecComponentTeletexString (mem_op, b, tagId0, elmtLen0, (&k->a.teletexString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.teletexString)->identifier.bv_val = (k->a.teletexString)->id_buf;
+ (k->a.teletexString)->identifier.bv_len = strlen("teletexString");
+ strcpy( (k->a.teletexString)->identifier.bv_val, "teletexString");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, PRINTABLESTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, PRINTABLESTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_PRINTABLESTRING;
+ rc = BDecComponentPrintableString (mem_op, b, tagId0, elmtLen0, (&k->a.printableString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.printableString)->identifier.bv_val = (k->a.printableString)->id_buf;
+ (k->a.printableString)->identifier.bv_len = strlen("printableString");
+ strcpy( (k->a.printableString)->identifier.bv_val, "printableString");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, UNIVERSALSTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, UNIVERSALSTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_UNIVERSALSTRING;
+ rc = BDecComponentUniversalString (mem_op, b, tagId0, elmtLen0, (&k->a.universalString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.universalString)->identifier.bv_val = (k->a.universalString)->id_buf;
+ (k->a.universalString)->identifier.bv_len = strlen("universalString");
+ strcpy( (k->a.universalString)->identifier.bv_val, "universalString");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, UTF8STRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, UTF8STRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_UTF8STRING;
+ rc = BDecComponentUTF8String (mem_op, b, tagId0, elmtLen0, (&k->a.utf8String), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utf8String)->identifier.bv_val = (k->a.utf8String)->id_buf;
+ (k->a.utf8String)->identifier.bv_len = strlen("utf8String");
+ strcpy( (k->a.utf8String)->identifier.bv_val, "utf8String");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, BMPSTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, BMPSTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_BMPSTRING;
+ rc = BDecComponentBMPString (mem_op, b, tagId0, elmtLen0, (&k->a.bmpString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.bmpString)->identifier.bv_val = (k->a.bmpString)->id_buf;
+ (k->a.bmpString)->identifier.bv_len = strlen("bmpString");
+ strcpy( (k->a.bmpString)->identifier.bv_val, "bmpString");
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentDirectoryString*) CompAlloc( mem_op, sizeof(ComponentDirectoryString) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentDirectoryString ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentDirectoryString ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentDirectoryString;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentDirectoryString;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecDirectoryStringContent */
+
+int
+GDecComponentDirectoryString PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentDirectoryString **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentDirectoryString *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("teletexString",peek_head, strlen("teletexString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_TELETEXSTRING;
+ rc = GDecComponentTeletexString (mem_op, b, (&k->a.teletexString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.teletexString)->identifier.bv_val = peek_head;
+ (k->a.teletexString)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("printableString",peek_head,strlen("printableString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_PRINTABLESTRING;
+ rc = GDecComponentPrintableString (mem_op, b, (&k->a.printableString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.printableString)->identifier.bv_val = peek_head;
+ (k->a.printableString)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("universalString",peek_head,strlen("universalString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_UNIVERSALSTRING;
+ rc = GDecComponentUniversalString (mem_op, b, (&k->a.universalString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.universalString)->identifier.bv_val = peek_head;
+ (k->a.universalString)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("utf8String",peek_head,strlen("utf8String")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_UTF8STRING;
+ rc = GDecComponentUTF8String (mem_op, b, (&k->a.utf8String), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utf8String)->identifier.bv_val = peek_head;
+ (k->a.utf8String)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("bmpString",peek_head,strlen("bmpString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_BMPSTRING;
+ rc = GDecComponentBMPString (mem_op, b, (&k->a.bmpString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.bmpString)->identifier.bv_val = peek_head;
+ (k->a.bmpString)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentDirectoryString*) CompAlloc( mem_op, sizeof(ComponentDirectoryString) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentDirectoryString ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentDirectoryString ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentDirectoryString;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentDirectoryString;
+ return LDAP_SUCCESS;
+} /* GDecDirectoryStringContent */
+
+
+int
+MatchingComponentEDIPartyName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ if(COMPONENTNOT_NULL( ((ComponentEDIPartyName*)csi_attr)->nameAssigner ) ) {
+ rc = MatchingComponentDirectoryString ( oid, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_attr)->nameAssigner, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_assert)->nameAssigner );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ rc = MatchingComponentDirectoryString ( oid, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_attr)->partyName, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_assert)->partyName );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentEDIPartyName */
+
+void*
+ExtractingComponentEDIPartyName ( void* mem_op, ComponentReference* cr, ComponentEDIPartyName *comp )
+{
+
+ if ( ( comp->nameAssigner->identifier.bv_val && strncmp(comp->nameAssigner->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->nameAssigner->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->nameAssigner;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentDirectoryString ( mem_op, cr, comp->nameAssigner );
+ }
+ }
+ if ( ( comp->partyName->identifier.bv_val && strncmp(comp->partyName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->partyName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->partyName;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentDirectoryString ( mem_op, cr, comp->partyName );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentEDIPartyName */
+
+int
+BDecComponentEDIPartyName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentEDIPartyName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ AsnLen totalElmtsLen3 = 0;
+ AsnLen elmtLen3;
+ AsnTag tagId3;
+ int old_mode = mode;
+ int rc;
+ ComponentEDIPartyName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = tagId2 = BDecTag (b, &totalElmtsLen1 );
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ BDecComponentDirectoryString (mem_op, b, tagId2, elmtLen2, (&k->nameAssigner), &totalElmtsLen1, mode);
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc(b, &totalElmtsLen1 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->nameAssigner)->identifier.bv_val = (k->nameAssigner)->id_buf;
+ (k->nameAssigner)->identifier.bv_len = strlen("nameAssigner");
+ strcpy( (k->nameAssigner)->identifier.bv_val, "nameAssigner");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = tagId2 = BDecTag (b, &totalElmtsLen1 );
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ BDecComponentDirectoryString (mem_op, b, tagId2, elmtLen2, (&k->partyName), &totalElmtsLen1, mode);
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc(b, &totalElmtsLen1 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->partyName)->identifier.bv_val = (k->partyName)->id_buf;
+ (k->partyName)->identifier.bv_len = strlen("partyName");
+ strcpy( (k->partyName)->identifier.bv_val, "partyName");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentEDIPartyName*) CompAlloc( mem_op, sizeof(ComponentEDIPartyName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEDIPartyName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEDIPartyName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentEDIPartyName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEDIPartyName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecEDIPartyName*/
+
+int
+GDecComponentEDIPartyName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentEDIPartyName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentEDIPartyName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "nameAssigner", strlen("nameAssigner") ) == 0 ) {
+ rc = GDecComponentDirectoryString (mem_op, b, (&k->nameAssigner), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->nameAssigner)->identifier.bv_val = peek_head;
+ ( k->nameAssigner)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "partyName", strlen("partyName") ) == 0 ) {
+ rc = GDecComponentDirectoryString (mem_op, b, (&k->partyName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->partyName)->identifier.bv_val = peek_head;
+ ( k->partyName)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentEDIPartyName*) CompAlloc( mem_op, sizeof(ComponentEDIPartyName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEDIPartyName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEDIPartyName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentEDIPartyName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEDIPartyName;
+ return LDAP_SUCCESS;
+} /* GDecEDIPartyName*/
+
+
+
+int
+MatchingComponentGeneralName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentGeneralName *v1, *v2;
+
+
+ v1 = (ComponentGeneralName*)csi_attr;
+ v2 = (ComponentGeneralName*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case GENERALNAME_OTHERNAME :
+ rc = MatchingComponentOtherName ( oid, (ComponentSyntaxInfo*)(v1->a.otherName), (ComponentSyntaxInfo*)(v2->a.otherName) );
+ break;
+ case GENERALNAME_RFC822NAME :
+ rc = MatchingComponentIA5String ( oid, (ComponentSyntaxInfo*)(v1->a.rfc822Name), (ComponentSyntaxInfo*)(v2->a.rfc822Name) );
+ break;
+ case GENERALNAME_DNSNAME :
+ rc = MatchingComponentIA5String ( oid, (ComponentSyntaxInfo*)(v1->a.dNSName), (ComponentSyntaxInfo*)(v2->a.dNSName) );
+ break;
+ case GENERALNAME_X400ADDRESS :
+ rc = MatchingComponentORAddress ( oid, (ComponentSyntaxInfo*)(v1->a.x400Address), (ComponentSyntaxInfo*)(v2->a.x400Address) );
+ break;
+ case GENERALNAME_DIRECTORYNAME :
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)(v1->a.directoryName), (ComponentSyntaxInfo*)(v2->a.directoryName) );
+ break;
+ case GENERALNAME_EDIPARTYNAME :
+ rc = MatchingComponentEDIPartyName ( oid, (ComponentSyntaxInfo*)(v1->a.ediPartyName), (ComponentSyntaxInfo*)(v2->a.ediPartyName) );
+ break;
+ case GENERALNAME_UNIFORMRESOURCEIDENTIFIER :
+ rc = MatchingComponentIA5String ( oid, (ComponentSyntaxInfo*)(v1->a.uniformResourceIdentifier), (ComponentSyntaxInfo*)(v2->a.uniformResourceIdentifier) );
+ break;
+ case GENERALNAME_IPADDRESS :
+ rc = MatchingComponentOcts ( oid, (ComponentSyntaxInfo*)(v1->a.iPAddress), (ComponentSyntaxInfo*)(v2->a.iPAddress) );
+ break;
+ case GENERALNAME_REGISTEREDID :
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)(v1->a.registeredID), (ComponentSyntaxInfo*)(v2->a.registeredID) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentGeneralNameContent */
+
+void*
+ExtractingComponentGeneralName ( void* mem_op, ComponentReference* cr, ComponentGeneralName *comp )
+{
+
+
+ if( (comp->choiceId) == GENERALNAME_OTHERNAME &&
+ (( comp->a.otherName->identifier.bv_val && strncmp(comp->a.otherName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.otherName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.otherName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentOtherName ( mem_op, cr, (comp->a.otherName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_RFC822NAME &&
+ (( comp->a.rfc822Name->identifier.bv_val && strncmp(comp->a.rfc822Name->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.rfc822Name->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.rfc822Name);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentIA5String ( mem_op, cr, (comp->a.rfc822Name) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_DNSNAME &&
+ (( comp->a.dNSName->identifier.bv_val && strncmp(comp->a.dNSName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.dNSName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.dNSName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentIA5String ( mem_op, cr, (comp->a.dNSName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_X400ADDRESS &&
+ (( comp->a.x400Address->identifier.bv_val && strncmp(comp->a.x400Address->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.x400Address->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.x400Address);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentORAddress ( mem_op, cr, (comp->a.x400Address) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_DIRECTORYNAME &&
+ (( comp->a.directoryName->identifier.bv_val && strncmp(comp->a.directoryName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.directoryName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.directoryName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, (comp->a.directoryName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_EDIPARTYNAME &&
+ (( comp->a.ediPartyName->identifier.bv_val && strncmp(comp->a.ediPartyName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.ediPartyName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.ediPartyName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentEDIPartyName ( mem_op, cr, (comp->a.ediPartyName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_UNIFORMRESOURCEIDENTIFIER &&
+ (( comp->a.uniformResourceIdentifier->identifier.bv_val && strncmp(comp->a.uniformResourceIdentifier->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.uniformResourceIdentifier->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.uniformResourceIdentifier);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentIA5String ( mem_op, cr, (comp->a.uniformResourceIdentifier) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_IPADDRESS &&
+ (( comp->a.iPAddress->identifier.bv_val && strncmp(comp->a.iPAddress->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.iPAddress->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.iPAddress);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentOcts ( mem_op, cr, (comp->a.iPAddress) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_REGISTEREDID &&
+ (( comp->a.registeredID->identifier.bv_val && strncmp(comp->a.registeredID->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.registeredID->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.registeredID);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentOid ( mem_op, cr, (comp->a.registeredID) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentGeneralName */
+
+int
+BDecComponentGeneralName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentGeneralName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ AsnLen totalElmtsLen3 = 0;
+ AsnLen elmtLen3;
+ AsnTag tagId3;
+ int old_mode = mode;
+ int rc;
+ ComponentGeneralName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (CNTX, CONS, 0):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_OTHERNAME;
+ rc = BDecComponentOtherName (mem_op, b, tagId1, elmtLen1, (&k->a.otherName), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.otherName)->identifier.bv_val = (k->a.otherName)->id_buf;
+ (k->a.otherName)->identifier.bv_len = strlen("otherName");
+ strcpy( (k->a.otherName)->identifier.bv_val, "otherName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 1):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_RFC822NAME;
+ rc = BDecComponentIA5String (mem_op, b, tagId1, elmtLen1, (&k->a.rfc822Name), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rfc822Name)->identifier.bv_val = (k->a.rfc822Name)->id_buf;
+ (k->a.rfc822Name)->identifier.bv_len = strlen("rfc822Name");
+ strcpy( (k->a.rfc822Name)->identifier.bv_val, "rfc822Name");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 2):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_DNSNAME;
+ rc = BDecComponentIA5String (mem_op, b, tagId1, elmtLen1, (&k->a.dNSName), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.dNSName)->identifier.bv_val = (k->a.dNSName)->id_buf;
+ (k->a.dNSName)->identifier.bv_len = strlen("dNSName");
+ strcpy( (k->a.dNSName)->identifier.bv_val, "dNSName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 3):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_X400ADDRESS;
+ rc = BDecComponentORAddress (mem_op, b, tagId1, elmtLen1, (&k->a.x400Address), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.x400Address)->identifier.bv_val = (k->a.x400Address)->id_buf;
+ (k->a.x400Address)->identifier.bv_len = strlen("x400Address");
+ strcpy( (k->a.x400Address)->identifier.bv_val, "x400Address");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 4):
+ (k->choiceId) = GENERALNAME_DIRECTORYNAME;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->a.directoryName), &totalElmtsLen1, mode);
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc(b, &totalElmtsLen1 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.directoryName)->identifier.bv_val = (k->a.directoryName)->id_buf;
+ (k->a.directoryName)->identifier.bv_len = strlen("directoryName");
+ strcpy( (k->a.directoryName)->identifier.bv_val, "directoryName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 5):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_EDIPARTYNAME;
+ rc = BDecComponentEDIPartyName (mem_op, b, tagId1, elmtLen1, (&k->a.ediPartyName), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.ediPartyName)->identifier.bv_val = (k->a.ediPartyName)->id_buf;
+ (k->a.ediPartyName)->identifier.bv_len = strlen("ediPartyName");
+ strcpy( (k->a.ediPartyName)->identifier.bv_val, "ediPartyName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 6):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_UNIFORMRESOURCEIDENTIFIER;
+ rc = BDecComponentIA5String (mem_op, b, tagId1, elmtLen1, (&k->a.uniformResourceIdentifier), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.uniformResourceIdentifier)->identifier.bv_val = (k->a.uniformResourceIdentifier)->id_buf;
+ (k->a.uniformResourceIdentifier)->identifier.bv_len = strlen("uniformResourceIdentifier");
+ strcpy( (k->a.uniformResourceIdentifier)->identifier.bv_val, "uniformResourceIdentifier");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 7):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, OCTETSTRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, OCTETSTRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_IPADDRESS;
+ rc = BDecComponentOcts (mem_op, b, tagId1, elmtLen1, (&k->a.iPAddress), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.iPAddress)->identifier.bv_val = (k->a.iPAddress)->id_buf;
+ (k->a.iPAddress)->identifier.bv_len = strlen("iPAddress");
+ strcpy( (k->a.iPAddress)->identifier.bv_val, "iPAddress");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 8):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_REGISTEREDID;
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->a.registeredID), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.registeredID)->identifier.bv_val = (k->a.registeredID)->id_buf;
+ (k->a.registeredID)->identifier.bv_len = strlen("registeredID");
+ strcpy( (k->a.registeredID)->identifier.bv_val, "registeredID");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralName*) CompAlloc( mem_op, sizeof(ComponentGeneralName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecGeneralNameContent */
+
+int
+GDecComponentGeneralName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentGeneralName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentGeneralName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("otherName",peek_head, strlen("otherName")) == 0){
+ (k->choiceId) = GENERALNAME_OTHERNAME;
+ rc = GDecComponentOtherName (mem_op, b, (&k->a.otherName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.otherName)->identifier.bv_val = peek_head;
+ (k->a.otherName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("rfc822Name",peek_head,strlen("rfc822Name")) == 0){
+ (k->choiceId) = GENERALNAME_RFC822NAME;
+ rc = GDecComponentIA5String (mem_op, b, (&k->a.rfc822Name), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rfc822Name)->identifier.bv_val = peek_head;
+ (k->a.rfc822Name)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("dNSName",peek_head,strlen("dNSName")) == 0){
+ (k->choiceId) = GENERALNAME_DNSNAME;
+ rc = GDecComponentIA5String (mem_op, b, (&k->a.dNSName), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.dNSName)->identifier.bv_val = peek_head;
+ (k->a.dNSName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("x400Address",peek_head,strlen("x400Address")) == 0){
+ (k->choiceId) = GENERALNAME_X400ADDRESS;
+ rc = GDecComponentORAddress (mem_op, b, (&k->a.x400Address), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.x400Address)->identifier.bv_val = peek_head;
+ (k->a.x400Address)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("directoryName",peek_head,strlen("directoryName")) == 0){
+ (k->choiceId) = GENERALNAME_DIRECTORYNAME;
+ rc = GDecComponentName (mem_op, b, (&k->a.directoryName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.directoryName)->identifier.bv_val = peek_head;
+ (k->a.directoryName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("ediPartyName",peek_head,strlen("ediPartyName")) == 0){
+ (k->choiceId) = GENERALNAME_EDIPARTYNAME;
+ rc = GDecComponentEDIPartyName (mem_op, b, (&k->a.ediPartyName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.ediPartyName)->identifier.bv_val = peek_head;
+ (k->a.ediPartyName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("uniformResourceIdentifier",peek_head,strlen("uniformResourceIdentifier")) == 0){
+ (k->choiceId) = GENERALNAME_UNIFORMRESOURCEIDENTIFIER;
+ rc = GDecComponentIA5String (mem_op, b, (&k->a.uniformResourceIdentifier), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.uniformResourceIdentifier)->identifier.bv_val = peek_head;
+ (k->a.uniformResourceIdentifier)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("iPAddress",peek_head,strlen("iPAddress")) == 0){
+ (k->choiceId) = GENERALNAME_IPADDRESS;
+ rc = GDecComponentOcts (mem_op, b, (&k->a.iPAddress), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.iPAddress)->identifier.bv_val = peek_head;
+ (k->a.iPAddress)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("registeredID",peek_head,strlen("registeredID")) == 0){
+ (k->choiceId) = GENERALNAME_REGISTEREDID;
+ rc = GDecComponentOid (mem_op, b, (&k->a.registeredID), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.registeredID)->identifier.bv_val = peek_head;
+ (k->a.registeredID)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralName*) CompAlloc( mem_op, sizeof(ComponentGeneralName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralName;
+ return LDAP_SUCCESS;
+} /* GDecGeneralNameContent */
+
+
+int
+MatchingComponentGeneralNames ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentGeneralNames*)csi_attr)->comp_list;
+ v2 = &((ComponentGeneralNames*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentGeneralName(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentGeneralNamesContent */
+
+void*
+ExtractingComponentGeneralNames ( void* mem_op, ComponentReference* cr, ComponentGeneralNames *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentGeneralName *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentGeneralNames */
+
+int
+BDecComponentGeneralNames PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentGeneralNames **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentGeneralNames *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentGeneralName));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentGeneralName **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ elmtLen1 = BDecLen (b, &totalElmtsLen1);
+ tmpVar = (ComponentGeneralName**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentGeneralName (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralNames*) CompAlloc( mem_op, sizeof(ComponentGeneralNames) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralNames ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralNames ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralNames;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralNames;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecGeneralNamesContent */
+
+int
+GDecComponentGeneralNames PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentGeneralNames **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentGeneralNames *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentGeneralName ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentGeneralName **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentGeneralName**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentGeneralName (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralNames*) CompAlloc( mem_op, sizeof(ComponentGeneralNames) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralNames ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralNames ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralNames;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralNames;
+ return LDAP_SUCCESS;
+} /* GDecGeneralNamesContent */
+
+
+int
+MatchingComponentAuthorityKeyIdentifier ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentKeyIdentifier ( oid, (ComponentSyntaxInfo*)&((ComponentAuthorityKeyIdentifier*)csi_attr)->keyIdentifier, (ComponentSyntaxInfo*)&((ComponentAuthorityKeyIdentifier*)csi_assert)->keyIdentifier );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertIssuer ) ) {
+ rc = MatchingComponentGeneralNames ( oid, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertIssuer, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_assert)->authorityCertIssuer );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ if(COMPONENTNOT_NULL( ((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertSerialNumber ) ) {
+ rc = MatchingComponentCertificateSerialNumber ( oid, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertSerialNumber, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_assert)->authorityCertSerialNumber );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentAuthorityKeyIdentifier */
+
+void*
+ExtractingComponentAuthorityKeyIdentifier ( void* mem_op, ComponentReference* cr, ComponentAuthorityKeyIdentifier *comp )
+{
+
+ if ( ( comp->keyIdentifier.identifier.bv_val && strncmp(comp->keyIdentifier.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->keyIdentifier.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->keyIdentifier;
+ else
+ return NULL;
+ }
+ if ( ( comp->authorityCertIssuer->identifier.bv_val && strncmp(comp->authorityCertIssuer->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->authorityCertIssuer->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->authorityCertIssuer;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralNames ( mem_op, cr, comp->authorityCertIssuer );
+ }
+ }
+ if ( ( comp->authorityCertSerialNumber->identifier.bv_val && strncmp(comp->authorityCertSerialNumber->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->authorityCertSerialNumber->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->authorityCertSerialNumber;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentCertificateSerialNumber ( mem_op, cr, comp->authorityCertSerialNumber );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentAuthorityKeyIdentifier */
+
+int
+BDecComponentAuthorityKeyIdentifier PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentAuthorityKeyIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentAuthorityKeyIdentifier *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 0)) ||
+(tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentKeyIdentifier (mem_op, b, tagId1, elmtLen1, (&k->keyIdentifier), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->keyIdentifier)->identifier.bv_val = (&k->keyIdentifier)->id_buf;
+ (&k->keyIdentifier)->identifier.bv_len = strlen("keyIdentifier");
+ strcpy( (&k->keyIdentifier)->identifier.bv_val, "keyIdentifier");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentGeneralNames (mem_op, b, tagId1, elmtLen1,
+(&k->authorityCertIssuer), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->authorityCertIssuer)->identifier.bv_val = (k->authorityCertIssuer)->id_buf;
+ (k->authorityCertIssuer)->identifier.bv_len = strlen("authorityCertIssuer");
+ strcpy( (k->authorityCertIssuer)->identifier.bv_val, "authorityCertIssuer");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 2))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentCertificateSerialNumber (mem_op, b, tagId1, elmtLen1, (&k->authorityCertSerialNumber), &totalElmtsLen1, DEC_ALLOC_MODE_0 ); if ( rc != LDAP_SUCCESS ) return rc;
+ (k->authorityCertSerialNumber)->identifier.bv_val = (k->authorityCertSerialNumber)->id_buf;
+ (k->authorityCertSerialNumber)->identifier.bv_len = strlen("authorityCertSerialNumber");
+ strcpy( (k->authorityCertSerialNumber)->identifier.bv_val, "authorityCertSerialNumber");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAuthorityKeyIdentifier*) CompAlloc( mem_op, sizeof(ComponentAuthorityKeyIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAuthorityKeyIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAuthorityKeyIdentifier;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecAuthorityKeyIdentifier*/
+
+int
+GDecComponentAuthorityKeyIdentifier PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentAuthorityKeyIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentAuthorityKeyIdentifier *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "keyIdentifier", strlen("keyIdentifier") ) == 0 ) {
+ rc = GDecComponentKeyIdentifier (mem_op, b, (&k->keyIdentifier), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->keyIdentifier)->identifier.bv_val = peek_head;
+ (&k->keyIdentifier)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "authorityCertIssuer", strlen("authorityCertIssuer") ) == 0 ) {
+ rc = GDecComponentGeneralNames (mem_op, b, (&k->authorityCertIssuer), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->authorityCertIssuer)->identifier.bv_val = peek_head;
+ ( k->authorityCertIssuer)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "authorityCertSerialNumber", strlen("authorityCertSerialNumber") ) == 0 ) {
+ rc = GDecComponentCertificateSerialNumber (mem_op, b, (&k->authorityCertSerialNumber), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->authorityCertSerialNumber)->identifier.bv_val = peek_head;
+ ( k->authorityCertSerialNumber)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAuthorityKeyIdentifier*) CompAlloc( mem_op, sizeof(ComponentAuthorityKeyIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAuthorityKeyIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAuthorityKeyIdentifier;
+ return LDAP_SUCCESS;
+} /* GDecAuthorityKeyIdentifier*/
+
+
diff --git a/contrib/slapd-modules/comp_match/authorityKeyIdentifier.h b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.h
new file mode 100644
index 0000000..5fa4ab5
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.h
@@ -0,0 +1,327 @@
+
+#include "asn-incl.h"
+/*
+ * authorityKeyIdentifier.h
+ * "AuthorityKeyIdentifierDefinition" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Sat Dec 11 10:15:39 2004
+ * The generated files are strongly encouraged to be
+ * compiled as a module for OpenLDAP Software
+ */
+
+#ifndef _authorityKeyIdentifier_h_
+#define _authorityKeyIdentifier_h_
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+#include "componentlib.h"
+typedef ComponentOcts ComponentKeyIdentifier; /* OCTET STRING */
+
+#define MatchingComponentKeyIdentifier MatchingComponentOcts
+
+#define ExtractingComponentKeyIdentifier ExtractingComponentOcts
+
+#define BDecComponentKeyIdentifier BDecComponentOcts
+
+#define GDecComponentKeyIdentifier GDecComponentOcts
+
+
+typedef ComponentInt ComponentCertificateSerialNumber; /* INTEGER */
+
+#define MatchingComponentCertificateSerialNumber MatchingComponentInt
+
+#define ExtractingComponentCertificateSerialNumber ExtractingComponentInt
+
+#define BDecComponentCertificateSerialNumber BDecComponentInt
+
+#define GDecComponentCertificateSerialNumber GDecComponentInt
+
+
+typedef struct OtherName /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid type_id; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy value; /* [0] EXPLICIT ANY DEFINED BY type-id */
+} ComponentOtherName;
+
+int MatchingComponentOtherName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentOtherName PROTO (( void* mem_op, ComponentReference *cr, ComponentOtherName *comp ));
+
+
+int BDecComponentOtherName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentOtherName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentOtherName PROTO (( void* mem_op, GenBuf * b, ComponentOtherName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct ORAddress /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid type_id; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type-id */
+ ComponentOcts extension; /* OCTET STRING OPTIONAL */
+} ComponentORAddress;
+
+int MatchingComponentORAddress PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentORAddress PROTO (( void* mem_op, ComponentReference *cr, ComponentORAddress *comp ));
+
+
+int BDecComponentORAddress PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentORAddress **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentORAddress PROTO (( void* mem_op, GenBuf * b, ComponentORAddress **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid type; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} ComponentAttributeTypeAndValue;
+
+int MatchingComponentAttributeTypeAndValue PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAttributeTypeAndValue PROTO (( void* mem_op, ComponentReference *cr, ComponentAttributeTypeAndValue *comp ));
+
+
+int BDecComponentAttributeTypeAndValue PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAttributeTypeAndValue PROTO (( void* mem_op, GenBuf * b, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct DirectoryString /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum DirectoryStringChoiceId
+ {
+ DIRECTORYSTRING_TELETEXSTRING,
+ DIRECTORYSTRING_PRINTABLESTRING,
+ DIRECTORYSTRING_UNIVERSALSTRING,
+ DIRECTORYSTRING_UTF8STRING,
+ DIRECTORYSTRING_BMPSTRING
+ } choiceId;
+ union DirectoryStringChoiceUnion
+ {
+ ComponentTeletexString* teletexString; /* TeletexString SIZE 1..MAX */
+ ComponentPrintableString* printableString; /* PrintableString SIZE 1..MAX */
+ ComponentUniversalString* universalString; /* UniversalString SIZE 1..MAX */
+ ComponentUTF8String* utf8String; /* UTF8String SIZE 1..MAX */
+ ComponentBMPString* bmpString; /* BMPString SIZE 1..MAX */
+ } a;
+} ComponentDirectoryString;
+
+int MatchingComponentDirectoryString PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentDirectoryString PROTO (( void* mem_op, ComponentReference *cr, ComponentDirectoryString *comp ));
+
+
+int BDecComponentDirectoryString PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentDirectoryString **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentDirectoryString PROTO (( void* mem_op, GenBuf * b, ComponentDirectoryString **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct EDIPartyName /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentDirectoryString* nameAssigner; /* [0] DirectoryString OPTIONAL */
+ ComponentDirectoryString* partyName; /* [1] DirectoryString */
+} ComponentEDIPartyName;
+
+int MatchingComponentEDIPartyName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentEDIPartyName PROTO (( void* mem_op, ComponentReference *cr, ComponentEDIPartyName *comp ));
+
+
+int BDecComponentEDIPartyName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentEDIPartyName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentEDIPartyName PROTO (( void* mem_op, GenBuf * b, ComponentEDIPartyName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRelativeDistinguishedName; /* SET OF AttributeTypeAndValue */
+
+int MatchingComponentRelativeDistinguishedName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRelativeDistinguishedName PROTO (( void* mem_op, ComponentReference *cr, ComponentRelativeDistinguishedName *comp ));
+
+
+int BDecComponentRelativeDistinguishedName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRelativeDistinguishedName PROTO (( void* mem_op, GenBuf * b, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRDNSequence; /* SEQUENCE OF RelativeDistinguishedName */
+
+int MatchingComponentRDNSequence PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRDNSequence PROTO (( void* mem_op, ComponentReference *cr, ComponentRDNSequence *comp ));
+
+
+int BDecComponentRDNSequence PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRDNSequence PROTO (( void* mem_op, GenBuf * b, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Name /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum NameChoiceId
+ {
+ NAME_RDNSEQUENCE
+ } choiceId;
+ union NameChoiceUnion
+ {
+ ComponentRDNSequence* rdnSequence; /* RDNSequence */
+ } a;
+} ComponentName;
+
+int MatchingComponentName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentName PROTO (( void* mem_op, ComponentReference *cr, ComponentName *comp ));
+
+
+int BDecComponentName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentName PROTO (( void* mem_op, GenBuf * b, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct GeneralName /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum GeneralNameChoiceId
+ {
+ GENERALNAME_OTHERNAME,
+ GENERALNAME_RFC822NAME,
+ GENERALNAME_DNSNAME,
+ GENERALNAME_X400ADDRESS,
+ GENERALNAME_DIRECTORYNAME,
+ GENERALNAME_EDIPARTYNAME,
+ GENERALNAME_UNIFORMRESOURCEIDENTIFIER,
+ GENERALNAME_IPADDRESS,
+ GENERALNAME_REGISTEREDID
+ } choiceId;
+ union GeneralNameChoiceUnion
+ {
+ ComponentOtherName* otherName; /* [0] OtherName */
+ ComponentIA5String* rfc822Name; /* [1] IA5String */
+ ComponentIA5String* dNSName; /* [2] IA5String */
+ ComponentORAddress* x400Address; /* [3] ORAddress */
+ ComponentName* directoryName; /* [4] Name */
+ ComponentEDIPartyName* ediPartyName; /* [5] EDIPartyName */
+ ComponentIA5String* uniformResourceIdentifier; /* [6] IA5String */
+ ComponentOcts* iPAddress; /* [7] OCTET STRING */
+ ComponentOid* registeredID; /* [8] OBJECT IDENTIFIER */
+ } a;
+} ComponentGeneralName;
+
+int MatchingComponentGeneralName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentGeneralName PROTO (( void* mem_op, ComponentReference *cr, ComponentGeneralName *comp ));
+
+
+int BDecComponentGeneralName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentGeneralName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentGeneralName PROTO (( void* mem_op, GenBuf * b, ComponentGeneralName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentGeneralNames; /* SEQUENCE SIZE 1..MAX OF GeneralName */
+
+int MatchingComponentGeneralNames PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentGeneralNames PROTO (( void* mem_op, ComponentReference *cr, ComponentGeneralNames *comp ));
+
+
+int BDecComponentGeneralNames PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentGeneralNames **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentGeneralNames PROTO (( void* mem_op, GenBuf * b, ComponentGeneralNames **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AuthorityKeyIdentifier /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentKeyIdentifier keyIdentifier; /* [0] KeyIdentifier OPTIONAL */
+ ComponentGeneralNames* authorityCertIssuer; /* [1] GeneralNames OPTIONAL */
+ ComponentCertificateSerialNumber* authorityCertSerialNumber; /* [2] CertificateSerialNumber OPTIONAL */
+} ComponentAuthorityKeyIdentifier;
+
+int MatchingComponentAuthorityKeyIdentifier PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAuthorityKeyIdentifier PROTO (( void* mem_op, ComponentReference *cr, ComponentAuthorityKeyIdentifier *comp ));
+
+
+int BDecComponentAuthorityKeyIdentifier PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAuthorityKeyIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAuthorityKeyIdentifier PROTO (( void* mem_op, GenBuf * b, ComponentAuthorityKeyIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+
+/* ========== Object Declarations ========== */
+
+
+/* ========== Object Set Declarations ========== */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* conditional include of authorityKeyIdentifier.h */
diff --git a/contrib/slapd-modules/comp_match/certificate.asn1 b/contrib/slapd-modules/comp_match/certificate.asn1
new file mode 100644
index 0000000..db81897
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/certificate.asn1
@@ -0,0 +1,175 @@
+AuthenticationFramework {joint-iso-itu-t ds(5) module(1) authenticationFramework(7) 4} DEFINITIONS ::=
+BEGIN
+-- based on RFC 3280 and X.509
+
+Certificate ::= SEQUENCE {
+ toBeSigned TBSCertificate,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING
+}
+
+TBSCertificate ::= SEQUENCE {
+ version [0] Version DEFAULT v1,
+ serialNumber CertificateSerialNumber,
+ signature AlgorithmIdentifier,
+ issuer Name,
+ validity Validity,
+ subject Name,
+ subjectPublicKeyInfo SubjectPublicKeyInfo,
+ issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ -- if present, version shall be v2 or v3
+ subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ -- if present, version shall be v2 or v3
+ extensions [3] Extensions OPTIONAL
+ -- If present, version shall be v3 -- }
+
+Version ::= INTEGER { v1(0), v2(1), v3(2) }
+
+CertificateSerialNumber ::= INTEGER
+
+AlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY algorithm OPTIONAL -- DSA, SHA-1--
+}
+
+Name ::= CHOICE {
+ rdnSequence RDNSequence }
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+
+AttributeTypeAndValue ::= SEQUENCE {
+ type AttributeType,
+ value ANY DEFINED BY type}
+
+AttributeType ::= OBJECT IDENTIFIER
+
+Validity ::= SEQUENCE {
+ notBefore Time,
+ notAfter Time }
+
+UniqueIdentifier ::= BIT STRING
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING }
+
+Time ::= CHOICE {
+ utcTime UTCTime,
+ generalizedTime GeneralizedTime }
+
+Extensions ::= SEQUENCE SIZE(1..MAX) OF Extension
+
+Extension ::= SEQUENCE {
+ extnID OBJECT IDENTIFIER,
+ critical BOOLEAN DEFAULT FALSE,
+ extnValue OCTET STRING
+-- contains a DER encoding of a value of type &ExtnType
+-- for the extension object identified by extnId --
+}
+
+nullOid OBJECT-TYPE
+ SYNTAX NULL
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 1 4 }
+
+nullOid2 OBJECT-TYPE
+ SYNTAX NULL
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 1 1 }
+
+nullOid3 OBJECT-TYPE
+ SYNTAX NULL
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 1 5 }
+
+printableStringOid OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 3 }
+
+printableStringOid2 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 6 }
+
+printableStringOid3 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 7 }
+
+printableStringOid4 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 8 }
+
+printableStringOid5 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 10 }
+
+printableStringOid6 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 11 }
+
+printableStringOid7 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 0 9 2342 19200300 100 1 3 }
+
+
+iA5StringOid OBJECT-TYPE
+ SYNTAX IA5String
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 9 1 }
+
+octetStringOid OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 19 }
+
+octetStringOid2 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 16 840 1 113730 1 13 }
+
+octetStringOid3 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 14 }
+
+octetStringOid4 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 21 }
+
+octetStringOid5 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 20 }
+
+octetStringOid7 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 28 }
+
+END
diff --git a/contrib/slapd-modules/comp_match/certificate.c b/contrib/slapd-modules/comp_match/certificate.c
new file mode 100644
index 0000000..8b58bdb
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/certificate.c
@@ -0,0 +1,3249 @@
+/*
+ * certificate.c
+ * "AuthenticationFramework" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Sat Dec 11 11:22:49 2004
+ * The generated files are supposed to be compiled as a module for OpenLDAP Software
+ */
+
+#include "certificate.h"
+
+BDecComponentCertificateTop( void* mem_op, GenBuf* b, void **v, AsnLen* bytesDecoded,int mode) {
+ AsnTag tag;
+ AsnLen elmtLen;
+
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return BDecComponentCertificate( mem_op, b, tag, elmtLen, (ComponentCertificate**)v,(AsnLen*)bytesDecoded, mode );
+}
+
+void init_module_AuthenticationFramework() {
+ /* Register Certificate OID and its decoder */
+ InstallOidDecoderMapping( "2.5.4.36", NULL,
+ GDecComponentCertificate,
+ BDecComponentCertificateTop,
+ ExtractingComponentCertificate,
+ MatchingComponentCertificate );
+ InitAnyAuthenticationFramework();
+}
+
+void InitAnyAuthenticationFramework()
+{
+ AsnOid oid0 ={ 9, "\52\206\110\206\367\15\1\1\4" };
+ AsnOid oid1 ={ 9, "\52\206\110\206\367\15\1\1\1" };
+ AsnOid oid2 ={ 9, "\52\206\110\206\367\15\1\1\5" };
+ AsnOid oid3 ={ 3, "\125\4\3" };
+ AsnOid oid4 ={ 3, "\125\4\6" };
+ AsnOid oid5 ={ 3, "\125\4\7" };
+ AsnOid oid6 ={ 3, "\125\4\10" };
+ AsnOid oid7 ={ 3, "\125\4\12" };
+ AsnOid oid8 ={ 3, "\125\4\13" };
+ AsnOid oid9 ={ 10, "\11\222\46\211\223\362\54\144\1\3" };
+ AsnOid oid10 ={ 9, "\52\206\110\206\367\15\1\11\1" };
+ AsnOid oid11 ={ 3, "\125\35\23" };
+ AsnOid oid12 ={ 9, "\140\206\110\1\206\370\102\1\15" };
+ AsnOid oid13 ={ 3, "\125\35\16" };
+ AsnOid oid14 ={ 3, "\125\35\25" };
+ AsnOid oid15 ={ 3, "\125\35\24" };
+ AsnOid oid17 ={ 3, "\125\35\34" };
+
+
+ InstallAnyByComponentOid (nullOid_ANY_ID, &oid0, sizeof (ComponentNull), (EncodeFcn)BEncAsnNull, (gser_decoder_func*)GDecComponentNull, (ber_tag_decoder_func*)BDecComponentNullTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentNull,(FreeFcn)FreeComponentNull, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (nullOid2_ANY_ID, &oid1, sizeof (ComponentNull), (EncodeFcn)BEncAsnNull, (gser_decoder_func*)GDecComponentNull, (ber_tag_decoder_func*)BDecComponentNullTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentNull,(FreeFcn)FreeComponentNull, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (nullOid3_ANY_ID, &oid2, sizeof (ComponentNull), (EncodeFcn)BEncAsnNull, (gser_decoder_func*)GDecComponentNull, (ber_tag_decoder_func*)BDecComponentNullTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentNull,(FreeFcn)FreeComponentNull, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid_ANY_ID, &oid3, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid2_ANY_ID, &oid4, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid3_ANY_ID, &oid5, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid4_ANY_ID, &oid6, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid5_ANY_ID, &oid7, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid6_ANY_ID, &oid8, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid7_ANY_ID, &oid9, sizeof (ComponentTeletexString), (EncodeFcn)BEncTeletexString, (gser_decoder_func*)GDecComponentTeletexString, (ber_tag_decoder_func*)BDecComponentTeletexStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentTeletexString,(FreeFcn)FreeComponentTeletexString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (iA5StringOid_ANY_ID, &oid10, sizeof (ComponentIA5String), (EncodeFcn)BEncIA5String, (gser_decoder_func*)GDecComponentIA5String, (ber_tag_decoder_func*)BDecComponentIA5StringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentIA5String,(FreeFcn)FreeComponentIA5String, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid_ANY_ID, &oid11, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid2_ANY_ID, &oid12, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid3_ANY_ID, &oid13, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid4_ANY_ID, &oid14, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid5_ANY_ID, &oid15, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid7_ANY_ID, &oid17, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+} /* InitAnyAuthenticationFramework */
+
+int
+MatchingComponentAlgorithmIdentifier ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentAlgorithmIdentifier*)csi_attr)->algorithm, (ComponentSyntaxInfo*)&((ComponentAlgorithmIdentifier*)csi_assert)->algorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentAlgorithmIdentifier*)csi_attr)->parameters, (&((ComponentAlgorithmIdentifier*)csi_attr)->algorithm));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentAlgorithmIdentifier*)csi_attr)->parameters, (ComponentAny*)&((ComponentAlgorithmIdentifier*)csi_assert)->parameters);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentAlgorithmIdentifier */
+
+void*
+ExtractingComponentAlgorithmIdentifier ( void* mem_op, ComponentReference* cr, ComponentAlgorithmIdentifier *comp )
+{
+
+ if ( ( comp->algorithm.identifier.bv_val && strncmp(comp->algorithm.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->algorithm.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->algorithm;
+ else
+ return NULL;
+ }
+ if ( ( comp->parameters.identifier.bv_val && strncmp(comp->parameters.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->parameters.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->parameters;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->parameters;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentAlgorithmIdentifier */
+
+int
+BDecComponentAlgorithmIdentifier PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentAlgorithmIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentAlgorithmIdentifier *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->algorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->algorithm)->identifier.bv_val = (&k->algorithm)->id_buf;
+ (&k->algorithm)->identifier.bv_len = strlen("algorithm");
+ strcpy( (&k->algorithm)->identifier.bv_val, "algorithm");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BufPeekByte (b);
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone) {
+ rc = SetAnyTypeByComponentOid ((&k->parameters), (&k->algorithm));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->parameters), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->parameters)->identifier.bv_val = (&k->parameters)->id_buf;
+ (&k->parameters)->identifier.bv_len = strlen("parameters");
+ strcpy( (&k->parameters)->identifier.bv_val, "parameters");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAlgorithmIdentifier*) CompAlloc( mem_op, sizeof(ComponentAlgorithmIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAlgorithmIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAlgorithmIdentifier;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecAlgorithmIdentifier*/
+
+int
+GDecComponentAlgorithmIdentifier PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentAlgorithmIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentAlgorithmIdentifier *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "algorithm", strlen("algorithm") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->algorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->algorithm)->identifier.bv_val = peek_head;
+ (&k->algorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "parameters", strlen("parameters") ) == 0 ) {
+ rc = rc = SetAnyTypeByComponentOid ((&k->parameters), (&k->algorithm));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->parameters), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->parameters)->identifier.bv_val = peek_head;
+ (&k->parameters)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAlgorithmIdentifier*) CompAlloc( mem_op, sizeof(ComponentAlgorithmIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAlgorithmIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAlgorithmIdentifier;
+ return LDAP_SUCCESS;
+} /* GDecAlgorithmIdentifier*/
+
+
+int
+MatchingComponentTime ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentTime *v1, *v2;
+
+
+ v1 = (ComponentTime*)csi_attr;
+ v2 = (ComponentTime*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case TIME_UTCTIME :
+ rc = MatchingComponentUTCTime ( oid, (ComponentSyntaxInfo*)(v1->a.utcTime), (ComponentSyntaxInfo*)(v2->a.utcTime) );
+ break;
+ case TIME_GENERALIZEDTIME :
+ rc = MatchingComponentGeneralizedTime ( oid, (ComponentSyntaxInfo*)(v1->a.generalizedTime), (ComponentSyntaxInfo*)(v2->a.generalizedTime) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentTimeContent */
+
+void*
+ExtractingComponentTime ( void* mem_op, ComponentReference* cr, ComponentTime *comp )
+{
+
+
+ if( (comp->choiceId) == TIME_UTCTIME &&
+ (( comp->a.utcTime->identifier.bv_val && strncmp(comp->a.utcTime->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.utcTime->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.utcTime);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentUTCTime ( mem_op, cr, (comp->a.utcTime) );
+ };
+ }
+ if( (comp->choiceId) == TIME_GENERALIZEDTIME &&
+ (( comp->a.generalizedTime->identifier.bv_val && strncmp(comp->a.generalizedTime->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.generalizedTime->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.generalizedTime);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralizedTime ( mem_op, cr, (comp->a.generalizedTime) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentTime */
+
+int
+BDecComponentTime PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTime **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentTime *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE):
+ (k->choiceId) = TIME_UTCTIME;
+ rc = BDecComponentUTCTime (mem_op, b, tagId0, elmtLen0, (&k->a.utcTime), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utcTime)->identifier.bv_val = (k->a.utcTime)->id_buf;
+ (k->a.utcTime)->identifier.bv_len = strlen("utcTime");
+ strcpy( (k->a.utcTime)->identifier.bv_val, "utcTime");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE):
+ (k->choiceId) = TIME_GENERALIZEDTIME;
+ rc = BDecComponentGeneralizedTime (mem_op, b, tagId0, elmtLen0, (&k->a.generalizedTime), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.generalizedTime)->identifier.bv_val = (k->a.generalizedTime)->id_buf;
+ (k->a.generalizedTime)->identifier.bv_len = strlen("generalizedTime");
+ strcpy( (k->a.generalizedTime)->identifier.bv_val, "generalizedTime");
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTime*) CompAlloc( mem_op, sizeof(ComponentTime) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTime ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTime ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTime;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTime;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTimeContent */
+
+int
+GDecComponentTime PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTime **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTime *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("utcTime",peek_head, strlen("utcTime")) == 0){
+ (k->choiceId) = TIME_UTCTIME;
+ rc = GDecComponentUTCTime (mem_op, b, (&k->a.utcTime), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utcTime)->identifier.bv_val = peek_head;
+ (k->a.utcTime)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("generalizedTime",peek_head,strlen("generalizedTime")) == 0){
+ (k->choiceId) = TIME_GENERALIZEDTIME;
+ rc = GDecComponentGeneralizedTime (mem_op, b, (&k->a.generalizedTime), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.generalizedTime)->identifier.bv_val = peek_head;
+ (k->a.generalizedTime)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTime*) CompAlloc( mem_op, sizeof(ComponentTime) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTime ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTime ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTime;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTime;
+ return LDAP_SUCCESS;
+} /* GDecTimeContent */
+
+
+int
+MatchingComponentExtension ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_attr)->extnID, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_assert)->extnID );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBool ( oid, (ComponentSyntaxInfo*)((ComponentExtension*)csi_attr)->critical, (ComponentSyntaxInfo*)((ComponentExtension*)csi_assert)->critical );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentOcts ( oid, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_attr)->extnValue, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_assert)->extnValue );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentExtension */
+
+void*
+ExtractingComponentExtension ( void* mem_op, ComponentReference* cr, ComponentExtension *comp )
+{
+
+ if ( ( comp->extnID.identifier.bv_val && strncmp(comp->extnID.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extnID.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->extnID;
+ else
+ return NULL;
+ }
+ if ( ( comp->critical->identifier.bv_val && strncmp(comp->critical->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->critical->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->critical;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentBool ( mem_op, cr, comp->critical );
+ }
+ }
+ if ( ( comp->extnValue.identifier.bv_val && strncmp(comp->extnValue.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extnValue.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->extnValue;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->extnValue;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentExtension */
+
+int
+BDecComponentExtension PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentExtension **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentExtension *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->extnID), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnID)->identifier.bv_val = (&k->extnID)->id_buf;
+ (&k->extnID)->identifier.bv_len = strlen("extnID");
+ strcpy( (&k->extnID)->identifier.bv_val, "extnID");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BOOLEAN_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBool (mem_op, b, tagId1, elmtLen1, (&k->critical), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->critical)->identifier.bv_val = (k->critical)->id_buf;
+ (k->critical)->identifier.bv_len = strlen("critical");
+ strcpy( (k->critical)->identifier.bv_val, "critical");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OCTETSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, OCTETSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOcts (mem_op, b, tagId1, elmtLen1, (&k->extnValue), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnValue)->identifier.bv_val = (&k->extnValue)->id_buf;
+ (&k->extnValue)->identifier.bv_len = strlen("extnValue");
+ strcpy( (&k->extnValue)->identifier.bv_val, "extnValue");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if(!COMPONENTNOT_NULL ((k->critical)))
+ {
+(k->critical) = CompAlloc( mem_op, sizeof(ComponentBool));
+ (k->critical)->identifier.bv_val = (k->critical)->id_buf;
+ (k->critical)->identifier.bv_len = strlen("critical");
+ strcpy( (k->critical)->identifier.bv_val, "critical");
+ (k->critical)->value = 0;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtension*) CompAlloc( mem_op, sizeof(ComponentExtension) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtension ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtension ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtension;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtension;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecExtension*/
+
+int
+GDecComponentExtension PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentExtension **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentExtension *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "extnID", strlen("extnID") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->extnID), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnID)->identifier.bv_val = peek_head;
+ (&k->extnID)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "critical", strlen("critical") ) == 0 ) {
+ rc = GDecComponentBool (mem_op, b, (&k->critical), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->critical)->identifier.bv_val = peek_head;
+ ( k->critical)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ else {
+(k->critical) = CompAlloc( mem_op, sizeof(ComponentBool));
+ (k->critical)->value = 0;
+ }
+ if ( strncmp( peek_head, "extnValue", strlen("extnValue") ) == 0 ) {
+ rc = GDecComponentOcts (mem_op, b, (&k->extnValue), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnValue)->identifier.bv_val = peek_head;
+ (&k->extnValue)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtension*) CompAlloc( mem_op, sizeof(ComponentExtension) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtension ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtension ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtension;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtension;
+ return LDAP_SUCCESS;
+} /* GDecExtension*/
+
+
+int
+MatchingComponentAttributeTypeAndValue ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentAttributeType ( oid, (ComponentSyntaxInfo*)&((ComponentAttributeTypeAndValue*)csi_attr)->type, (ComponentSyntaxInfo*)&((ComponentAttributeTypeAndValue*)csi_assert)->type );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentAttributeTypeAndValue*)csi_attr)->value, (&((ComponentAttributeTypeAndValue*)csi_attr)->type));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentAttributeTypeAndValue*)csi_attr)->value, (ComponentAny*)&((ComponentAttributeTypeAndValue*)csi_assert)->value);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentAttributeTypeAndValue */
+
+void*
+ExtractingComponentAttributeTypeAndValue ( void* mem_op, ComponentReference* cr, ComponentAttributeTypeAndValue *comp )
+{
+
+ if ( ( comp->type.identifier.bv_val && strncmp(comp->type.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->type.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->type;
+ else
+ return NULL;
+ }
+ if ( ( comp->value.identifier.bv_val && strncmp(comp->value.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->value.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->value;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_SELECT ) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->value;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentAttributeTypeAndValue */
+
+int
+BDecComponentAttributeTypeAndValue PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentAttributeTypeAndValue **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentAttributeTypeAndValue *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAttributeType (mem_op, b, tagId1, elmtLen1, (&k->type), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type)->identifier.bv_val = (&k->type)->id_buf;
+ (&k->type)->identifier.bv_len = strlen("type");
+ strcpy( (&k->type)->identifier.bv_val, "type");
+ }
+ else
+ return -1;
+
+
+
+ {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->value), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = (&k->value)->id_buf;
+ (&k->value)->identifier.bv_len = strlen("value");
+ strcpy( (&k->value)->identifier.bv_val, "value");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAttributeTypeAndValue*) CompAlloc( mem_op, sizeof(ComponentAttributeTypeAndValue) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAttributeTypeAndValue;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAttributeTypeAndValue;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecAttributeTypeAndValue*/
+
+int
+GDecComponentAttributeTypeAndValue PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentAttributeTypeAndValue **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentAttributeTypeAndValue *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "type", strlen("type") ) == 0 ) {
+ rc = GDecComponentAttributeType (mem_op, b, (&k->type), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type)->identifier.bv_val = peek_head;
+ (&k->type)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "value", strlen("value") ) == 0 ) {
+ rc = rc = SetAnyTypeByComponentOid ((&k->value), (&k->type));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->value), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = peek_head;
+ (&k->value)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAttributeTypeAndValue*) CompAlloc( mem_op, sizeof(ComponentAttributeTypeAndValue) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAttributeTypeAndValue;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAttributeTypeAndValue;
+ return LDAP_SUCCESS;
+} /* GDecAttributeTypeAndValue*/
+
+
+int
+MatchingComponentValidity ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentValidity*)csi_attr)->notBefore, (ComponentSyntaxInfo*)((ComponentValidity*)csi_assert)->notBefore );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentValidity*)csi_attr)->notAfter, (ComponentSyntaxInfo*)((ComponentValidity*)csi_assert)->notAfter );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentValidity */
+
+void*
+ExtractingComponentValidity ( void* mem_op, ComponentReference* cr, ComponentValidity *comp )
+{
+
+ if ( ( comp->notBefore->identifier.bv_val && strncmp(comp->notBefore->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->notBefore->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->notBefore;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->notBefore );
+ }
+ }
+ if ( ( comp->notAfter->identifier.bv_val && strncmp(comp->notAfter->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->notAfter->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->notAfter;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->notAfter );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentValidity */
+
+int
+BDecComponentValidity PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentValidity **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentValidity *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->notBefore), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->notBefore)->identifier.bv_val = (k->notBefore)->id_buf;
+ (k->notBefore)->identifier.bv_len = strlen("notBefore");
+ strcpy( (k->notBefore)->identifier.bv_val, "notBefore");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->notAfter), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->notAfter)->identifier.bv_val = (k->notAfter)->id_buf;
+ (k->notAfter)->identifier.bv_len = strlen("notAfter");
+ strcpy( (k->notAfter)->identifier.bv_val, "notAfter");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentValidity*) CompAlloc( mem_op, sizeof(ComponentValidity) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentValidity ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentValidity ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentValidity;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentValidity;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecValidity*/
+
+int
+GDecComponentValidity PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentValidity **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentValidity *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "notBefore", strlen("notBefore") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->notBefore), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->notBefore)->identifier.bv_val = peek_head;
+ ( k->notBefore)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "notAfter", strlen("notAfter") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->notAfter), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->notAfter)->identifier.bv_val = peek_head;
+ ( k->notAfter)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentValidity*) CompAlloc( mem_op, sizeof(ComponentValidity) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentValidity ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentValidity ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentValidity;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentValidity;
+ return LDAP_SUCCESS;
+} /* GDecValidity*/
+
+
+int
+MatchingComponentSubjectPublicKeyInfo ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentSubjectPublicKeyInfo*)csi_attr)->algorithm, (ComponentSyntaxInfo*)((ComponentSubjectPublicKeyInfo*)csi_assert)->algorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBits ( oid, (ComponentSyntaxInfo*)&((ComponentSubjectPublicKeyInfo*)csi_attr)->subjectPublicKey, (ComponentSyntaxInfo*)&((ComponentSubjectPublicKeyInfo*)csi_assert)->subjectPublicKey );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentSubjectPublicKeyInfo */
+
+void*
+ExtractingComponentSubjectPublicKeyInfo ( void* mem_op, ComponentReference* cr, ComponentSubjectPublicKeyInfo *comp )
+{
+
+ if ( ( comp->algorithm->identifier.bv_val && strncmp(comp->algorithm->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->algorithm->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->algorithm;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->algorithm );
+ }
+ }
+ if ( ( comp->subjectPublicKey.identifier.bv_val && strncmp(comp->subjectPublicKey.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subjectPublicKey.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->subjectPublicKey;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->subjectPublicKey;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentSubjectPublicKeyInfo */
+
+int
+BDecComponentSubjectPublicKeyInfo PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentSubjectPublicKeyInfo **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentSubjectPublicKeyInfo *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->algorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->algorithm)->identifier.bv_val = (k->algorithm)->id_buf;
+ (k->algorithm)->identifier.bv_len = strlen("algorithm");
+ strcpy( (k->algorithm)->identifier.bv_val, "algorithm");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBits (mem_op, b, tagId1, elmtLen1, (&k->subjectPublicKey), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectPublicKey)->identifier.bv_val = (&k->subjectPublicKey)->id_buf;
+ (&k->subjectPublicKey)->identifier.bv_len = strlen("subjectPublicKey");
+ strcpy( (&k->subjectPublicKey)->identifier.bv_val, "subjectPublicKey");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentSubjectPublicKeyInfo*) CompAlloc( mem_op, sizeof(ComponentSubjectPublicKeyInfo) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentSubjectPublicKeyInfo;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentSubjectPublicKeyInfo;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecSubjectPublicKeyInfo*/
+
+int
+GDecComponentSubjectPublicKeyInfo PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentSubjectPublicKeyInfo **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentSubjectPublicKeyInfo *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "algorithm", strlen("algorithm") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->algorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->algorithm)->identifier.bv_val = peek_head;
+ ( k->algorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subjectPublicKey", strlen("subjectPublicKey") ) == 0 ) {
+ rc = GDecComponentBits (mem_op, b, (&k->subjectPublicKey), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectPublicKey)->identifier.bv_val = peek_head;
+ (&k->subjectPublicKey)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentSubjectPublicKeyInfo*) CompAlloc( mem_op, sizeof(ComponentSubjectPublicKeyInfo) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentSubjectPublicKeyInfo;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentSubjectPublicKeyInfo;
+ return LDAP_SUCCESS;
+} /* GDecSubjectPublicKeyInfo*/
+
+
+int
+MatchingComponentExtensions ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentExtensions*)csi_attr)->comp_list;
+ v2 = &((ComponentExtensions*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentExtension(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentExtensionsContent */
+
+void*
+ExtractingComponentExtensions ( void* mem_op, ComponentReference* cr, ComponentExtensions *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentExtension *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtension ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtension ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentExtensions */
+
+int
+BDecComponentExtensions PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentExtensions **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentExtensions *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentExtension));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentExtension **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentExtension**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentExtension (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtensions*) CompAlloc( mem_op, sizeof(ComponentExtensions) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtensions ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtensions ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtensions;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtensions;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecExtensionsContent */
+
+int
+GDecComponentExtensions PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentExtensions **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentExtensions *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentExtension ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentExtension **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentExtension**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentExtension (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtensions*) CompAlloc( mem_op, sizeof(ComponentExtensions) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtensions ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtensions ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtensions;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtensions;
+ return LDAP_SUCCESS;
+} /* GDecExtensionsContent */
+
+
+int
+MatchingComponentRelativeDistinguishedName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentRelativeDistinguishedName*)csi_attr)->comp_list;
+ v2 = &((ComponentRelativeDistinguishedName*)csi_assert)->comp_list;
+ AsnListInit( &t_list, 0 );
+ if( AsnListCount( v1 ) != AsnListCount( v2 ) )
+ return LDAP_COMPARE_FALSE;
+ FOR_EACH_LIST_ELMT (component1, v1)
+ {
+ FOR_EACH_LIST_ELMT(component2, v2)
+ {
+ if( MatchingComponentAttributeTypeAndValue(oid, (ComponentSyntaxInfo*)component1,(ComponentSyntaxInfo*)component2) == LDAP_COMPARE_TRUE ) {
+ AsnElmtMove( v2, &t_list );
+ break;
+ }
+ } /* end of inner for */
+ } /* end of outer for */
+
+ if( AsnListCount( v2 ) == 0 )
+ rc = LDAP_COMPARE_TRUE;
+ else
+ rc = LDAP_COMPARE_FALSE;
+ AsnListMove( &t_list, v2 );
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ return rc;
+} /* BMatchingComponentRelativeDistinguishedNameContent */
+
+void*
+ExtractingComponentRelativeDistinguishedName ( void* mem_op, ComponentReference* cr, ComponentRelativeDistinguishedName *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentAttributeTypeAndValue *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAttributeTypeAndValue ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAttributeTypeAndValue ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentRelativeDistinguishedName */
+
+int
+BDecComponentRelativeDistinguishedName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentRelativeDistinguishedName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentRelativeDistinguishedName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentAttributeTypeAndValue));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentAttributeTypeAndValue **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentAttributeTypeAndValue**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentAttributeTypeAndValue (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRelativeDistinguishedName*) CompAlloc( mem_op, sizeof(ComponentRelativeDistinguishedName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)ConvertRDN2RFC2253;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRelativeDistinguishedName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RelativeDistinguishedName;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRelativeDistinguishedName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecRelativeDistinguishedNameContent */
+
+int
+GDecComponentRelativeDistinguishedName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentRelativeDistinguishedName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentRelativeDistinguishedName *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentAttributeTypeAndValue ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentAttributeTypeAndValue **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentAttributeTypeAndValue**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentAttributeTypeAndValue (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRelativeDistinguishedName*) CompAlloc( mem_op, sizeof(ComponentRelativeDistinguishedName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRelativeDistinguishedName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RelativeDistinguishedName;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRelativeDistinguishedName;
+ return LDAP_SUCCESS;
+} /* GDecRelativeDistinguishedNameContent */
+
+
+int
+MatchingComponentRDNSequence ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentRDNSequence*)csi_attr)->comp_list;
+ v2 = &((ComponentRDNSequence*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentRelativeDistinguishedName(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentRDNSequenceContent */
+
+void*
+ExtractingComponentRDNSequence ( void* mem_op, ComponentReference* cr, ComponentRDNSequence *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentRelativeDistinguishedName *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentRelativeDistinguishedName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentRelativeDistinguishedName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentRDNSequence */
+
+int
+BDecComponentRDNSequence PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentRDNSequence **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentRDNSequence *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentRelativeDistinguishedName));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentRelativeDistinguishedName **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SET_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentRelativeDistinguishedName**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentRelativeDistinguishedName (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRDNSequence*) CompAlloc( mem_op, sizeof(ComponentRDNSequence) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ldap_encoder = (encoder_func*) ConvertRDNSequence2RFC2253;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRDNSequence ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRDNSequence ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRDNSequence;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RDNSequence;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRDNSequence;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecRDNSequenceContent */
+
+int
+GDecComponentRDNSequence PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentRDNSequence **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentRDNSequence *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentRelativeDistinguishedName ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentRelativeDistinguishedName **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentRelativeDistinguishedName**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentRelativeDistinguishedName (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRDNSequence*) CompAlloc( mem_op, sizeof(ComponentRDNSequence) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)ConvertRDNSequence2RFC2253;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRDNSequence ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRDNSequence ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRDNSequence;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RDNSequence ;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRDNSequence;
+ return LDAP_SUCCESS;
+} /* GDecRDNSequenceContent */
+
+
+int
+MatchingComponentName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentName *v1, *v2;
+
+
+ v1 = (ComponentName*)csi_attr;
+ v2 = (ComponentName*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case NAME_RDNSEQUENCE :
+ rc = MatchingComponentRDNSequence ( oid, (ComponentSyntaxInfo*)(v1->a.rdnSequence), (ComponentSyntaxInfo*)(v2->a.rdnSequence) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentNameContent */
+
+void*
+ExtractingComponentName ( void* mem_op, ComponentReference* cr, ComponentName *comp )
+{
+
+
+ if( (comp->choiceId) == NAME_RDNSEQUENCE &&
+ (( comp->a.rdnSequence->identifier.bv_val && strncmp(comp->a.rdnSequence->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.rdnSequence->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.rdnSequence);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentRDNSequence ( mem_op, cr, (comp->a.rdnSequence) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentName */
+
+int
+BDecComponentName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE):
+ (k->choiceId) = NAME_RDNSEQUENCE;
+ rc = BDecComponentRDNSequence (mem_op, b, tagId0, elmtLen0, (&k->a.rdnSequence), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rdnSequence)->identifier.bv_val = (k->a.rdnSequence)->id_buf;
+ (k->a.rdnSequence)->identifier.bv_len = strlen("rdnSequence");
+ strcpy( (k->a.rdnSequence)->identifier.bv_val, "rdnSequence");
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentName*) CompAlloc( mem_op, sizeof(ComponentName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecNameContent */
+
+int
+GDecComponentName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("rdnSequence",peek_head, strlen("rdnSequence")) == 0){
+ (k->choiceId) = NAME_RDNSEQUENCE;
+ rc = GDecComponentRDNSequence (mem_op, b, (&k->a.rdnSequence), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rdnSequence)->identifier.bv_val = peek_head;
+ (k->a.rdnSequence)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentName*) CompAlloc( mem_op, sizeof(ComponentName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentName;
+ return LDAP_SUCCESS;
+} /* GDecNameContent */
+
+
+int
+MatchingComponentTBSCertificate ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentVersion ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->version, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->version );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentCertificateSerialNumber ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_attr)->serialNumber, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_assert)->serialNumber );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->signature, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->issuer, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->issuer );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentValidity ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->validity, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->validity );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->subject, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->subject );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentSubjectPublicKeyInfo ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->subjectPublicKeyInfo, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->subjectPublicKeyInfo );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentUniqueIdentifier ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_attr)->issuerUniqueIdentifier, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_assert)->issuerUniqueIdentifier );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentUniqueIdentifier ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_attr)->subjectUniqueIdentifier, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_assert)->subjectUniqueIdentifier );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertificate*)csi_attr)->extensions ) ) {
+ rc = MatchingComponentExtensions ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->extensions, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->extensions );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertificate */
+
+void*
+ExtractingComponentTBSCertificate ( void* mem_op, ComponentReference* cr, ComponentTBSCertificate *comp )
+{
+
+ if ( ( comp->version->identifier.bv_val && strncmp(comp->version->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->version->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->version;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentVersion ( mem_op, cr, comp->version );
+ }
+ }
+ if ( ( comp->serialNumber.identifier.bv_val && strncmp(comp->serialNumber.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->serialNumber.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->serialNumber;
+ else
+ return NULL;
+ }
+ if ( ( comp->signature->identifier.bv_val && strncmp(comp->signature->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signature;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signature );
+ }
+ }
+ if ( ( comp->issuer->identifier.bv_val && strncmp(comp->issuer->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->issuer->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->issuer;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, comp->issuer );
+ }
+ }
+ if ( ( comp->validity->identifier.bv_val && strncmp(comp->validity->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->validity->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->validity;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentValidity ( mem_op, cr, comp->validity );
+ }
+ }
+ if ( ( comp->subject->identifier.bv_val && strncmp(comp->subject->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subject->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->subject;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, comp->subject );
+ }
+ }
+ if ( ( comp->subjectPublicKeyInfo->identifier.bv_val && strncmp(comp->subjectPublicKeyInfo->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subjectPublicKeyInfo->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->subjectPublicKeyInfo;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentSubjectPublicKeyInfo ( mem_op, cr, comp->subjectPublicKeyInfo );
+ }
+ }
+ if ( ( comp->issuerUniqueIdentifier.identifier.bv_val && strncmp(comp->issuerUniqueIdentifier.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->issuerUniqueIdentifier.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->issuerUniqueIdentifier;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->issuerUniqueIdentifier;
+ } else {
+ return NULL;
+ }
+ }
+ if ( ( comp->subjectUniqueIdentifier.identifier.bv_val && strncmp(comp->subjectUniqueIdentifier.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subjectUniqueIdentifier.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->subjectUniqueIdentifier;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->subjectUniqueIdentifier;
+ } else {
+ return NULL;
+ }
+ }
+ if ( ( comp->extensions->identifier.bv_val && strncmp(comp->extensions->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extensions->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->extensions;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtensions ( mem_op, cr, comp->extensions );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentTBSCertificate */
+
+int
+BDecComponentTBSCertificate PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertificate *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tagId2 = BDecTag (b, &totalElmtsLen1 );
+
+ if (tagId2 != MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentVersion (mem_op, b, tagId2, elmtLen2, (&k->version), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->version)->identifier.bv_val = (k->version)->id_buf;
+ (k->version)->identifier.bv_len = strlen("version");
+ strcpy( (k->version)->identifier.bv_val, "version");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentCertificateSerialNumber (mem_op, b, tagId1, elmtLen1, (&k->serialNumber), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->serialNumber)->identifier.bv_val = (&k->serialNumber)->id_buf;
+ (&k->serialNumber)->identifier.bv_len = strlen("serialNumber");
+ strcpy( (&k->serialNumber)->identifier.bv_val, "serialNumber");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signature)->identifier.bv_val = (k->signature)->id_buf;
+ (k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (k->signature)->identifier.bv_val, "signature");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->issuer), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->issuer)->identifier.bv_val = (k->issuer)->id_buf;
+ (k->issuer)->identifier.bv_len = strlen("issuer");
+ strcpy( (k->issuer)->identifier.bv_val, "issuer");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentValidity (mem_op, b, tagId1, elmtLen1, (&k->validity), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->validity)->identifier.bv_val = (k->validity)->id_buf;
+ (k->validity)->identifier.bv_len = strlen("validity");
+ strcpy( (k->validity)->identifier.bv_val, "validity");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->subject), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->subject)->identifier.bv_val = (k->subject)->id_buf;
+ (k->subject)->identifier.bv_len = strlen("subject");
+ strcpy( (k->subject)->identifier.bv_val, "subject");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentSubjectPublicKeyInfo (mem_op, b, tagId1, elmtLen1, (&k->subjectPublicKeyInfo), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->subjectPublicKeyInfo)->identifier.bv_val = (k->subjectPublicKeyInfo)->id_buf;
+ (k->subjectPublicKeyInfo)->identifier.bv_len = strlen("subjectPublicKeyInfo");
+ strcpy( (k->subjectPublicKeyInfo)->identifier.bv_val, "subjectPublicKeyInfo");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 1)) ||
+(tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentUniqueIdentifier (mem_op, b, tagId1, elmtLen1, (&k->issuerUniqueIdentifier), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->issuerUniqueIdentifier)->identifier.bv_val = (&k->issuerUniqueIdentifier)->id_buf;
+ (&k->issuerUniqueIdentifier)->identifier.bv_len = strlen("issuerUniqueIdentifier");
+ strcpy( (&k->issuerUniqueIdentifier)->identifier.bv_val, "issuerUniqueIdentifier");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 2)) ||
+(tagId1 == MAKE_TAG_ID (CNTX, CONS, 2))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentUniqueIdentifier (mem_op, b, tagId1, elmtLen1, (&k->subjectUniqueIdentifier), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectUniqueIdentifier)->identifier.bv_val = (&k->subjectUniqueIdentifier)->id_buf;
+ (&k->subjectUniqueIdentifier)->identifier.bv_len = strlen("subjectUniqueIdentifier");
+ strcpy( (&k->subjectUniqueIdentifier)->identifier.bv_val, "subjectUniqueIdentifier");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 3))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tagId2 = BDecTag (b, &totalElmtsLen1 );
+
+ if (tagId2 != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentExtensions (mem_op, b, tagId2, elmtLen2, (&k->extensions), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->extensions)->identifier.bv_val = (k->extensions)->id_buf;
+ (k->extensions)->identifier.bv_len = strlen("extensions");
+ strcpy( (k->extensions)->identifier.bv_val, "extensions");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if(!COMPONENTNOT_NULL ((k->version)))
+ {
+(k->version) = CompAlloc( mem_op, sizeof(ComponentVersion));
+ (k->version)->identifier.bv_val = (k->version)->id_buf;
+ (k->version)->identifier.bv_len = strlen("version");
+ strcpy( (k->version)->identifier.bv_val, "version");
+ (k->version)->value = 0;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertificate*) CompAlloc( mem_op, sizeof(ComponentTBSCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertificate;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertificate*/
+
+int
+GDecComponentTBSCertificate PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertificate *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "version", strlen("version") ) == 0 ) {
+ rc = GDecComponentVersion (mem_op, b, (&k->version), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->version)->identifier.bv_val = peek_head;
+ ( k->version)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ else {
+(k->version) = CompAlloc( mem_op, sizeof(ComponentVersion));
+ (k->version)->value = 0;
+ }
+ if ( strncmp( peek_head, "serialNumber", strlen("serialNumber") ) == 0 ) {
+ rc = GDecComponentCertificateSerialNumber (mem_op, b, (&k->serialNumber), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->serialNumber)->identifier.bv_val = peek_head;
+ (&k->serialNumber)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signature)->identifier.bv_val = peek_head;
+ ( k->signature)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "issuer", strlen("issuer") ) == 0 ) {
+ rc = GDecComponentName (mem_op, b, (&k->issuer), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->issuer)->identifier.bv_val = peek_head;
+ ( k->issuer)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "validity", strlen("validity") ) == 0 ) {
+ rc = GDecComponentValidity (mem_op, b, (&k->validity), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->validity)->identifier.bv_val = peek_head;
+ ( k->validity)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subject", strlen("subject") ) == 0 ) {
+ rc = GDecComponentName (mem_op, b, (&k->subject), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->subject)->identifier.bv_val = peek_head;
+ ( k->subject)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subjectPublicKeyInfo", strlen("subjectPublicKeyInfo") ) == 0 ) {
+ rc = GDecComponentSubjectPublicKeyInfo (mem_op, b, (&k->subjectPublicKeyInfo), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->subjectPublicKeyInfo)->identifier.bv_val = peek_head;
+ ( k->subjectPublicKeyInfo)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "issuerUniqueIdentifier", strlen("issuerUniqueIdentifier") ) == 0 ) {
+ rc = GDecComponentUniqueIdentifier (mem_op, b, (&k->issuerUniqueIdentifier), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->issuerUniqueIdentifier)->identifier.bv_val = peek_head;
+ (&k->issuerUniqueIdentifier)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subjectUniqueIdentifier", strlen("subjectUniqueIdentifier") ) == 0 ) {
+ rc = GDecComponentUniqueIdentifier (mem_op, b, (&k->subjectUniqueIdentifier), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectUniqueIdentifier)->identifier.bv_val = peek_head;
+ (&k->subjectUniqueIdentifier)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "extensions", strlen("extensions") ) == 0 ) {
+ rc = GDecComponentExtensions (mem_op, b, (&k->extensions), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->extensions)->identifier.bv_val = peek_head;
+ ( k->extensions)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertificate*) CompAlloc( mem_op, sizeof(ComponentTBSCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertificate;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertificate*/
+
+
+int
+MatchingComponentCertificate ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentTBSCertificate ( oid, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_attr)->toBeSigned, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_assert)->toBeSigned );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_attr)->signatureAlgorithm, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_assert)->signatureAlgorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBits ( oid, (ComponentSyntaxInfo*)&((ComponentCertificate*)csi_attr)->signature, (ComponentSyntaxInfo*)&((ComponentCertificate*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentCertificate */
+
+void*
+ExtractingComponentCertificate ( void* mem_op, ComponentReference* cr, ComponentCertificate *comp )
+{
+
+ if ( ( comp->toBeSigned->identifier.bv_val && strncmp(comp->toBeSigned->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->toBeSigned->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->toBeSigned;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertificate ( mem_op, cr, comp->toBeSigned );
+ }
+ }
+ if ( ( comp->signatureAlgorithm->identifier.bv_val && strncmp(comp->signatureAlgorithm->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signatureAlgorithm->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signatureAlgorithm;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signatureAlgorithm );
+ }
+ }
+ if ( ( comp->signature.identifier.bv_val && strncmp(comp->signature.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->signature;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->signature;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentCertificate */
+
+int
+BDecComponentCertificate PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentCertificate *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTBSCertificate (mem_op, b, tagId1, elmtLen1, (&k->toBeSigned), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->toBeSigned)->identifier.bv_val = (k->toBeSigned)->id_buf;
+ (k->toBeSigned)->identifier.bv_len = strlen("toBeSigned");
+ strcpy( (k->toBeSigned)->identifier.bv_val, "toBeSigned");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signatureAlgorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signatureAlgorithm)->identifier.bv_val = (k->signatureAlgorithm)->id_buf;
+ (k->signatureAlgorithm)->identifier.bv_len = strlen("signatureAlgorithm");
+ strcpy( (k->signatureAlgorithm)->identifier.bv_val, "signatureAlgorithm");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBits (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = (&k->signature)->id_buf;
+ (&k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (&k->signature)->identifier.bv_val, "signature");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificate*) CompAlloc( mem_op, sizeof(ComponentCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificate;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecCertificate*/
+
+int
+GDecComponentCertificate PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentCertificate *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "toBeSigned", strlen("toBeSigned") ) == 0 ) {
+ rc = GDecComponentTBSCertificate (mem_op, b, (&k->toBeSigned), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->toBeSigned)->identifier.bv_val = peek_head;
+ ( k->toBeSigned)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signatureAlgorithm", strlen("signatureAlgorithm") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signatureAlgorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signatureAlgorithm)->identifier.bv_val = peek_head;
+ ( k->signatureAlgorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentBits (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = peek_head;
+ (&k->signature)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificate*) CompAlloc( mem_op, sizeof(ComponentCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificate;
+ return LDAP_SUCCESS;
+} /* GDecCertificate*/
+
+
diff --git a/contrib/slapd-modules/comp_match/certificate.h b/contrib/slapd-modules/comp_match/certificate.h
new file mode 100644
index 0000000..d1df75f
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/certificate.h
@@ -0,0 +1,379 @@
+
+#include "asn-incl.h"
+/*
+ * certificate.h
+ * "AuthenticationFramework" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Sat Dec 11 11:22:49 2004
+ * The generated files are strongly encouraged to be
+ * compiled as a module for OpenLDAP Software
+ */
+
+#ifndef _certificate_h_
+#define _certificate_h_
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+#include "componentlib.h"
+typedef enum AuthenticationFrameworkAnyId
+{
+ nullOid_ANY_ID = 0,
+ nullOid2_ANY_ID = 1,
+ nullOid3_ANY_ID = 2,
+ printableStringOid_ANY_ID = 3,
+ printableStringOid2_ANY_ID = 4,
+ printableStringOid3_ANY_ID = 5,
+ printableStringOid4_ANY_ID = 6,
+ printableStringOid5_ANY_ID = 7,
+ printableStringOid6_ANY_ID = 8,
+ printableStringOid7_ANY_ID = 9,
+ iA5StringOid_ANY_ID = 10,
+ octetStringOid_ANY_ID = 11,
+ octetStringOid2_ANY_ID = 12,
+ octetStringOid3_ANY_ID = 13,
+ octetStringOid4_ANY_ID = 14,
+ octetStringOid5_ANY_ID = 15,
+ octetStringOid7_ANY_ID = 17} AuthenticationFrameworkAnyId;
+
+void InitAnyAuthenticationFramework();
+
+
+#define V1 0
+#define V2 1
+#define V3 2
+
+typedef ComponentInt ComponentVersion; /* INTEGER { V1 (0), V2 (1), V3 (2) } */
+
+#define MatchingComponentVersion MatchingComponentInt
+
+#define ExtractingComponentVersion ExtractingComponentInt
+
+#define BDecComponentVersion BDecComponentInt
+
+#define GDecComponentVersion GDecComponentInt
+
+
+typedef ComponentInt ComponentCertificateSerialNumber; /* INTEGER */
+
+#define MatchingComponentCertificateSerialNumber MatchingComponentInt
+
+#define ExtractingComponentCertificateSerialNumber ExtractingComponentInt
+
+#define BDecComponentCertificateSerialNumber BDecComponentInt
+
+#define GDecComponentCertificateSerialNumber GDecComponentInt
+
+
+typedef ComponentOid ComponentAttributeType; /* OBJECT IDENTIFIER */
+
+#define MatchingComponentAttributeType MatchingComponentOid
+
+#define ExtractingComponentAttributeType ExtractingComponentOid
+
+#define BDecComponentAttributeType BDecComponentOid
+
+#define GDecComponentAttributeType GDecComponentOid
+
+
+typedef ComponentBits ComponentUniqueIdentifier; /* BIT STRING */
+
+#define MatchingComponentUniqueIdentifier MatchingComponentBits
+
+#define ExtractingComponentUniqueIdentifier ExtractingComponentBits
+
+#define BDecComponentUniqueIdentifier BDecComponentBits
+
+#define GDecComponentUniqueIdentifier GDecComponentBits
+
+
+typedef struct AlgorithmIdentifier /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid algorithm; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy parameters; /* ANY DEFINED BY algorithm OPTIONAL */
+} ComponentAlgorithmIdentifier;
+
+int MatchingComponentAlgorithmIdentifier PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAlgorithmIdentifier PROTO (( void* mem_op, ComponentReference *cr, ComponentAlgorithmIdentifier *comp ));
+
+
+int BDecComponentAlgorithmIdentifier PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAlgorithmIdentifier PROTO (( void* mem_op, GenBuf * b, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Time /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum TimeChoiceId
+ {
+ TIME_UTCTIME,
+ TIME_GENERALIZEDTIME
+ } choiceId;
+ union TimeChoiceUnion
+ {
+ ComponentUTCTime* utcTime; /* < unknown type id ?! > */
+ ComponentGeneralizedTime* generalizedTime; /* < unknown type id ?! > */
+ } a;
+} ComponentTime;
+
+int MatchingComponentTime PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTime PROTO (( void* mem_op, ComponentReference *cr, ComponentTime *comp ));
+
+
+int BDecComponentTime PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTime PROTO (( void* mem_op, GenBuf * b, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Extension /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid extnID; /* OBJECT IDENTIFIER */
+ ComponentBool* critical; /* BOOLEAN DEFAULT FALSE */
+ ComponentOcts extnValue; /* OCTET STRING */
+} ComponentExtension;
+
+int MatchingComponentExtension PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtension PROTO (( void* mem_op, ComponentReference *cr, ComponentExtension *comp ));
+
+
+int BDecComponentExtension PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtension PROTO (( void* mem_op, GenBuf * b, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAttributeType type; /* AttributeType */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} ComponentAttributeTypeAndValue;
+
+int MatchingComponentAttributeTypeAndValue PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAttributeTypeAndValue PROTO (( void* mem_op, ComponentReference *cr, ComponentAttributeTypeAndValue *comp ));
+
+
+int BDecComponentAttributeTypeAndValue PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAttributeTypeAndValue PROTO (( void* mem_op, GenBuf * b, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Validity /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTime* notBefore; /* Time */
+ ComponentTime* notAfter; /* Time */
+} ComponentValidity;
+
+int MatchingComponentValidity PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentValidity PROTO (( void* mem_op, ComponentReference *cr, ComponentValidity *comp ));
+
+
+int BDecComponentValidity PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentValidity PROTO (( void* mem_op, GenBuf * b, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct SubjectPublicKeyInfo /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAlgorithmIdentifier* algorithm; /* AlgorithmIdentifier */
+ ComponentBits subjectPublicKey; /* BIT STRING */
+} ComponentSubjectPublicKeyInfo;
+
+int MatchingComponentSubjectPublicKeyInfo PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentSubjectPublicKeyInfo PROTO (( void* mem_op, ComponentReference *cr, ComponentSubjectPublicKeyInfo *comp ));
+
+
+int BDecComponentSubjectPublicKeyInfo PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentSubjectPublicKeyInfo **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentSubjectPublicKeyInfo PROTO (( void* mem_op, GenBuf * b, ComponentSubjectPublicKeyInfo **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentExtensions; /* SEQUENCE SIZE 1..MAX OF Extension */
+
+int MatchingComponentExtensions PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtensions PROTO (( void* mem_op, ComponentReference *cr, ComponentExtensions *comp ));
+
+
+int BDecComponentExtensions PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtensions PROTO (( void* mem_op, GenBuf * b, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRelativeDistinguishedName; /* SET OF AttributeTypeAndValue */
+
+int MatchingComponentRelativeDistinguishedName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRelativeDistinguishedName PROTO (( void* mem_op, ComponentReference *cr, ComponentRelativeDistinguishedName *comp ));
+
+
+int BDecComponentRelativeDistinguishedName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRelativeDistinguishedName PROTO (( void* mem_op, GenBuf * b, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRDNSequence; /* SEQUENCE OF RelativeDistinguishedName */
+
+int MatchingComponentRDNSequence PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRDNSequence PROTO (( void* mem_op, ComponentReference *cr, ComponentRDNSequence *comp ));
+
+
+int BDecComponentRDNSequence PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRDNSequence PROTO (( void* mem_op, GenBuf * b, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Name /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum NameChoiceId
+ {
+ NAME_RDNSEQUENCE
+ } choiceId;
+ union NameChoiceUnion
+ {
+ ComponentRDNSequence* rdnSequence; /* RDNSequence */
+ } a;
+} ComponentName;
+
+int MatchingComponentName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentName PROTO (( void* mem_op, ComponentReference *cr, ComponentName *comp ));
+
+
+int BDecComponentName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentName PROTO (( void* mem_op, GenBuf * b, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct TBSCertificate /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentVersion* version; /* [0] Version DEFAULT v1 */
+ ComponentCertificateSerialNumber serialNumber; /* CertificateSerialNumber */
+ ComponentAlgorithmIdentifier* signature; /* AlgorithmIdentifier */
+ ComponentName* issuer; /* Name */
+ ComponentValidity* validity; /* Validity */
+ ComponentName* subject; /* Name */
+ ComponentSubjectPublicKeyInfo* subjectPublicKeyInfo; /* SubjectPublicKeyInfo */
+ ComponentUniqueIdentifier issuerUniqueIdentifier; /* [1] IMPLICIT UniqueIdentifier OPTIONAL */
+ ComponentUniqueIdentifier subjectUniqueIdentifier; /* [2] IMPLICIT UniqueIdentifier OPTIONAL */
+ ComponentExtensions* extensions; /* [3] Extensions OPTIONAL */
+} ComponentTBSCertificate;
+
+int MatchingComponentTBSCertificate PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertificate PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertificate *comp ));
+
+
+int BDecComponentTBSCertificate PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertificate PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Certificate /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTBSCertificate* toBeSigned; /* TBSCertificate */
+ ComponentAlgorithmIdentifier* signatureAlgorithm; /* AlgorithmIdentifier */
+ ComponentBits signature; /* BIT STRING */
+} ComponentCertificate;
+
+int MatchingComponentCertificate PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentCertificate PROTO (( void* mem_op, ComponentReference *cr, ComponentCertificate *comp ));
+
+
+int BDecComponentCertificate PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentCertificate PROTO (( void* mem_op, GenBuf * b, ComponentCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+
+/* ========== Object Declarations ========== */
+
+
+/* ========== Object Set Declarations ========== */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* conditional include of certificate.h */
diff --git a/contrib/slapd-modules/comp_match/componentlib.c b/contrib/slapd-modules/comp_match/componentlib.c
new file mode 100644
index 0000000..6f5460a
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/componentlib.c
@@ -0,0 +1,2370 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+
+#include "portable.h"
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include "slap.h"
+#include "component.h"
+
+#include "componentlib.h"
+#include "asn.h"
+#include <asn-gser.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#ifndef SLAPD_COMP_MATCH
+#define SLAPD_COMP_MATCH SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_COMP_MATCH
+/*
+ * Matching function : BIT STRING
+ */
+int
+MatchingComponentBits ( char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentBits *a, *b;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule(oid, (AsnTypeId)csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentBits*)csi_attr);
+ b = ((ComponentBits*)csi_assert);
+ rc = ( a->value.bitLen == b->value.bitLen &&
+ strncmp( a->value.bits,b->value.bits,a->value.bitLen ) == 0 );
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * Free function: BIT STRING
+ */
+void
+FreeComponentBits ( ComponentBits* v ) {
+ FreeAsnBits( &v->value );
+}
+
+/*
+ * GSER Encoder : BIT STRING
+ */
+int
+GEncComponentBits ( GenBuf *b, ComponentBits *in )
+{
+ GAsnBits bits = {0};
+
+ bits.value = in->value;
+ if ( !in )
+ return (-1);
+ return GEncAsnBitsContent ( b, &bits);
+}
+
+
+/*
+ * GSER Decoder : BIT STRING
+ */
+int
+GDecComponentBits ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentBits* k, **k2;
+ GAsnBits result;
+
+ k = (ComponentBits*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBits**) v;
+ *k2 = (ComponentBits*) CompAlloc( mem_op, sizeof( ComponentBits ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnBitsContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_BITSTRING);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : BIT STRING
+ */
+int
+BDecComponentBitsTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentBits ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentBits ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentBits* k, **k2;
+ AsnBits result;
+
+ k = (ComponentBits*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBits**) v;
+ *k2 = (ComponentBits*) CompAlloc( mem_op, sizeof( ComponentBits ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnBits ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnBitsContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+
+ if ( rc < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_BITSTRING);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component GSER BMPString Encoder
+ */
+int
+GEncComponentBMPString ( GenBuf *b, ComponentBMPString *in )
+{
+ GBMPString t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncBMPStringContent ( b, &t );
+}
+
+/*
+ * Component GSER BMPString Decoder
+ */
+int
+GDecComponentBMPString ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode)
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentBMPString* k, **k2;
+ GBMPString result;
+
+ k = (ComponentBMPString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBMPString**) v;
+ *k2 = (ComponentBMPString*) CompAlloc( mem_op, sizeof( ComponentBMPString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ *bytesDecoded = 0;
+
+ if ( GDecBMPStringContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_BMP_STR);
+
+ return LDAP_SUCCESS;
+
+}
+
+/*
+ * Component BER BMPString Decoder
+ */
+int
+BDecComponentBMPStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentBMPString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentBMPString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentBMPString* k, **k2;
+ BMPString result;
+
+ k = (ComponentBMPString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBMPString**) v;
+ *k2 = (ComponentBMPString*) CompAlloc( mem_op, sizeof( ComponentBMPString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecBMPString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecBMPStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+
+ if ( rc < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_BMP_STR);
+
+ return LDAP_SUCCESS;
+
+}
+
+/*
+ * Component GSER Encoder : UTF8 String
+ */
+int
+GEncComponentUTF8String ( GenBuf *b, ComponentUTF8String *in )
+{
+ GUTF8String t = {0};
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncUTF8StringContent ( b, &t );
+}
+
+/*
+ * Component GSER Decoder : UTF8 String
+ */
+int
+GDecComponentUTF8String ( void* mem_op, GenBuf *b, void *v,
+ AsnLen *bytesDecoded, int mode) {
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentUTF8String* k, **k2;
+ GUTF8String result;
+
+ k = (ComponentUTF8String*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentUTF8String**) v;
+ *k2 = (ComponentUTF8String*)CompAlloc( mem_op, sizeof( ComponentUTF8String ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ *bytesDecoded = 0;
+
+ if ( GDecUTF8StringContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_UTF8_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : UTF8String
+ */
+int
+BDecComponentUTF8StringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentUTF8String ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentUTF8String ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len,
+ void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentUTF8String* k, **k2;
+ UTF8String result;
+
+ k = (ComponentUTF8String*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentUTF8String**) v;
+ *k2 = (ComponentUTF8String*) CompAlloc( mem_op, sizeof( ComponentUTF8String ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecUTF8String ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecUTF8StringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_UTF8_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component GSER Encoder : Teletex String
+ */
+int
+GEncComponentTeletexString ( GenBuf *b, ComponentTeletexString *in )
+{
+ GTeletexString t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncTeletexStringContent ( b, &t );
+}
+
+/*
+ * Component GSER Decoder : Teletex String
+ */
+int
+GDecComponentTeletexString ( void* mem_op, GenBuf *b, void *v,
+ AsnLen *bytesDecoded, int mode) {
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentTeletexString* k, **k2;
+ GTeletexString result;
+
+ k = (ComponentTeletexString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentTeletexString**) v;
+ *k2 = (ComponentTeletexString*)CompAlloc( mem_op, sizeof( ComponentTeletexString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ *bytesDecoded = 0;
+
+ if ( GDecTeletexStringContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_VIDEOTEX_STR);
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Matching function : BOOLEAN
+ */
+int
+MatchingComponentBool(char* oid, ComponentSyntaxInfo* csi_attr,
+ ComponentSyntaxInfo* csi_assert )
+{
+ MatchingRule* mr;
+ ComponentBool *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+
+ a = ((ComponentBool*)csi_attr);
+ b = ((ComponentBool*)csi_assert);
+
+ return (a->value == b->value) ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : BOOLEAN
+ */
+int
+GEncComponentBool ( GenBuf *b, ComponentBool *in )
+{
+ GAsnBool t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnBoolContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : BOOLEAN
+ */
+int
+GDecComponentBool ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ ComponentBool* k, **k2;
+ GAsnBool result;
+
+ k = (ComponentBool*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBool**) v;
+ *k2 = (ComponentBool*) CompAlloc( mem_op, sizeof( ComponentBool ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnBoolContent( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_BOOLEAN);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : BOOLEAN
+ */
+int
+BDecComponentBoolTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentBool ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentBool ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ ComponentBool* k, **k2;
+ AsnBool result;
+
+ k = (ComponentBool*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBool**) v;
+ *k2 = (ComponentBool*) CompAlloc( mem_op, sizeof( ComponentBool ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnBool ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnBoolContent( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_BOOLEAN);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : ENUMERATE
+ */
+int
+MatchingComponentEnum ( char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentEnum *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentEnum*)csi_attr);
+ b = ((ComponentEnum*)csi_assert);
+ rc = (a->value == b->value);
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : ENUMERATE
+ */
+int
+GEncComponentEnum ( GenBuf *b, ComponentEnum *in )
+{
+ GAsnEnum t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnEnumContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : ENUMERATE
+ */
+int
+GDecComponentEnum ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentEnum* k, **k2;
+ GAsnEnum result;
+
+ k = (ComponentEnum*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentEnum**) v;
+ *k2 = (ComponentEnum*) CompAlloc( mem_op, sizeof( ComponentEnum ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnEnumContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value_identifier.bv_val = result.value_identifier;
+ k->value_identifier.bv_len = result.len;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentEnum;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEnum;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEnum;
+ k->comp_desc->cd_free = (comp_free_func*)NULL;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ENUMERATED;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEnum;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : ENUMERATE
+ */
+int
+BDecComponentEnumTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentEnum ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentEnum ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentEnum* k, **k2;
+ AsnEnum result;
+
+ k = (ComponentEnum*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentEnum**) v;
+ *k2 = (ComponentEnum*) CompAlloc( mem_op, sizeof( ComponentEnum ) );
+ if ( k ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnEnum ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnEnumContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentEnum;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEnum;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEnum;
+ k->comp_desc->cd_free = (comp_free_func*)NULL;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ENUMERATED;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEnum;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component GSER Encoder : IA5String
+ */
+int
+GEncComponentIA5Stirng ( GenBuf *b, ComponentIA5String* in )
+{
+ GIA5String t = {0};
+ t.value = in->value;
+ if ( !in || in->value.octetLen <= 0 ) return (-1);
+ return GEncIA5StringContent( b, &t );
+}
+
+/*
+ * Component BER Decoder : IA5String
+ */
+int
+BDecComponentIA5StringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentIA5String ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentIA5String ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentIA5String* k, **k2;
+ IA5String result;
+
+ k = (ComponentIA5String*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentIA5String**) v;
+ *k2 = (ComponentIA5String*) CompAlloc( mem_op, sizeof( ComponentIA5String ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecIA5String ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecIA5StringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentIA5String;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentIA5String;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentIA5String;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentIA5String;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_IA5_STR;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentIA5String;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : INTEGER
+ */
+int
+MatchingComponentInt(char* oid, ComponentSyntaxInfo* csi_attr,
+ ComponentSyntaxInfo* csi_assert )
+{
+ MatchingRule* mr;
+ ComponentInt *a, *b;
+
+ if( oid ) {
+ /* check if this ASN type's matching rule is overridden */
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ /* if existing function is overridden, call the overriding
+function*/
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentInt*)csi_attr);
+ b = ((ComponentInt*)csi_assert);
+
+ return ( a->value == b->value ) ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : INTEGER
+ */
+int
+GEncComponentInt ( GenBuf *b, ComponentInt* in )
+{
+ GAsnInt t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnIntContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : INTEGER
+ */
+int
+GDecComponentInt( void* mem_op, GenBuf * b, void *v, AsnLen *bytesDecoded, int mode)
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentInt* k, **k2;
+ GAsnInt result;
+
+ k = (ComponentInt*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentInt**) v;
+ *k2 = (ComponentInt*) CompAlloc( mem_op, sizeof( ComponentInt ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnIntContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_INTEGER );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : INTEGER
+ */
+int
+BDecComponentIntTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentInt ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentInt ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentInt* k, **k2;
+ AsnInt result;
+
+ k = (ComponentInt*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentInt**) v;
+ *k2 = (ComponentInt*) CompAlloc( mem_op, sizeof( ComponentInt ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnInt ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnIntContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_INTEGER );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : NULL
+ */
+int
+MatchingComponentNull ( char *oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ MatchingRule* mr;
+ ComponentNull *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentNull*)csi_attr);
+ b = ((ComponentNull*)csi_assert);
+
+ return (a->value == b->value) ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : NULL
+ */
+int
+GEncComponentNull ( GenBuf *b, ComponentNull *in )
+{
+ GAsnNull t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnNullContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : NULL
+ */
+int
+GDecComponentNull ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentNull* k, **k2;
+ GAsnNull result;
+
+ k = (ComponentNull*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentNull**) v;
+ *k2 = (ComponentNull*) CompAlloc( mem_op, sizeof( ComponentNull ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnNullContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentNull;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentNull;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentNull;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentNull;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_NULL;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentNull;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : NULL
+ */
+int
+BDecComponentNullTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ return BDecComponentNull ( mem_op, b, 0, 0, v,bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentNull ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentNull* k, **k2;
+ AsnNull result;
+
+ k = (ComponentNull*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentNull**) v;
+ *k2 = (ComponentNull*) CompAlloc( mem_op, sizeof( ComponentNull ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnNull ( mem_op, b, &result, bytesDecoded );
+ }
+ else {
+ rc = BDecAsnNullContent ( mem_op, b, tagId, len, &result, bytesDecoded);
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentNull;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentNull;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentNull;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentNull;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_NULL;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentNull;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : NumericString
+ */
+int
+BDecComponentNumericStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentNumericString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentNumericString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentNumericString* k, **k2;
+ NumericString result;
+
+ k = (ComponentNumericString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentNumericString**) v;
+ *k2 = (ComponentNumericString*) CompAlloc( mem_op, sizeof( ComponentNumericString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecNumericString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecNumericStringContent ( mem_op, b, tagId, len, &result, bytesDecoded);
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentNumericString;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentNumericString;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentNumericString;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentNumericString;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_NUMERIC_STR;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentNumericString;
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Free function : OCTET STRING
+ */
+void
+FreeComponentOcts ( ComponentOcts* v) {
+ FreeAsnOcts( &v->value );
+}
+
+/*
+ * Matching function : OCTET STRING
+ */
+int
+MatchingComponentOcts ( char* oid, ComponentSyntaxInfo* csi_attr,
+ ComponentSyntaxInfo* csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentOcts *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = (ComponentOcts*) csi_attr;
+ b = (ComponentOcts*) csi_assert;
+ /* Assume that both of OCTET string has end of string character */
+ if ( (a->value.octetLen == b->value.octetLen) &&
+ strncmp ( a->value.octs, b->value.octs, a->value.octetLen ) == 0 )
+ return LDAP_COMPARE_TRUE;
+ else
+ return LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : OCTET STRING
+ */
+int
+GEncComponentOcts ( GenBuf* b, ComponentOcts *in )
+{
+ GAsnOcts t = {0};
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+
+ t.value = in->value;
+ return GEncAsnOctsContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : OCTET STRING
+ */
+int
+GDecComponentOcts ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char *peek_head, *data;
+ int i, j, strLen;
+ void* component_values;
+ ComponentOcts* k, **k2;
+ GAsnOcts result;
+
+ k = (ComponentOcts*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOcts**) v;
+ *k2 = (ComponentOcts*) CompAlloc( mem_op, sizeof( ComponentOcts ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnOctsContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentOcts;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOcts;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOcts;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentOcts;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_OCTETSTRING;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOcts;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : OCTET STRING
+ */
+int
+BDecComponentOctsTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentOcts ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentOcts ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char *peek_head, *data;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentOcts* k, **k2;
+ AsnOcts result;
+
+ k = (ComponentOcts*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOcts**) v;
+ *k2 = (ComponentOcts*) CompAlloc( mem_op, sizeof( ComponentOcts ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnOcts ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnOctsContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentOcts;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOcts;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOcts;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentOcts;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_OCTETSTRING;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOcts;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : OBJECT IDENTIFIER
+ */
+int
+MatchingComponentOid ( char *oid, ComponentSyntaxInfo *csi_attr ,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentOid *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+
+ a = (ComponentOid*)csi_attr;
+ b = (ComponentOid*)csi_assert;
+ if ( a->value.octetLen != b->value.octetLen )
+ return LDAP_COMPARE_FALSE;
+ rc = ( strncmp( a->value.octs, b->value.octs, a->value.octetLen ) == 0 );
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : OID
+ */
+GEncComponentOid ( GenBuf *b, ComponentOid *in )
+{
+ GAsnOid t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnOidContent( b, (GAsnOcts*)&t );
+}
+
+/*
+ * GSER Decoder : OID
+ */
+int
+GDecAsnDescOidContent ( void* mem_op, GenBuf *b, GAsnOid *result, AsnLen *bytesDecoded ){
+ AttributeType *ad_type;
+ struct berval name;
+ char* peek_head;
+ int strLen;
+
+ strLen = LocateNextGSERToken ( mem_op, b, &peek_head, GSER_NO_COPY );
+ name.bv_val = peek_head;
+ name.bv_len = strLen;
+
+ ad_type = at_bvfind( &name );
+
+ if ( !ad_type )
+ return LDAP_DECODING_ERROR;
+
+ peek_head = ad_type->sat_atype.at_oid;
+ strLen = strlen ( peek_head );
+
+ result->value.octs = (char*)EncodeComponentOid ( mem_op, peek_head , &strLen );
+ result->value.octetLen = strLen;
+ return LDAP_SUCCESS;
+}
+
+int
+IsNumericOid ( char* peek_head , int strLen ) {
+ int i;
+ int num_dot;
+ for ( i = 0, num_dot = 0 ; i < strLen ; i++ ) {
+ if ( peek_head[i] == '.' ) num_dot++;
+ else if ( peek_head[i] > '9' || peek_head[i] < '0' )
+ return (-1);
+ }
+ if ( num_dot )
+ return (1);
+ else
+ return (-1);
+}
+
+int
+GDecComponentOid ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentOid* k, **k2;
+ GAsnOid result;
+
+ k = (ComponentOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOid**) v;
+ *k2 = (ComponentOid*) CompAlloc( mem_op, sizeof( ComponentOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ strLen = LocateNextGSERToken ( mem_op, b, &peek_head, GSER_PEEK );
+ if ( IsNumericOid ( peek_head , strLen ) >= 1 ) {
+ /* numeric-oid */
+ if ( GDecAsnOidContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ }
+ else {
+ /*descr*/
+ if ( GDecAsnDescOidContent ( mem_op, b, &result, bytesDecoded ) < 0 ){
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : OID
+ */
+int
+BDecComponentOidTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentOid ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentOid ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentOid* k, **k2;
+ AsnOid result;
+
+ k = (ComponentOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOid**) v;
+ *k2 = (ComponentOid*) CompAlloc( mem_op, sizeof( ComponentOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnOid ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnOidContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : PrintableString
+ */
+
+int
+BDecComponentPrintableStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ return BDecComponentPrintableString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentPrintableString( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentPrintableString* k, **k2;
+ AsnOid result;
+
+ k = (ComponentPrintableString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentPrintableString**) v;
+ *k2 = (ComponentPrintableString*) CompAlloc( mem_op, sizeof( ComponentPrintableString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ) {
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecPrintableString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecPrintableStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_PRINTABLE_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : TeletexString
+ */
+
+int
+BDecComponentTeletexStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ return BDecComponentTeletexString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentTeletexString( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentTeletexString* k, **k2;
+ AsnOid result;
+
+ k = (ComponentTeletexString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentTeletexString**) v;
+ *k2 = (ComponentTeletexString*) CompAlloc( mem_op, sizeof( ComponentTeletexString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ) {
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecTeletexString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecTeletexStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_T61_STR);
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Matching function : Real
+ */
+int
+MatchingComponentReal (char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentReal *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = (ComponentReal*)csi_attr;
+ b = (ComponentReal*)csi_assert;
+ rc = (a->value == b->value);
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : Real
+ */
+int
+GEncComponentReal ( GenBuf *b, ComponentReal *in )
+{
+ GAsnReal t = {0};
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnRealContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : Real
+ */
+int
+GDecComponentReal ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentReal* k, **k2;
+ GAsnReal result;
+
+ k = (ComponentReal*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentReal**) v;
+ *k2 = (ComponentReal*) CompAlloc( mem_op, sizeof( ComponentReal ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnRealContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_REAL);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : Real
+ */
+int
+BDecComponentRealTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentReal ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentReal ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentReal* k, **k2;
+ AsnReal result;
+
+ k = (ComponentReal*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentReal**) v;
+ *k2 = (ComponentReal*) CompAlloc( mem_op, sizeof( ComponentReal ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnReal ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnRealContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_REAL);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : Relative OID
+ */
+int
+MatchingComponentRelativeOid ( char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentRelativeOid *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+
+ a = (ComponentRelativeOid*)csi_attr;
+ b = (ComponentRelativeOid*)csi_assert;
+
+ if ( a->value.octetLen != b->value.octetLen )
+ return LDAP_COMPARE_FALSE;
+ rc = ( strncmp( a->value.octs, b->value.octs, a->value.octetLen ) == 0 );
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : RELATIVE_OID.
+ */
+int
+GEncComponentRelativeOid ( GenBuf *b, ComponentRelativeOid *in )
+{
+ GAsnRelativeOid t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnRelativeOidContent ( b , (GAsnOcts*)&t );
+}
+
+/*
+ * GSER Decoder : RELATIVE_OID.
+ */
+int
+GDecComponentRelativeOid ( void* mem_op, GenBuf *b,void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentRelativeOid* k, **k2;
+ GAsnRelativeOid result;
+
+ k = (ComponentRelativeOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentRelativeOid**) v;
+ *k2 = (ComponentRelativeOid*) CompAlloc( mem_op, sizeof( ComponentRelativeOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnRelativeOidContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : RELATIVE_OID.
+ */
+int
+BDecComponentRelativeOidTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentRelativeOid ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentRelativeOid ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentRelativeOid* k, **k2;
+ AsnRelativeOid result;
+
+ k = (ComponentRelativeOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentRelativeOid**) v;
+ *k2 = (ComponentRelativeOid*) CompAlloc( mem_op, sizeof( ComponentRelativeOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnRelativeOid ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnRelativeOidContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * GSER Encoder : UniversalString
+ */
+int
+GEncComponentUniversalString ( GenBuf *b, ComponentUniversalString *in )
+{
+ GUniversalString t = {0};
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncUniversalStringContent( b, &t );
+}
+
+/*
+ * GSER Decoder : UniversalString
+ */
+static int
+UTF8toUniversalString( char* octs, int len){
+ /* Need to be Implemented */
+ return LDAP_SUCCESS;
+}
+
+int
+GDecComponentUniversalString ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ if ( GDecComponentUTF8String ( mem_op, b, v, bytesDecoded, mode) < 0 )
+ UTF8toUniversalString( ((ComponentUniversalString*)v)->value.octs, ((ComponentUniversalString*)v)->value.octetLen );
+ return LDAP_DECODING_ERROR;
+}
+
+/*
+ * Component BER Decoder : UniverseString
+ */
+int
+BDecComponentUniversalStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentUniversalString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentUniversalString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentUniversalString* k, **k2;
+ UniversalString result;
+
+ k = (ComponentUniversalString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentUniversalString**) v;
+ *k2 = (ComponentUniversalString*) CompAlloc( mem_op, sizeof( ComponentUniversalString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecUniversalString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecUniversalStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_UNIVERSAL_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : VisibleString
+ */
+int
+BDecComponentVisibleStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentVisibleString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentVisibleString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentVisibleString* k, **k2;
+ VisibleString result;
+
+ k = (ComponentVisibleString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentVisibleString**) v;
+ *k2 = (ComponentVisibleString*) CompAlloc( mem_op, sizeof( ComponentVisibleString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecVisibleString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecVisibleStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_VISIBLE_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Routines for handling an ANY DEFINED Type
+ */
+
+/* Check if the <select> type CR and the OID of the given ANY type */
+int
+CheckSelectTypeCorrect ( void* mem_op, ComponentAnyInfo* cai, struct berval* select ) {
+ int strLen;
+ AttributeType* ad_type;
+ char* oid;
+ char* result;
+
+ if ( IsNumericOid ( select->bv_val , select->bv_len ) ) {
+ oid = select->bv_val;
+ strLen = select->bv_len;
+ } else {
+ ad_type = at_bvfind( select );
+
+ if ( !ad_type )
+ return LDAP_DECODING_ERROR;
+
+ oid = ad_type->sat_atype.at_oid;
+ strLen = strlen ( oid );
+ }
+ result = EncodeComponentOid ( mem_op, oid , &strLen );
+ if ( !result || strLen <= 0 ) return (-1);
+
+ if ( cai->oid.octetLen == strLen &&
+ strncmp ( cai->oid.octs, result, strLen ) == 0 )
+ return (1);
+ else
+ return (-1);
+}
+
+int
+SetAnyTypeByComponentOid ( ComponentAny *v, ComponentOid *id ) {
+ Hash hash;
+ void *anyInfo;
+
+ /* use encoded oid as hash string */
+ hash = MakeHash (id->value.octs, id->value.octetLen);
+ if (CheckForAndReturnValue (anyOidHashTblG, hash, &anyInfo))
+ v->cai = (ComponentAnyInfo*) anyInfo;
+ else
+ v->cai = NULL;
+
+ if ( !v->cai ) {
+ /*
+ * If not found, the data considered as octet chunk
+ * Yet-to-be-Implemented
+ */
+ }
+ return LDAP_SUCCESS;
+}
+
+void
+SetAnyTypeByComponentInt( ComponentAny *v, ComponentInt id) {
+ Hash hash;
+ void *anyInfo;
+
+ hash = MakeHash ((char*)&id, sizeof (id));
+ if (CheckForAndReturnValue (anyIntHashTblG, hash, &anyInfo))
+ v->cai = (ComponentAnyInfo*) anyInfo;
+ else
+ v->cai = NULL;
+}
+
+int
+GEncComponentAny ( GenBuf *b, ComponentAny *in )
+{
+ if ( in->cai != NULL && in->cai->Encode != NULL )
+ return in->cai->Encode(b, &in->value );
+ else
+ return (-1);
+}
+
+int
+BEncComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode)
+{
+ ComponentAny *k, **k2;
+
+ k = (ComponentAny*) result;
+
+ if ( !k ) return (-1);
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentAny**) result;
+ *k2 = (ComponentAny*) CompAlloc( mem_op, sizeof( ComponentAny ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ((result->cai != NULL) && (result->cai->BER_Decode != NULL)) {
+ result->value = (void*) CompAlloc ( mem_op, result->cai->size );
+ if ( !result->value ) return 0;
+ result->cai->BER_Decode ( mem_op, b, result->value, (int*)bytesDecoded, DEC_ALLOC_MODE_1);
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentAny;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAny;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAny;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentAny;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ANY;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAny;
+ return LDAP_SUCCESS;
+ }
+ else {
+ Asn1Error ("ERROR - Component ANY Decode routine is NULL\n");
+ return 0;
+ }
+}
+
+int
+BDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode) {
+ int rc;
+ ComponentAny *k, **k2;
+
+ k = (ComponentAny*) result;
+
+ if ( !k ) return (-1);
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentAny**) result;
+ *k2 = (ComponentAny*) CompAlloc( mem_op, sizeof( ComponentAny ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ((result->cai != NULL) && (result->cai->BER_Decode != NULL)) {
+ result->cai->BER_Decode ( mem_op, b, (ComponentSyntaxInfo*)&result->value, (int*)bytesDecoded, DEC_ALLOC_MODE_0 );
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentAny;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAny;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAny;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentAny;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ANY;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAny;
+ return LDAP_SUCCESS;
+ }
+ else {
+ Asn1Error ("ERROR - Component ANY Decode routine is NULL\n");
+ return 0;
+ }
+}
+
+int
+GDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode) {
+ ComponentAny *k, **k2;
+
+ k = (ComponentAny*) result;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentAny**) result;
+ *k2 = (ComponentAny*) CompAlloc( mem_op, sizeof( ComponentAny ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+ if ((result->cai != NULL) && (result->cai->GSER_Decode != NULL)) {
+ result->value = (void*) CompAlloc ( mem_op, result->cai->size );
+ if ( !result->value ) return 0;
+ result->cai->GSER_Decode ( mem_op, b, result->value, (int*)bytesDecoded, DEC_ALLOC_MODE_1);
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentAny;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAny;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAny;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentAny;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type_id = BASICTYPE_ANY;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAny;
+ return LDAP_SUCCESS;
+ }
+ else {
+ Asn1Error ("ERROR - ANY Decode routine is NULL\n");
+ return 0;
+ }
+}
+
+int
+MatchingComponentAny (char* oid, ComponentAny *result, ComponentAny *result2) {
+ void *comp1, *comp2;
+
+ if ( result->comp_desc->cd_type_id == BASICTYPE_ANY )
+ comp1 = result->value;
+ else
+ comp1 = result;
+
+ if ( result2->comp_desc->cd_type_id == BASICTYPE_ANY )
+ comp2 = result2->value;
+ else
+ comp2 = result2;
+
+ if ((result->cai != NULL) && (result->cai->Match != NULL)) {
+ if ( result->comp_desc->cd_type_id == BASICTYPE_ANY )
+ return result->cai->Match(oid, comp1, comp2 );
+ else if ( result2->comp_desc->cd_type_id == BASICTYPE_ANY )
+ return result2->cai->Match(oid, comp1, comp2);
+ else
+ return LDAP_INVALID_SYNTAX;
+ }
+ else {
+ Asn1Error ("ERROR - ANY Matching routine is NULL\n");
+ return LDAP_INVALID_SYNTAX;
+ }
+}
+
+void*
+ExtractingComponentAny ( void* mem_op, ComponentReference* cr, ComponentAny *result ) {
+ if ((result->cai != NULL) && (result->cai->Extract != NULL)) {
+ return (void*) result->cai->Extract( mem_op, cr , result->value );
+ }
+ else {
+ Asn1Error ("ERROR - ANY Extracting routine is NULL\n");
+ return (void*)NULL;
+ }
+}
+
+void
+FreeComponentAny (ComponentAny* any) {
+ if ( any->cai != NULL && any->cai->Free != NULL ) {
+ any->cai->Free( any->value );
+ free ( ((ComponentSyntaxInfo*)any->value)->csi_comp_desc );
+ free ( any->value );
+ }
+ else
+ Asn1Error ("ERROR - ANY Free routine is NULL\n");
+}
+
+void
+InstallAnyByComponentInt (int anyId, ComponentInt intId, unsigned int size,
+ EncodeFcn encode, gser_decoder_func* G_decode,
+ ber_tag_decoder_func* B_decode, ExtractFcn extract,
+ MatchFcn match, FreeFcn free,
+ PrintFcn print)
+{
+ ComponentAnyInfo *a;
+ Hash h;
+
+ a = (ComponentAnyInfo*) malloc(sizeof (ComponentAnyInfo));
+ a->anyId = anyId;
+ a->oid.octs = NULL;
+ a->oid.octetLen = 0;
+ a->intId = intId;
+ a->size = size;
+ a->Encode = encode;
+ a->GSER_Decode = G_decode;
+ a->BER_Decode = B_decode;
+ a->Match = match;
+ a->Extract = extract;
+ a->Free = free;
+ a->Print = print;
+
+ if (anyIntHashTblG == NULL)
+ anyIntHashTblG = InitHash();
+
+ h = MakeHash ((char*)&intId, sizeof (intId));
+
+ if(anyIntHashTblG != NULL)
+ Insert(anyIntHashTblG, a, h);
+}
+
+
+/*
+ * OID and its corresponding decoder can be registered with this func.
+ * If contained types constrained by <select> are used,
+ * their OID and decoder MUST be registered, otherwise it will return no entry.
+ * An open type(ANY type) also need be registered.
+ */
+void
+InstallOidDecoderMapping ( char* ch_oid, EncodeFcn encode, gser_decoder_func* G_decode, ber_tag_decoder_func* B_decode, ExtractFcn extract, MatchFcn match ) {
+ AsnOid oid;
+ int strLen;
+ void* mem_op;
+
+ strLen = strlen( ch_oid );
+ if( strLen <= 0 ) return;
+ mem_op = comp_nibble_memory_allocator ( 128, 16 );
+ oid.octs = EncodeComponentOid ( mem_op, ch_oid, &strLen );
+ oid.octetLen = strLen;
+ if( strLen <= 0 ) return;
+
+
+ InstallAnyByComponentOid ( 0, &oid, 0, encode, G_decode, B_decode,
+ extract, match, NULL, NULL);
+ comp_nibble_memory_free(mem_op);
+}
+
+/*
+ * Look up Oid-decoder mapping table by berval have either
+ * oid or description
+ */
+OidDecoderMapping*
+RetrieveOidDecoderMappingbyBV( struct berval* in ) {
+ if ( IsNumericOid ( in->bv_val, in->bv_len ) )
+ return RetrieveOidDecoderMappingbyOid( in->bv_val, in->bv_len );
+ else
+ return RetrieveOidDecoderMappingbyDesc( in->bv_val, in->bv_len );
+}
+
+/*
+ * Look up Oid-decoder mapping table by dotted OID
+ */
+OidDecoderMapping*
+RetrieveOidDecoderMappingbyOid( char* ch_oid, int oid_len ) {
+ Hash hash;
+ void *anyInfo;
+ AsnOid oid;
+ int strLen;
+ void* mem_op;
+
+ mem_op = comp_nibble_memory_allocator ( 128, 16 );
+ oid.octs = EncodeComponentOid ( mem_op, ch_oid, &oid_len);
+ oid.octetLen = oid_len;
+ if( oid_len <= 0 ) {
+ comp_nibble_memory_free( mem_op );
+ return NULL;
+ }
+
+ /* use encoded oid as hash string */
+ hash = MakeHash ( oid.octs, oid.octetLen);
+ comp_nibble_memory_free( mem_op );
+ if (CheckForAndReturnValue (anyOidHashTblG, hash, &anyInfo))
+ return (OidDecoderMapping*) anyInfo;
+ else
+ return (OidDecoderMapping*) NULL;
+
+}
+
+/*
+ * Look up Oid-decoder mapping table by description
+ */
+OidDecoderMapping*
+RetrieveOidDecoderMappingbyDesc( char* desc, int desc_len ) {
+ Hash hash;
+ void *anyInfo;
+ AsnOid oid;
+ AttributeType* ad_type;
+ struct berval bv;
+ void* mem_op;
+
+ bv.bv_val = desc;
+ bv.bv_len = desc_len;
+ ad_type = at_bvfind( &bv );
+
+ oid.octs = ad_type->sat_atype.at_oid;
+ oid.octetLen = strlen ( oid.octs );
+
+ if ( !ad_type )
+ return (OidDecoderMapping*) NULL;
+
+ mem_op = comp_nibble_memory_allocator ( 128, 16 );
+
+ oid.octs = EncodeComponentOid ( mem_op, oid.octs , (int*)&oid.octetLen );
+ if( oid.octetLen <= 0 ) {
+ comp_nibble_memory_free( mem_op );
+ return (OidDecoderMapping*) NULL;
+ }
+
+ /* use encoded oid as hash string */
+ hash = MakeHash ( oid.octs, oid.octetLen);
+ comp_nibble_memory_free( mem_op );
+ if (CheckForAndReturnValue (anyOidHashTblG, hash, &anyInfo))
+ return (OidDecoderMapping*) anyInfo;
+ else
+ return (OidDecoderMapping*) NULL;
+
+}
+void
+InstallAnyByComponentOid (int anyId, AsnOid *oid, unsigned int size,
+ EncodeFcn encode, gser_decoder_func* G_decode,
+ ber_tag_decoder_func* B_decode, ExtractFcn extract,
+ MatchFcn match, FreeFcn free, PrintFcn print)
+{
+ ComponentAnyInfo *a;
+ Hash h;
+
+ a = (ComponentAnyInfo*) malloc (sizeof (ComponentAnyInfo));
+ a->anyId = anyId;
+ if ( oid ) {
+ a->oid.octs = malloc( oid->octetLen );
+ memcpy ( a->oid.octs, oid->octs, oid->octetLen );
+ a->oid.octetLen = oid->octetLen;
+ }
+ a->size = size;
+ a->Encode = encode;
+ a->GSER_Decode = G_decode;
+ a->BER_Decode = B_decode;
+ a->Match = match;
+ a->Extract = extract;
+ a->Free = free;
+ a->Print = print;
+
+ h = MakeHash (oid->octs, oid->octetLen);
+
+ if (anyOidHashTblG == NULL)
+ anyOidHashTblG = InitHash();
+
+ if(anyOidHashTblG != NULL)
+ Insert(anyOidHashTblG, a, h);
+}
+
+int
+BDecComponentTop (
+ber_decoder_func *decoder _AND_
+void* mem_op _AND_
+GenBuf *b _AND_
+AsnTag tag _AND_
+AsnLen elmtLen _AND_
+void **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode) {
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return (*decoder)( mem_op, b, tag, elmtLen, (ComponentSyntaxInfo*)v,(int*)bytesDecoded, mode );
+}
+
+/*
+ * ASN.1 specification of a distinguished name
+ * DistinguishedName ::= RDNSequence
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ * RelativeDistinguishedName ::= SET SIZE(1..MAX) OF AttributeTypeandValue
+ * AttributeTypeandValue ::= SEQUENCE {
+ * type AttributeType
+ * value AttributeValue
+ * }
+ * When dnMatch/rdnMatch is used in a component assertion value
+ * the component in DistinguishedName/RelativeDistinguishedName
+ * need to be converted to the LDAP encodings in RFC2253
+ * in order to be matched against the assertion value
+ * If allComponentMatch is used, the assertion value may be
+ * decoded into the Internal Representation(Component Tree)
+ * by the corresponding GSER or BER decoder
+ * Following routine converts a component tree(DistinguishedName) into
+ * LDAP encodings in RFC2253
+ * Example)
+ * IR : ComponentRDNSequence
+ * GSER : { { type cn, value sang },{ type o, value ibm}, {type c, value us} }
+ * LDAP Encodings : cn=sang,o=ibm,c=us
+ */
+
+increment_bv_mem_by_size ( struct berval* in, int size ) {
+ int new_size = in->bv_len + size;
+ in->bv_val = realloc( in->bv_val, new_size );
+ in->bv_len = new_size;
+}
+
+int
+ConvertBER2Desc( char* in, int size, struct berval* out, int* pos ) {
+ int desc_size;
+ char* desc_ptr;
+ unsigned int firstArcNum;
+ unsigned int arcNum;
+ int i, rc, start_pos = *pos;
+ char buf[MAX_OID_LEN];
+ AttributeType *at;
+ struct berval bv_name;
+
+ /*convert BER oid to desc*/
+ for ( i = 0, arcNum = 0; (i < size) && (in[i] & 0x80 ); i++ )
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ i++;
+ firstArcNum = (unsigned short)(arcNum/40);
+ if ( firstArcNum > 2 )
+ firstArcNum = 2;
+
+ arcNum = arcNum - (firstArcNum * 40 );
+
+ rc = intToAscii ( arcNum, buf );
+
+ /*check if the buffer can store the first/second arc and two dots*/
+ if ( out->bv_len < *pos + 2 + 1 + rc )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+
+ if ( firstArcNum == 1)
+ out->bv_val[*pos] = '1';
+ else
+ out->bv_val[*pos] = '2';
+ (*pos)++;
+ out->bv_val[*pos] = '.';
+ (*pos)++;
+
+ memcpy( out->bv_val + *pos, buf, rc );
+ *pos += rc;
+ out->bv_val[*pos] = '.';
+ (*pos)++;
+
+ for ( ; i < size ; ) {
+ for ( arcNum=0; (i < size) && (in[i] & 0x80) ; i++ )
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ i++;
+
+ rc = intToAscii ( arcNum, buf );
+
+ if ( out->bv_len < *pos + rc + 1 )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+
+ memcpy( out->bv_val + *pos, buf, rc );
+ *pos += rc;
+ out->bv_val[*pos] = '.';
+ (*pos)++;
+ }
+ (*pos)--;/*remove the last '.'*/
+
+ /*
+ * lookup OID database to locate desc
+ * then overwrite OID with desc in *out
+ * If failed to look up desc, OID form is used
+ */
+ bv_name.bv_val = out->bv_val + start_pos;
+ bv_name.bv_len = *pos - start_pos;
+ at = at_bvfind( &bv_name );
+ if ( !at )
+ return LDAP_SUCCESS;
+ desc_size = at->sat_cname.bv_len;
+ memcpy( out->bv_val + start_pos, at->sat_cname.bv_val, desc_size );
+ *pos = start_pos + desc_size;
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertComponentAttributeTypeAndValue2RFC2253 ( irAttributeTypeAndValue* in, struct berval* out, int *pos ) {
+ int rc;
+ int value_size = ((ComponentUTF8String*)in->value.value)->value.octetLen;
+ char* value_ptr = ((ComponentUTF8String*)in->value.value)->value.octs;
+
+ rc = ConvertBER2Desc( in->type.value.octs, in->type.value.octetLen, out, pos );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ if ( out->bv_len < *pos + 1/*for '='*/ )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ /*Between type and value, put '='*/
+ out->bv_val[*pos] = '=';
+ (*pos)++;
+
+ /*Assume it is string*/
+ if ( out->bv_len < *pos + value_size )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ memcpy( out->bv_val + *pos, value_ptr, value_size );
+ out->bv_len += value_size;
+ *pos += value_size;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertRelativeDistinguishedName2RFC2253 ( irRelativeDistinguishedName* in, struct berval *out , int* pos) {
+ irAttributeTypeAndValue* attr_typeNvalue;
+ int rc;
+
+
+ FOR_EACH_LIST_ELMT( attr_typeNvalue, &in->comp_list)
+ {
+ rc = ConvertComponentAttributeTypeAndValue2RFC2253( attr_typeNvalue, out, pos );
+ if ( rc != LDAP_SUCCESS ) return LDAP_INVALID_SYNTAX;
+
+ if ( out->bv_len < *pos + 1/*for '+'*/ )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ /*between multivalued RDNs, put comma*/
+ out->bv_val[(*pos)++] = '+';
+ }
+ (*pos)--;/*remove the last '+'*/
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertRDN2RFC2253 ( irRelativeDistinguishedName* in, struct berval *out ) {
+ int rc, pos = 0;
+ out->bv_val = (char*)malloc( INITIAL_DN_SIZE );
+ out->bv_len = INITIAL_DN_SIZE;
+
+ rc = ConvertRelativeDistinguishedName2RFC2253 ( in, out , &pos);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ out->bv_val[pos] = '\0';
+ out->bv_len = pos;
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertRDNSequence2RFC2253( irRDNSequence *in, struct berval* out ) {
+ irRelativeDistinguishedName* rdn_seq;
+ AsnList* seq = &in->comp_list;
+ int pos = 0, rc ;
+
+ out->bv_val = (char*)malloc( INITIAL_DN_SIZE );
+ out->bv_len = INITIAL_DN_SIZE;
+
+ FOR_EACH_LIST_ELMT( rdn_seq, seq )
+ {
+ rc = ConvertRelativeDistinguishedName2RFC2253( rdn_seq, out, &pos );
+ if ( rc != LDAP_SUCCESS ) return LDAP_INVALID_SYNTAX;
+
+ if ( out->bv_len < pos + 1/*for ','*/ )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ /*Between RDN, put comma*/
+ out->bv_val[pos++] = ',';
+ }
+ pos--;/*remove the last '+'*/
+ out->bv_val[pos] = '\0';
+ out->bv_len =pos;
+ return LDAP_SUCCESS;
+}
+
+#endif
diff --git a/contrib/slapd-modules/comp_match/componentlib.h b/contrib/slapd-modules/comp_match/componentlib.h
new file mode 100644
index 0000000..1ebd11e
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/componentlib.h
@@ -0,0 +1,593 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+
+#ifndef _H_COMPONENT_MODULE
+#define _H_COMPONENT_MODULE
+
+#include "portable.h"
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include <slap.h>
+#include <component.h>
+
+#include <asn-incl.h>
+#include "asn.h"
+#include <asn-gser.h>
+#include <string.h>
+
+#define MAX_IDENTIFIER_LEN 32
+#define COMPONENTNOT_NULL(ptr) ((ptr) != NULL)
+
+typedef struct slap_component_type {
+ /*
+ * Don't change the order of following fields
+ * They are identical the first 9 fields of
+ * AttributeType
+ */
+ LDAPAttributeType ct_atype;
+ struct berval ct_cname;
+ struct slap_attribute_type *ct_sup;
+ struct slap_attribute_type **ct_subtypes;
+ MatchingRule *ct_equality;
+ MatchingRule *ct_approx;
+ MatchingRule *ct_ordering;
+ MatchingRule *ct_substr;
+ Syntax *ct_syntax;
+} ComponentType;
+
+
+/*
+ * BIT STRING
+ */
+typedef struct ComponentBits {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnBits value;
+} ComponentBits;
+
+#define GASNBITS_PRESENT(abits) ((abits)->value.bits != NULL)
+#define COMPONENTBITS_PRESENT(abits) ((abits)->value.bits != NULL)
+int GEncComponentBits (GenBuf *b, ComponentBits* bits);
+int GDecComponentBits (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentBits (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentBits (char* oid, ComponentSyntaxInfo *bits1 , ComponentSyntaxInfo* bits2);
+#define ExtractingComponentBits( mem_op, cr,data ) NULL
+
+/*
+ * BMP String
+ */
+typedef struct ComponentBMPString {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ BMPString value;
+} ComponentBMPString;
+
+int GEncComponentBMPString (GenBuf *b, ComponentBMPString* bmp);
+int GDecComponentBMPString (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentBMPString (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentBMPString MatchingComponentOcts
+#define ExtractingComponentBMPString( mem_op, cr, data ) NULL
+#define FreeComponentBMPString FreeComponentOcts
+
+/*
+ * BOOLEAN
+ */
+typedef struct ComponentBool {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnBool value;
+} ComponentBool;
+
+int GEncComponentBool (GenBuf *b, ComponentBool * bool );
+int GDecComponentBool ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentBool ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentBool (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentBool( mem_op, cr, data ) NULL
+#define FreeComponentBool(v) NULL
+
+/*
+ * ENUMERATED
+ */
+typedef struct ComponentEnum {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnEnum value;
+ struct berval value_identifier;/*Why this value is defined here?*/
+} ComponentEnum;
+
+int GEncComponentEnum (GenBuf *b, ComponentEnum* comp_enum);
+int GDecComponentEnum ( void* mem_op, GenBuf *a, void *result, AsnLen *bytesDecoded,int mode);
+int BDecComponentEnum ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentEnum (char *oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo * b);
+#define ExtractingComponentEnum( mem_op, cr, data ) NULL
+#define FreeComponentEnum FreeComponentInt
+
+/*
+ * IA5 String
+ */
+typedef struct ComponentIA5String {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ IA5String value;
+} ComponentIA5String;
+
+#define GEncComponentIA5String GEncComponentUTF8String
+#define GDecComponentIA5String GDecComponentUTF8String
+int
+BDecComponentIA5StringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int BDecComponentIA5String ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentIA5String MatchingComponentOcts
+#define ExtractingComponentIA5String(mem_op, cr,data) NULL
+#define FreeComponentIA5String FreeComponentOcts
+
+
+/*
+ * INTEGER
+ */
+typedef struct ComponentInt {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ int value;
+} ComponentInt;
+
+#define GNOT_NULL(ptr) ((ptr) != NULL)
+int GEncComponentInt (GenBuf *b, ComponentInt *comp_int);
+int GDecComponentInt ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode );
+int BDecComponentInt ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentInt (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentInt(mem_op, cr,data) NULL
+#define FreeComponentInt(v) NULL
+
+/*
+ * LIST Data Structure for C_LIST
+ */
+typedef struct ComponentList {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnList comp_list;
+} ComponentList;
+
+/*
+ * NULL
+ */
+typedef struct ComponentNull {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnNull value;
+} ComponentNull;
+
+int GEncComponentNull (GenBuf *b, ComponentNull* comp_null);
+int GDecComponentNull ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentNull ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentNullTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int MatchingComponentNull (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentNull(mem_op, cr, data) NULL
+#define FreeComponentNull NULL
+
+/*
+ * Numeric String
+ */
+typedef struct ComponentNumericString {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ NumericString value;
+} ComponentNumericString;
+
+#define GEncComponentNumericString GEncComponentUTF8String
+#define GDecComponentNumericString GDecComponentUTF8String
+int BDecComponentNumericString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentNumericString MatchingComponentOcts
+#define ExtractingComponentNumericString(mem_op, cr,data) NULL
+#define FreeComponentNumericString FreeComponentOcts
+
+/*
+ * OCTETS STRING
+ */
+typedef struct ComponentOcts {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnOcts value;
+} ComponentOcts;
+
+#define GASNOCTS_PRESENT(aocts) ((aocts)->value.octs != NULL)
+int GEncComponentOcts (GenBuf *b, ComponentOcts *octs);
+int GDecComponentOcts (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentOctsTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int BDecComponentOcts (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentOcts (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentOcts(mem_op,cr,data) NULL
+void FreeComponentOcts( ComponentOcts* octs );
+
+/*
+ * OID (Object Identifier)
+ */
+typedef struct ComponentOid {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnOid value;
+} ComponentOid;
+
+#define GASNOID_PRESENT(aoid) ASNOCTS_PRESENT(aoid)
+int GEncComponentOid (GenBuf *b, ComponentOid *oid);
+int GDecComponentOid (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentOid (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentOid (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentOid(mem_op, cr, data) NULL
+#define FreeComponentOid FreeComponentOcts
+
+/*
+ * Printable String
+ */
+typedef struct ComponentPrintableString{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ PrintableString value;
+} ComponentPrintableString;
+#define GEncComponentPrintableString GEncComponentUTF8String
+#define GDecComponentPrintableString GDecComponentUTF8String
+int BDecComponentPrintableString (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentPrintableStringTag (void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+#define MatchingComponentPrintableString MatchingComponentOcts
+#define ExtractingComponentPrintableString(mem_op, cr, data) NULL
+#define FreeComponentPrintableString FreeComponentOcts
+
+/*
+ * REAL
+ */
+typedef struct ComponentReal{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnReal value;
+} ComponentReal;
+
+int GEncComponentReal (GenBuf *b, ComponentReal* comp_real);
+int GDecComponentReal (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentReal (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentReal (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentReal( mem_op, cr, data ) NULL
+#define FreeComponentReal(v) NULL
+
+/*
+ * Relative OID
+ */
+
+typedef struct ComponentRelativeOid {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnRelativeOid value;
+} ComponentRelativeOid;
+
+int GEncComponentRelativeOid (GenBuf *b, ComponentRelativeOid *r_oid);
+int GDecComponentRelativeOid ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentRelativeOid ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentRelativeOid (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentRelativeOid( mem_op, cr, data ) NULL
+#define FreeComponentRelativeOid FreeComponentOid
+
+/*
+ * Teletex String
+ */
+typedef struct ComponentTeletexString {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ TeletexString value;
+} ComponentTeletexString;
+
+int GEncComponentTeletexString (GenBuf *b, ComponentTeletexString * tel_str);
+int GDecComponentTeletexString ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode );
+int BDecComponentTeletexStringTag (void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int BDecComponentTeletexString( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode );
+#define MatchingComponentTeletexString MatchingComponentOcts
+#define ExtractingComponentTeletexString(mem_op,cr,data)
+#define FreeComponentTeletexString FreeComponentOcts
+
+
+/*
+ * Universal String
+ */
+typedef struct ComponentUniversalString{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ UniversalString value;
+} ComponentUniversalString;
+
+int GEncComponentUniversalString (GenBuf *b, ComponentUniversalString* uni_str);
+int GDecComponentUniversalString ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentUniversalString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentUniversalString MatchingComponentOcts
+#define ExtractingComponentUniversalString(mem_op,cr,data)
+#define FreeComponentUniversalString FreeComponentOcts
+
+/*
+ * UTF8 String
+ */
+typedef struct ComponentUTF8String{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ UTF8String value;
+} ComponentUTF8String;
+
+int GEncComponentUTF8String (GenBuf *b, ComponentUTF8String * utf_str);
+int GDecComponentUTF8String (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentUTF8String (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentUTF8String MatchingComponentOcts
+#define ExtractingComponentUTF8String(mem_op,cr,data)
+#define FreeComponentUTF8String FreeComponentOcts
+
+/*
+ * Visible String
+ */
+typedef struct ComponentVisibleString{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ VisibleString value;
+} ComponentVisibleString;
+
+#define GEncComponentVisibleString GEncComponentUTF8String
+#define GDecComponentVisibleString GDecComponentUTF8String
+int BDecComponentVisibleString (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentVisibleString MatchingComponentOcts
+#define ExtractingComponentVisibleString(mem_op,cr,data)
+#define FreeComponentVisibleString FreeComponentOcts
+
+/*
+ * ANY and ANY DEFINED BY
+ */
+
+typedef int (*MatchFcn) (char*, void*, void*);
+typedef void* (*ExtractFcn) (void*, ComponentReference*, void * );
+
+typedef struct ComponentAnyInfo
+{
+ int anyId;
+ AsnOid oid;
+ ComponentInt intId;
+ unsigned int size;
+ EncodeFcn Encode;
+ gser_decoder_func* GSER_Decode;
+ ber_tag_decoder_func* BER_Decode;
+ ExtractFcn Extract;
+ MatchFcn Match;
+ FreeFcn Free;
+ PrintFcn Print;
+} ComponentAnyInfo;
+
+typedef struct ComponentAnyInfo OidDecoderMapping ;
+
+typedef struct ComponentAny{
+ void* syntax;
+ ComponentDesc *comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAnyInfo *cai;
+ void *value;
+} ComponentAny;
+
+typedef ComponentAny ComponentAnyDefinedBy;
+
+#define BDecComponentAnyDefinedBy BDecComponentAny
+#define GDecComponentAnyDefinedBy GDecComponentAny
+#define MatchingComponentAnyDefinedBy MatchingComponentAny
+#define FreeComponentAnyDefinedBy FreeComponentAny
+
+int GEncComponentAny (GenBuf *b, ComponentAny *comp_any);
+int BDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode);
+int GDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentAny (char* oid, ComponentAny *a, ComponentAny *b);
+void FreeComponentAny ( ComponentAny*);
+
+void InstallAnyByComponentInt (int anyId, ComponentInt intId, unsigned int size, EncodeFcn encode, gser_decoder_func* G_decode, ber_tag_decoder_func B_decode, ExtractFcn extract, MatchFcn match, FreeFcn free, PrintFcn print);
+
+void InstallAnyByComponentOid (int anyId, AsnOid *oid, unsigned int size, EncodeFcn encode, gser_decoder_func* G_decode, ber_tag_decoder_func* B_decode, ExtractFcn extract, MatchFcn match, FreeFcn free, PrintFcn print);
+
+int CheckSelectTypeCorrect ( void* mem_op, ComponentAnyInfo *v, struct berval* select );
+
+OidDecoderMapping* RetrieveOidDecoderMappingbyBV( struct berval* in );
+OidDecoderMapping* RetrieveOidDecoderMappingbyOid( char* ch_oid, int oid_len );
+OidDecoderMapping* RetrieveOidDecoderMappingbyDesc( char* desc, int desc_len );
+/*
+ * UTCTime
+ */
+typedef ComponentVisibleString ComponentUTCTime;
+#define GEncComponentUTCTime GEncComponentUTF8String
+#define GDecComponentUTCTime GDecComponentVisibleString
+#define BDecComponentUTCTime BDecComponentOcts
+#define MatchingComponentUTCTime MatchingComponentOcts
+#define ExtractingComponentUTCTime(mem_op,cr,data) NULL
+#define FreeComponentUTCTime FreeComponentOcts
+
+/*
+ * GeneralizedTime
+ */
+typedef ComponentVisibleString ComponentGeneralizedTime;
+int GEncComponentGeneralizedTime (GenBuf *b, ComponentGeneralizedTime *gen_time);
+#define GDecComponentGeneralizedTime GDecComponentVisibleString
+#define BDecComponentGeneralizedTime BDecComponentOcts
+#define MatchingComponentGeneralizedTime MatchingComponentOcts
+#define ExtractingComponentGeneralizedTime(mem_op,cr,data) NULL
+#define FreeComponentGeneralizedTime FreeComponentOcts
+
+typedef int converter_func LDAP_P ((
+ struct berval* in ));
+
+typedef struct asntype_to_syntax {
+ AsnTypeId ats_typeId;
+ /* Syntax Descriptor */
+ char *ats_syn_name;
+ /* Syntax OID */
+ char *ats_syn_oid;
+ Syntax *ats_syn;
+} AsnTypetoSyntax;
+
+typedef struct asntype_to_comp_matchingrule {
+ AsnTypeId atc_typeId;
+ char* atc_equality;
+ char* atc_approx;
+ char* atc_ordering;
+ char* atc_substr;
+} AsnTypetoCompMatchingRule;
+
+typedef struct asntype_to_comp_desc {
+ AsnTypeId atcd_typeId;
+ ComponentDesc atcd_cd;
+} AsnTypetoCompDesc;
+
+typedef struct asntype_to_comp_type {
+ AsnTypeId ac_asn_id;
+ ComponentType ac_comp_type;
+} AsnTypetoCompType;
+
+/* refined matching purpose */
+typedef struct asntype_to_matchingrule {
+ AsnTypeId atmr_typeId;
+ char* atmr_mr_name;
+ /*Implicitly corresponding LDAP syntax OID*/
+ char* atmr_syn_oid;
+ MatchingRule *atmr_mr;
+} AsnTypetoMatchingRule;
+
+typedef struct asntype_to_matchingrule_table {
+ char* atmr_oid;
+ struct asntype_to_matchingrule atmr_table[ASNTYPE_END];
+ struct asntype_to_matchingrule_table* atmr_table_next;
+} AsnTypetoMatchingRuleTable;
+
+#define MAX_OID_LEN 256
+#define MAX_OD_ENTRY 8
+
+/*
+ * Object Identifier and corresponding Syntax Decoder Table
+ */
+typedef struct OID_Decoder_entry {
+ char oe_oid[MAX_OID_LEN];
+ gser_decoder_func* oe_gser_decoder;
+ ber_decoder_func* oe_ber_decoder;
+ converter_func* oe_converter;
+ struct OID_Decoder_entry* oe_next;
+ struct OID_Decoder_entry* oe_prev;
+} OD_entry;
+
+void
+m_convert_asn_to_ldap ( ComponentSyntaxInfo* csi, struct berval* bv);
+int
+m_convert_assert_to_comp ( gser_decoder_func* decoder, struct berval* bv,
+ ComponentSyntaxInfo** csi, int len, int mode );
+void*
+m_convert_attr_to_comp ( Attribute* a, struct berval* bv );
+
+/*
+ * Decoder Modes
+ * Different operation is required to handle Decoding(2), Extracted Component
+ * decoding(0), ANY DEFINED TYPe(2)
+ * b0 : Component Alloc(yes)
+ * Constructed type : Component Alloc (Yes)
+ * Primitive type : Component Alloc (Yes)
+ * set to mode 2 in inner decoders
+ * b1 : Component Alloc (No)
+ * Constructed type : Component Alloc (No)
+ * Primitive type : Component Alloc (No)
+ * set to mode 2 in inner decoders
+ * b2 : Default Mode
+ * Constructed type : Component Alloc (Yes)
+ * Primitive type : Component Alloc (No)
+ * in addition to above modes, the 4th bit has special meaning,
+ * b4 : if the 4th bit is clear, DecxxxContent is called
+ * b4 : if the 4th bit is set, Decxxx is called, then it is cleared.
+ */
+#define DEC_ALLOC_MODE_0 0x01
+#define DEC_ALLOC_MODE_1 0x02
+#define DEC_ALLOC_MODE_2 0x04
+#define CALL_TAG_DECODER 0x08
+#define CALL_CONTENT_DECODER ~0x08
+
+#define OID_ALL_COMP_MATCH "1.2.36.79672281.1.13.6"
+#define OID_COMP_FILTER_MATCH "1.2.36.79672281.1.13.2"
+#define MAX_LDAP_STR_LEN 128
+
+MatchingRule*
+retrieve_matching_rule( char* mr_oid, AsnTypeId type );
+
+#define INITIAL_DN_SIZE 128
+#define INITIAL_ATTR_SIZE 256
+#define INCREMENT_SIZE 32
+/*
+ * The following are for conversion from ASN.1 RDN and DN to
+ * LDAP encodings
+ */
+#define MAX_ALIASING_ENTRY 128
+int increment_bv_mem ( struct berval* in );
+int intToAscii ( int value, char* buf );
+typedef ComponentList irRDNSequence;
+typedef ComponentList irRelativeDistinguishedName;
+typedef ComponentOid irAttributeType;
+typedef struct comp_irAttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ irAttributeType type; /* AttributeType */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} irAttributeTypeAndValue;
+#define RDN_MATCH_OID "1.2.36.79672281.1.13.3"
+#define DN_MATCH_OID "2.5.13.1"
+
+extern AsnTypetoSyntax asn_to_syntax_mapping_tbl[];
+extern AsnTypetoCompMatchingRule asntype_to_compMR_mapping_tbl[];
+extern AsnTypetoCompType asntype_to_compType_mapping_tbl[];
+extern AsnTypetoCompDesc asntype_to_compdesc_mapping_tbl[];
+
+int ConvertRDN2RFC2253 ( irRelativeDistinguishedName* in, struct berval *out );
+int ConvertRDNSequence2RFC2253( irRDNSequence *in, struct berval* out );
+
+void* comp_nibble_memory_allocator ( int init_mem, int inc_mem );
+
+ComponentDesc* get_ComponentDesc( int id );
+#endif
diff --git a/contrib/slapd-modules/comp_match/crl.c b/contrib/slapd-modules/comp_match/crl.c
new file mode 100644
index 0000000..abd82cd
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/crl.c
@@ -0,0 +1,1294 @@
+/*
+ * crl.c
+ * "CertificateRevokationList" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Fri Jan 21 11:25:24 2005
+ * The generated files are supposed to be compiled as a module for OpenLDAP Software
+ */
+
+#include "crl.h"
+
+BDecComponentCertificateListTop( void* mem_op, GenBuf* b, void *v, AsnLen* bytesDecoded,int mode) {
+ AsnTag tag;
+ AsnLen elmtLen;
+
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return BDecComponentCertificateList( mem_op, b, tag, elmtLen, ( ComponentCertificateList**)v, (AsnLen*)bytesDecoded, mode );
+}
+
+
+void init_module_CertificateRevokationList() {
+ InstallOidDecoderMapping( "2.5.4.39", NULL,
+ GDecComponentCertificateList,
+ BDecComponentCertificateListTop,
+ ExtractingComponentCertificateList,
+ MatchingComponentCertificateList);
+}
+
+int
+MatchingComponentTBSCertListSeqOfSeq ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentCertificateSerialNumber ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertListSeqOfSeq*)csi_attr)->userCertificate, (ComponentSyntaxInfo*)&((ComponentTBSCertListSeqOfSeq*)csi_assert)->userCertificate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_attr)->revocationDate, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_assert)->revocationDate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertListSeqOfSeq*)csi_attr)->crlEntryExtensions ) ) {
+ rc = MatchingComponentExtensions ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_attr)->crlEntryExtensions, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_assert)->crlEntryExtensions );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertListSeqOfSeq */
+
+void*
+ExtractingComponentTBSCertListSeqOfSeq ( void* mem_op, ComponentReference* cr, ComponentTBSCertListSeqOfSeq *comp )
+{
+
+ if ( ( comp->userCertificate.identifier.bv_val && strncmp(comp->userCertificate.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->userCertificate.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->userCertificate;
+ else
+ return NULL;
+ }
+ if ( ( comp->revocationDate->identifier.bv_val && strncmp(comp->revocationDate->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->revocationDate->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->revocationDate;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->revocationDate );
+ }
+ }
+ if ( ( comp->crlEntryExtensions->identifier.bv_val && strncmp(comp->crlEntryExtensions->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->crlEntryExtensions->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->crlEntryExtensions;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtensions ( mem_op, cr, comp->crlEntryExtensions );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentTBSCertListSeqOfSeq */
+
+int
+BDecComponentTBSCertListSeqOfSeq PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertListSeqOfSeq **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertListSeqOfSeq *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentCertificateSerialNumber (mem_op, b, tagId1, elmtLen1, (&k->userCertificate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->userCertificate)->identifier.bv_val = (&k->userCertificate)->id_buf;
+ (&k->userCertificate)->identifier.bv_len = strlen("userCertificate");
+ strcpy( (&k->userCertificate)->identifier.bv_val, "userCertificate");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->revocationDate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->revocationDate)->identifier.bv_val = (k->revocationDate)->id_buf;
+ (k->revocationDate)->identifier.bv_len = strlen("revocationDate");
+ strcpy( (k->revocationDate)->identifier.bv_val, "revocationDate");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentExtensions (mem_op, b, tagId1, elmtLen1, (&k->crlEntryExtensions), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->crlEntryExtensions)->identifier.bv_val = (k->crlEntryExtensions)->id_buf;
+ (k->crlEntryExtensions)->identifier.bv_len = strlen("crlEntryExtensions");
+ strcpy( (k->crlEntryExtensions)->identifier.bv_val, "crlEntryExtensions");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOfSeq*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOfSeq) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOfSeq;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOfSeq;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertListSeqOfSeq*/
+
+int
+GDecComponentTBSCertListSeqOfSeq PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertListSeqOfSeq **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertListSeqOfSeq *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "userCertificate", strlen("userCertificate") ) == 0 ) {
+ rc = GDecComponentCertificateSerialNumber (mem_op, b, (&k->userCertificate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->userCertificate)->identifier.bv_val = peek_head;
+ (&k->userCertificate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "revocationDate", strlen("revocationDate") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->revocationDate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->revocationDate)->identifier.bv_val = peek_head;
+ ( k->revocationDate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "crlEntryExtensions", strlen("crlEntryExtensions") ) == 0 ) {
+ rc = GDecComponentExtensions (mem_op, b, (&k->crlEntryExtensions), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->crlEntryExtensions)->identifier.bv_val = peek_head;
+ ( k->crlEntryExtensions)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOfSeq*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOfSeq) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOfSeq;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOfSeq;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertListSeqOfSeq*/
+
+
+int
+MatchingComponentTBSCertListSeqOf ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentTBSCertListSeqOf*)csi_attr)->comp_list;
+ v2 = &((ComponentTBSCertListSeqOf*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentTBSCertListSeqOfSeq(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertListSeqOfContent */
+
+void*
+ExtractingComponentTBSCertListSeqOf ( void* mem_op, ComponentReference* cr, ComponentTBSCertListSeqOf *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentTBSCertListSeqOfSeq *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertListSeqOfSeq ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertListSeqOfSeq ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentTBSCertListSeqOf */
+
+int
+BDecComponentTBSCertListSeqOf PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertListSeqOf **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertListSeqOf *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentTBSCertListSeqOfSeq));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentTBSCertListSeqOfSeq **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentTBSCertListSeqOfSeq**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentTBSCertListSeqOfSeq (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOf*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOf) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOf;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOf;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertListSeqOfContent */
+
+int
+GDecComponentTBSCertListSeqOf PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertListSeqOf **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertListSeqOf *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentTBSCertListSeqOfSeq ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentTBSCertListSeqOfSeq **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentTBSCertListSeqOfSeq**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentTBSCertListSeqOfSeq (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOf*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOf) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOf;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOf;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertListSeqOfContent */
+
+int
+MatchingComponentTBSCertList ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertList*)csi_attr)->version ) ) {
+ rc = MatchingComponentVersion ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->version, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->version );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->signature, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->issuer, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->issuer );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->thisUpdate, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->thisUpdate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertList*)csi_attr)->nextUpdate ) ) {
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->nextUpdate, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->nextUpdate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ rc = MatchingComponentTBSCertListSeqOf ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->revokedCertificates, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->revokedCertificates );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertList*)csi_attr)->crlExtensions ) ) {
+ rc = MatchingComponentExtensions ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->crlExtensions, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->crlExtensions );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertList */
+
+void*
+ExtractingComponentTBSCertList ( void* mem_op, ComponentReference* cr, ComponentTBSCertList *comp )
+{
+
+ if ( ( comp->version->identifier.bv_val && strncmp(comp->version->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->version->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->version;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentVersion ( mem_op, cr, comp->version );
+ }
+ }
+ if ( ( comp->signature->identifier.bv_val && strncmp(comp->signature->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signature;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signature );
+ }
+ }
+ if ( ( comp->issuer->identifier.bv_val && strncmp(comp->issuer->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->issuer->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->issuer;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, comp->issuer );
+ }
+ }
+ if ( ( comp->thisUpdate->identifier.bv_val && strncmp(comp->thisUpdate->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->thisUpdate->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->thisUpdate;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->thisUpdate );
+ }
+ }
+ if ( ( comp->nextUpdate->identifier.bv_val && strncmp(comp->nextUpdate->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->nextUpdate->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->nextUpdate;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->nextUpdate );
+ }
+ }
+ if ( ( comp->revokedCertificates->identifier.bv_val && strncmp(comp->revokedCertificates->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->revokedCertificates->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->revokedCertificates;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertListSeqOf ( mem_op, cr, comp->revokedCertificates );
+ }
+ }
+ if ( ( comp->crlExtensions->identifier.bv_val && strncmp(comp->crlExtensions->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->crlExtensions->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->crlExtensions;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtensions ( mem_op, cr, comp->crlExtensions );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentTBSCertList */
+
+int
+BDecComponentTBSCertList PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertList *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentVersion (mem_op, b, tagId1, elmtLen1, (&k->version), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->version)->identifier.bv_val = (k->version)->id_buf;
+ (k->version)->identifier.bv_len = strlen("version");
+ strcpy( (k->version)->identifier.bv_val, "version");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signature)->identifier.bv_val = (k->signature)->id_buf;
+ (k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (k->signature)->identifier.bv_val, "signature");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->issuer), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->issuer)->identifier.bv_val = (k->issuer)->id_buf;
+ (k->issuer)->identifier.bv_len = strlen("issuer");
+ strcpy( (k->issuer)->identifier.bv_val, "issuer");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->thisUpdate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->thisUpdate)->identifier.bv_val = (k->thisUpdate)->id_buf;
+ (k->thisUpdate)->identifier.bv_len = strlen("thisUpdate");
+ strcpy( (k->thisUpdate)->identifier.bv_val, "thisUpdate");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->nextUpdate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->nextUpdate)->identifier.bv_val = (k->nextUpdate)->id_buf;
+ (k->nextUpdate)->identifier.bv_len = strlen("nextUpdate");
+ strcpy( (k->nextUpdate)->identifier.bv_val, "nextUpdate");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTBSCertListSeqOf (mem_op, b, tagId1, elmtLen1, (&k->revokedCertificates), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->revokedCertificates)->identifier.bv_val = (k->revokedCertificates)->id_buf;
+ (k->revokedCertificates)->identifier.bv_len = strlen("revokedCertificates");
+ strcpy( (k->revokedCertificates)->identifier.bv_val, "revokedCertificates");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tagId2 = BDecTag (b, &totalElmtsLen1 );
+
+ if (tagId2 != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentExtensions (mem_op, b, tagId2, elmtLen2, (&k->crlExtensions), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->crlExtensions)->identifier.bv_val = (k->crlExtensions)->id_buf;
+ (k->crlExtensions)->identifier.bv_len = strlen("crlExtensions");
+ strcpy( (k->crlExtensions)->identifier.bv_val, "crlExtensions");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertList*) CompAlloc( mem_op, sizeof(ComponentTBSCertList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertList;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertList*/
+
+int
+GDecComponentTBSCertList PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertList *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "version", strlen("version") ) == 0 ) {
+ rc = GDecComponentVersion (mem_op, b, (&k->version), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->version)->identifier.bv_val = peek_head;
+ ( k->version)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signature)->identifier.bv_val = peek_head;
+ ( k->signature)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "issuer", strlen("issuer") ) == 0 ) {
+ rc = GDecComponentName (mem_op, b, (&k->issuer), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->issuer)->identifier.bv_val = peek_head;
+ ( k->issuer)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "thisUpdate", strlen("thisUpdate") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->thisUpdate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->thisUpdate)->identifier.bv_val = peek_head;
+ ( k->thisUpdate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "nextUpdate", strlen("nextUpdate") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->nextUpdate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->nextUpdate)->identifier.bv_val = peek_head;
+ ( k->nextUpdate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "revokedCertificates", strlen("revokedCertificates") ) == 0 ) {
+ rc = GDecComponentTBSCertListSeqOf (mem_op, b, (&k->revokedCertificates), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->revokedCertificates)->identifier.bv_val = peek_head;
+ ( k->revokedCertificates)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "crlExtensions", strlen("crlExtensions") ) == 0 ) {
+ rc = GDecComponentExtensions (mem_op, b, (&k->crlExtensions), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->crlExtensions)->identifier.bv_val = peek_head;
+ ( k->crlExtensions)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertList*) CompAlloc( mem_op, sizeof(ComponentTBSCertList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertList;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertList*/
+
+
+int
+MatchingComponentCertificateList ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentTBSCertList ( oid, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_attr)->tbsCertList, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_assert)->tbsCertList );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_attr)->signatureAlgorithm, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_assert)->signatureAlgorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBits ( oid, (ComponentSyntaxInfo*)&((ComponentCertificateList*)csi_attr)->signature, (ComponentSyntaxInfo*)&((ComponentCertificateList*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentCertificateList */
+
+void*
+ExtractingComponentCertificateList ( void* mem_op, ComponentReference* cr, ComponentCertificateList *comp )
+{
+
+ if ( ( comp->tbsCertList->identifier.bv_val && strncmp(comp->tbsCertList->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->tbsCertList->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->tbsCertList;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertList ( mem_op, cr, comp->tbsCertList );
+ }
+ }
+ if ( ( comp->signatureAlgorithm->identifier.bv_val && strncmp(comp->signatureAlgorithm->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signatureAlgorithm->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signatureAlgorithm;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signatureAlgorithm );
+ }
+ }
+ if ( ( comp->signature.identifier.bv_val && strncmp(comp->signature.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->signature;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->signature;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentCertificateList */
+
+int
+BDecComponentCertificateList PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentCertificateList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentCertificateList *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTBSCertList (mem_op, b, tagId1, elmtLen1, (&k->tbsCertList), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->tbsCertList)->identifier.bv_val = (k->tbsCertList)->id_buf;
+ (k->tbsCertList)->identifier.bv_len = strlen("tbsCertList");
+ strcpy( (k->tbsCertList)->identifier.bv_val, "tbsCertList");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signatureAlgorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signatureAlgorithm)->identifier.bv_val = (k->signatureAlgorithm)->id_buf;
+ (k->signatureAlgorithm)->identifier.bv_len = strlen("signatureAlgorithm");
+ strcpy( (k->signatureAlgorithm)->identifier.bv_val, "signatureAlgorithm");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBits (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = (&k->signature)->id_buf;
+ (&k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (&k->signature)->identifier.bv_val, "signature");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificateList*) CompAlloc( mem_op, sizeof(ComponentCertificateList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificateList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificateList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificateList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificateList;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecCertificateList*/
+
+int
+GDecComponentCertificateList PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentCertificateList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentCertificateList *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "tbsCertList", strlen("tbsCertList") ) == 0 ) {
+ rc = GDecComponentTBSCertList (mem_op, b, (&k->tbsCertList), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->tbsCertList)->identifier.bv_val = peek_head;
+ ( k->tbsCertList)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signatureAlgorithm", strlen("signatureAlgorithm") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signatureAlgorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signatureAlgorithm)->identifier.bv_val = peek_head;
+ ( k->signatureAlgorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentBits (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = peek_head;
+ (&k->signature)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificateList*) CompAlloc( mem_op, sizeof(ComponentCertificateList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificateList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificateList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificateList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificateList;
+ return LDAP_SUCCESS;
+} /* GDecCertificateList*/
diff --git a/contrib/slapd-modules/comp_match/crl.h b/contrib/slapd-modules/comp_match/crl.h
new file mode 100644
index 0000000..f2b4a24
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/crl.h
@@ -0,0 +1,359 @@
+
+#include "asn-incl.h"
+/*
+ * crl.h
+ * "CertificateRevokationList" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Fri Jan 21 11:25:24 2005
+ * The generated files are strongly encouraged to be
+ * compiled as a module for OpenLDAP Software
+ */
+
+#ifndef _crl_h_
+#define _crl_h_
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+#include "componentlib.h"
+
+#define V1 0
+#define V2 1
+#define V3 2
+
+typedef ComponentInt ComponentVersion; /* INTEGER { V1 (0), V2 (1), V3 (2) } */
+
+#define MatchingComponentVersion MatchingComponentInt
+
+#define ExtractingComponentVersion ExtractingComponentInt
+
+#define BDecComponentVersion BDecComponentInt
+
+#define GDecComponentVersion GDecComponentInt
+
+
+typedef ComponentInt ComponentCertificateSerialNumber; /* INTEGER */
+
+#define MatchingComponentCertificateSerialNumber MatchingComponentInt
+
+#define ExtractingComponentCertificateSerialNumber ExtractingComponentInt
+
+#define BDecComponentCertificateSerialNumber BDecComponentInt
+
+#define GDecComponentCertificateSerialNumber GDecComponentInt
+
+
+typedef ComponentOid ComponentAttributeType; /* OBJECT IDENTIFIER */
+
+#define MatchingComponentAttributeType MatchingComponentOid
+
+#define ExtractingComponentAttributeType ExtractingComponentOid
+
+#define BDecComponentAttributeType BDecComponentOid
+
+#define GDecComponentAttributeType GDecComponentOid
+
+
+typedef struct AlgorithmIdentifier /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid algorithm; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy parameters; /* ANY DEFINED BY algorithm OPTIONAL */
+} ComponentAlgorithmIdentifier;
+
+int MatchingComponentAlgorithmIdentifier PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAlgorithmIdentifier PROTO (( void* mem_op, ComponentReference *cr, ComponentAlgorithmIdentifier *comp ));
+
+
+int BDecComponentAlgorithmIdentifier PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAlgorithmIdentifier PROTO (( void* mem_op, GenBuf * b, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Time /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum TimeChoiceId
+ {
+ TIME_UTCTIME,
+ TIME_GENERALIZEDTIME
+ } choiceId;
+ union TimeChoiceUnion
+ {
+ ComponentUTCTime* utcTime; /* < unknown type id ?! > */
+ ComponentGeneralizedTime* generalizedTime; /* < unknown type id ?! > */
+ } a;
+} ComponentTime;
+
+int MatchingComponentTime PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTime PROTO (( void* mem_op, ComponentReference *cr, ComponentTime *comp ));
+
+
+int BDecComponentTime PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTime PROTO (( void* mem_op, GenBuf * b, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Extension /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid extnID; /* OBJECT IDENTIFIER */
+ ComponentBool* critical; /* BOOLEAN DEFAULT FALSE */
+ ComponentOcts extnValue; /* OCTET STRING */
+} ComponentExtension;
+
+int MatchingComponentExtension PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtension PROTO (( void* mem_op, ComponentReference *cr, ComponentExtension *comp ));
+
+
+int BDecComponentExtension PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtension PROTO (( void* mem_op, GenBuf * b, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAttributeType type; /* AttributeType */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} ComponentAttributeTypeAndValue;
+
+int MatchingComponentAttributeTypeAndValue PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAttributeTypeAndValue PROTO (( void* mem_op, ComponentReference *cr, ComponentAttributeTypeAndValue *comp ));
+
+
+int BDecComponentAttributeTypeAndValue PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAttributeTypeAndValue PROTO (( void* mem_op, GenBuf * b, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentExtensions; /* SEQUENCE SIZE 1..MAX OF Extension */
+
+int MatchingComponentExtensions PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtensions PROTO (( void* mem_op, ComponentReference *cr, ComponentExtensions *comp ));
+
+
+int BDecComponentExtensions PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtensions PROTO (( void* mem_op, GenBuf * b, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct TBSCertListSeqOfSeq /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentCertificateSerialNumber userCertificate; /* CertificateSerialNumber */
+ ComponentTime* revocationDate; /* Time */
+ ComponentExtensions* crlEntryExtensions; /* Extensions OPTIONAL */
+} ComponentTBSCertListSeqOfSeq;
+
+int MatchingComponentTBSCertListSeqOfSeq PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertListSeqOfSeq PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertListSeqOfSeq *comp ));
+
+
+int BDecComponentTBSCertListSeqOfSeq PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertListSeqOfSeq **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertListSeqOfSeq PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertListSeqOfSeq **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentTBSCertListSeqOf; /* SEQUENCE OF TBSCertListSeqOfSeq */
+
+int MatchingComponentTBSCertListSeqOf PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertListSeqOf PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertListSeqOf *comp ));
+
+
+int BDecComponentTBSCertListSeqOf PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertListSeqOf **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertListSeqOf PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertListSeqOf **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRelativeDistinguishedName; /* SET OF AttributeTypeAndValue */
+
+int MatchingComponentRelativeDistinguishedName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRelativeDistinguishedName PROTO (( void* mem_op, ComponentReference *cr, ComponentRelativeDistinguishedName *comp ));
+
+
+int BDecComponentRelativeDistinguishedName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRelativeDistinguishedName PROTO (( void* mem_op, GenBuf * b, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRDNSequence; /* SEQUENCE OF RelativeDistinguishedName */
+
+int MatchingComponentRDNSequence PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRDNSequence PROTO (( void* mem_op, ComponentReference *cr, ComponentRDNSequence *comp ));
+
+
+int BDecComponentRDNSequence PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRDNSequence PROTO (( void* mem_op, GenBuf * b, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Name /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum NameChoiceId
+ {
+ NAME_RDNSEQUENCE
+ } choiceId;
+ union NameChoiceUnion
+ {
+ ComponentRDNSequence* rdnSequence; /* RDNSequence */
+ } a;
+} ComponentName;
+
+int MatchingComponentName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentName PROTO (( void* mem_op, ComponentReference *cr, ComponentName *comp ));
+
+
+int BDecComponentName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentName PROTO (( void* mem_op, GenBuf * b, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct TBSCertList /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentVersion* version; /* Version OPTIONAL */
+ ComponentAlgorithmIdentifier* signature; /* AlgorithmIdentifier */
+ ComponentName* issuer; /* Name */
+ ComponentTime* thisUpdate; /* Time */
+ ComponentTime* nextUpdate; /* Time OPTIONAL */
+ ComponentTBSCertListSeqOf* revokedCertificates; /* TBSCertListSeqOf */
+ ComponentExtensions* crlExtensions; /* [0] EXPLICIT Extensions OPTIONAL */
+} ComponentTBSCertList;
+
+int MatchingComponentTBSCertList PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertList PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertList *comp ));
+
+
+int BDecComponentTBSCertList PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertList **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertList PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertList **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct CertificateList /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTBSCertList* tbsCertList; /* TBSCertList */
+ ComponentAlgorithmIdentifier* signatureAlgorithm; /* AlgorithmIdentifier */
+ ComponentBits signature; /* BIT STRING */
+} ComponentCertificateList;
+
+int MatchingComponentCertificateList PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentCertificateList PROTO (( void* mem_op, ComponentReference *cr, ComponentCertificateList *comp ));
+
+
+int BDecComponentCertificateList PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentCertificateList **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentCertificateList PROTO (( void* mem_op, GenBuf * b, ComponentCertificateList **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Validity /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTime* notBefore; /* Time */
+ ComponentTime* notAfter; /* Time */
+} ComponentValidity;
+
+int MatchingComponentValidity PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentValidity PROTO (( void* mem_op, ComponentReference *cr, ComponentValidity *comp ));
+
+
+int BDecComponentValidity PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentValidity PROTO (( void* mem_op, GenBuf * b, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+
+/* ========== Object Declarations ========== */
+
+
+/* ========== Object Set Declarations ========== */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* conditional include of crl.h */
diff --git a/contrib/slapd-modules/comp_match/init.c b/contrib/slapd-modules/comp_match/init.c
new file mode 100644
index 0000000..c3ab83c
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/init.c
@@ -0,0 +1,839 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+
+#include "portable.h"
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include "slap.h"
+#include "component.h"
+
+#include "componentlib.h"
+#include "asn.h"
+#include <asn-gser.h>
+
+#include <string.h>
+
+#ifndef SLAPD_COMP_MATCH
+#define SLAPD_COMP_MATCH SLAPD_MOD_DYNAMIC
+#endif
+
+/*
+ * Attribute and MatchingRule aliasing table
+ */
+AttributeAliasing aa_table [ MAX_ALIASING_ENTRY ];
+MatchingRuleAliasing mra_table [ MAX_ALIASING_ENTRY ];
+
+OD_entry* gOD_table = NULL;
+AsnTypetoMatchingRuleTable* gATMR_table = NULL;
+
+int
+load_derived_matching_rule ( char* cfg_path ){
+}
+
+AttributeAliasing*
+comp_is_aliased_attribute( void *in )
+{
+ AttributeAliasing* curr_aa;
+ int i;
+ AttributeDescription *ad = (AttributeDescription*)in;
+
+ for ( i = 0; aa_table[i].aa_aliasing_ad && i < MAX_ALIASING_ENTRY; i++ ) {
+ if ( strncmp(aa_table[i].aa_aliasing_ad->ad_cname.bv_val , ad->ad_cname.bv_val, ad->ad_cname.bv_len) == 0 )
+ return &aa_table[i];
+ }
+ return NULL;
+}
+
+static int
+add_aa_entry( int index, char* aliasing_at_name, char* aliased_at_name, char* mr_name, char* component_filter )
+{
+ char text[1][128];
+ int rc;
+ struct berval type;
+
+ /* get and store aliasing AttributeDescription */
+ type.bv_val = aliasing_at_name;
+ type.bv_len = strlen ( aliasing_at_name );
+ rc = slap_bv2ad ( &type, &aa_table[index].aa_aliasing_ad,(const char**)text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ /* get and store aliased AttributeDescription */
+ type.bv_val = aliased_at_name;
+ type.bv_len = strlen ( aliased_at_name );
+ rc = slap_bv2ad ( &type, &aa_table[index].aa_aliased_ad,(const char**)text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ /* get and store componentFilterMatch */
+ type.bv_val = mr_name;
+ type.bv_len = strlen ( mr_name);
+ aa_table[index].aa_mr = mr_bvfind ( &type );
+
+ /* get and store a component filter */
+ type.bv_val = component_filter;
+ type.bv_len = strlen ( component_filter );
+ rc = get_comp_filter( NULL, &type, &aa_table[index].aa_cf,(const char**)text);
+
+ aa_table[index].aa_cf_str = component_filter;
+
+ return rc;
+}
+
+/*
+ * Initialize attribute aliasing table when this module is loaded
+ * add_aa_entry ( index for the global table,
+ * name of the aliasing attribute,
+ * component filter with filling value parts "xxx"
+ * )
+ * "xxx" will be replaced with effective values later.
+ * See RFC3687 to understand the content of a component filter.
+ */
+char* pre_processed_comp_filter[] = {
+/*1*/"item:{ component \"toBeSigned.issuer.rdnSequence\", rule distinguishedNameMatch, value xxx }",
+/*2*/"item:{ component \"toBeSigned.serialNumber\", rule integerMatch, value xxx }",
+/*3*/"and:{ item:{ component \"toBeSigned.serialNumber\", rule integerMatch, value xxx }, item:{ component \"toBeSigned.issuer.rdnSequence\", rule distinguishedNameMatch, value xxx } }"
+};
+
+static int
+init_attribute_aliasing_table ()
+{
+ int rc;
+ int index = 0 ;
+
+ rc = add_aa_entry ( index, "x509CertificateIssuer", "userCertificate","componentFilterMatch", pre_processed_comp_filter[index] );
+ if ( rc != LDAP_SUCCESS ) return LDAP_PARAM_ERROR;
+ index++;
+
+ rc = add_aa_entry ( index, "x509CertificateSerial","userCertificate", "componentFilterMatch", pre_processed_comp_filter[index] );
+ if ( rc != LDAP_SUCCESS ) return LDAP_PARAM_ERROR;
+ index++;
+
+ rc = add_aa_entry ( index, "x509CertificateSerialAndIssuer", "userCertificate", "componentFilterMatch", pre_processed_comp_filter[index] );
+ if ( rc != LDAP_SUCCESS ) return LDAP_PARAM_ERROR;
+ index++;
+
+ return LDAP_SUCCESS;
+}
+
+void
+init_component_description_table () {
+ AsnTypeId id;
+ struct berval mr;
+ AsnTypetoSyntax* asn_to_syn;
+ Syntax* syn;
+
+ for ( id = BASICTYPE_BOOLEAN; id != ASNTYPE_END ; id++ ) {
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_subtypes = NULL;
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_syntax = NULL;
+
+ /* Equality Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_equality ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_equality;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_equality);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_equality = mr_bvfind( &mr );
+ }
+ /* Approx Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_approx ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_approx;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_approx);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_approx = mr_bvfind( &mr );
+ }
+
+ /* Ordering Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_ordering ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_ordering;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_ordering);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_ordering= mr_bvfind( &mr );
+ }
+
+ /* Substr Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_substr ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_substr;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_substr);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_substr = mr_bvfind( &mr );
+ }
+ /* Syntax */
+
+ asn_to_syn = &asn_to_syntax_mapping_tbl[ id ];
+ if ( asn_to_syn->ats_syn_oid )
+ syn = syn_find ( asn_to_syn->ats_syn_oid );
+ else
+ syn = NULL;
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_syntax = syn;
+
+ /* Initialize Component Descriptions of primitive ASN.1 types */
+ asntype_to_compdesc_mapping_tbl[id].atcd_cd.cd_comp_type = (AttributeType*)&asntype_to_compType_mapping_tbl[id].ac_comp_type;
+ }
+}
+
+MatchingRule*
+retrieve_matching_rule( char* mr_oid, AsnTypeId type ) {
+ char* tmp;
+ struct berval mr_name = BER_BVNULL;
+ AsnTypetoMatchingRuleTable* atmr;
+
+ for ( atmr = gATMR_table ; atmr ; atmr = atmr->atmr_table_next ) {
+ if ( strcmp( atmr->atmr_oid, mr_oid ) == 0 ) {
+ tmp = atmr->atmr_table[type].atmr_mr_name;
+ if ( tmp ) {
+ mr_name.bv_val = tmp;
+ mr_name.bv_len = strlen( tmp );
+ return mr_bvfind ( &mr_name );
+ }
+ }
+ }
+ return (MatchingRule*)NULL;
+}
+
+void*
+comp_convert_attr_to_comp LDAP_P (( Attribute* a, Syntax *syn, struct berval* bv ))
+{
+ char* peek_head;
+ int mode, bytesDecoded, size, rc;
+ void* component;
+ char* oid = a->a_desc->ad_type->sat_atype.at_oid ;
+ GenBuf* b = NULL;
+ ExpBuf* buf = NULL;
+ OidDecoderMapping* odm;
+
+ /* look for the decoder registered for the given attribute */
+ odm = RetrieveOidDecoderMappingbyOid( oid, strlen(oid) );
+
+ if ( !odm || (!odm->BER_Decode && !odm->GSER_Decode) )
+ return (void*)NULL;
+
+ buf = ExpBufAllocBuf();
+ ExpBuftoGenBuf( buf, &b );
+ ExpBufInstallDataInBuf ( buf, bv->bv_val, bv->bv_len );
+ BufResetInReadMode( b );
+
+ mode = DEC_ALLOC_MODE_2;
+ /*
+ * How can we decide which decoder will be called, GSER or BER?
+ * Currently BER decoder is called for a certificate.
+ * The flag of Attribute will say something about it in the future
+ */
+ if ( syn && slap_syntax_is_ber ( syn ) ) {
+#if 0
+ rc =BDecComponentTop(odm->BER_Decode, a->a_comp_data->cd_mem_op, b, 0,0, &component,&bytesDecoded,mode ) ;
+#endif
+ rc = odm->BER_Decode ( a->a_comp_data->cd_mem_op, b, (ComponentSyntaxInfo*)&component, &bytesDecoded, mode );
+ }
+ else {
+ rc = odm->GSER_Decode( a->a_comp_data->cd_mem_op, b, (ComponentSyntaxInfo**)component, &bytesDecoded, mode);
+ }
+
+ ExpBufFreeBuf( buf );
+ GenBufFreeBuf( b );
+ if ( rc == -1 ) {
+#if 0
+ ShutdownNibbleMemLocal ( a->a_comp_data->cd_mem_op );
+ free ( a->a_comp_data );
+ a->a_comp_data = NULL;
+#endif
+ return (void*)NULL;
+ }
+ else {
+ return component;
+ }
+}
+
+#include <nibble-alloc.h>
+void
+comp_free_component ( void* mem_op ) {
+ ShutdownNibbleMemLocal( (NibbleMem*)mem_op );
+ return;
+}
+
+void
+comp_convert_assert_to_comp (
+ void* mem_op,
+ ComponentSyntaxInfo *csi_attr,
+ struct berval* bv,
+ ComponentSyntaxInfo** csi, int* len, int mode )
+{
+ int rc;
+ GenBuf* genBuf;
+ ExpBuf* buf;
+ gser_decoder_func *decoder = csi_attr->csi_comp_desc->cd_gser_decoder;
+
+ buf = ExpBufAllocBuf();
+ ExpBuftoGenBuf( buf, &genBuf );
+ ExpBufInstallDataInBuf ( buf, bv->bv_val, bv->bv_len );
+ BufResetInReadMode( genBuf );
+
+ if ( csi_attr->csi_comp_desc->cd_type_id == BASICTYPE_ANY )
+ decoder = ((ComponentAny*)csi_attr)->cai->GSER_Decode;
+
+ rc = (*decoder)( mem_op, genBuf, csi, len, mode );
+ ExpBufFreeBuf ( buf );
+ GenBufFreeBuf( genBuf );
+}
+
+int intToAscii( int value, char* buf ) {
+ int minus=0,i,temp;
+ int total_num_digits;
+
+ if ( value == 0 ){
+ buf[0] = '0';
+ return 1;
+ }
+
+ if ( value < 0 ){
+ minus = 1;
+ value = value*(-1);
+ buf[0] = '-';
+ }
+
+ /* How many digits */
+ for ( temp = value, total_num_digits=0 ; temp ; total_num_digits++ )
+ temp = temp/10;
+
+ total_num_digits += minus;
+
+ for ( i = minus ; value ; i++ ) {
+ buf[ total_num_digits - i - 1 ]= (char)(value%10 + '0');
+ value = value/10;
+ }
+ return i;
+}
+
+int
+comp_convert_asn_to_ldap ( MatchingRule* mr, ComponentSyntaxInfo* csi, struct berval* bv, int *allocated )
+{
+ int rc;
+ struct berval prettied;
+ Syntax* syn;
+
+ AsnTypetoSyntax* asn_to_syn =
+ &asn_to_syntax_mapping_tbl[csi->csi_comp_desc->cd_type_id];
+ if ( asn_to_syn->ats_syn_oid )
+ csi->csi_syntax = syn_find ( asn_to_syn->ats_syn_oid );
+ else
+ csi->csi_syntax = NULL;
+
+
+ switch ( csi->csi_comp_desc->cd_type_id ) {
+ case BASICTYPE_BOOLEAN :
+ bv->bv_val = (char*)malloc( 5 );
+ *allocated = 1;
+ bv->bv_len = 5;
+ if ( ((ComponentBool*)csi)->value > 0 ) {
+ strcpy ( bv->bv_val , "TRUE" );
+ bv->bv_len = 4;
+ }
+ else {
+ strcpy ( bv->bv_val , "FALSE" );
+ bv->bv_len = 5;
+ }
+ break ;
+ case BASICTYPE_NULL :
+ bv->bv_len = 0;
+ break;
+ case BASICTYPE_INTEGER :
+ bv->bv_val = (char*)malloc( INITIAL_ATTR_SIZE );
+ *allocated = 1;
+ bv->bv_len = INITIAL_ATTR_SIZE;
+ bv->bv_len = intToAscii(((ComponentInt*)csi)->value, bv->bv_val );
+ if ( bv->bv_len <= 0 )
+ return LDAP_INVALID_SYNTAX;
+ break;
+ case BASICTYPE_REAL :
+ return LDAP_INVALID_SYNTAX;
+ case BASICTYPE_ENUMERATED :
+ bv->bv_val = (char*)malloc( INITIAL_ATTR_SIZE );
+ *allocated = 1;
+ bv->bv_len = INITIAL_ATTR_SIZE;
+ bv->bv_len = intToAscii(((ComponentEnum*)csi)->value, bv->bv_val );
+ if ( bv->bv_len <= 0 )
+ return LDAP_INVALID_SYNTAX;
+ break;
+ case BASICTYPE_OID :
+ case BASICTYPE_OCTETSTRING :
+ case BASICTYPE_BITSTRING :
+ case BASICTYPE_NUMERIC_STR :
+ case BASICTYPE_PRINTABLE_STR :
+ case BASICTYPE_UNIVERSAL_STR :
+ case BASICTYPE_IA5_STR :
+ case BASICTYPE_BMP_STR :
+ case BASICTYPE_UTF8_STR :
+ case BASICTYPE_UTCTIME :
+ case BASICTYPE_GENERALIZEDTIME :
+ case BASICTYPE_GRAPHIC_STR :
+ case BASICTYPE_VISIBLE_STR :
+ case BASICTYPE_GENERAL_STR :
+ case BASICTYPE_OBJECTDESCRIPTOR :
+ case BASICTYPE_VIDEOTEX_STR :
+ case BASICTYPE_T61_STR :
+ case BASICTYPE_OCTETCONTAINING :
+ case BASICTYPE_BITCONTAINING :
+ case BASICTYPE_RELATIVE_OID :
+ bv->bv_val = ((ComponentOcts*)csi)->value.octs;
+ bv->bv_len = ((ComponentOcts*)csi)->value.octetLen;
+ break;
+ case BASICTYPE_ANY :
+ csi = ((ComponentAny*)csi)->value;
+ if ( csi->csi_comp_desc->cd_type != ASN_BASIC ||
+ csi->csi_comp_desc->cd_type_id == BASICTYPE_ANY )
+ return LDAP_INVALID_SYNTAX;
+ return comp_convert_asn_to_ldap( mr, csi, bv, allocated );
+ case COMPOSITE_ASN1_TYPE :
+ break;
+ case RDNSequence :
+ /*dnMatch*/
+ if( strncmp( mr->smr_mrule.mr_oid, DN_MATCH_OID, strlen(DN_MATCH_OID) ) != 0 )
+ return LDAP_INVALID_SYNTAX;
+ *allocated = 1;
+ rc = ConvertRDNSequence2RFC2253( (irRDNSequence*)csi, bv );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ break;
+ case RelativeDistinguishedName :
+ /*rdnMatch*/
+ if( strncmp( mr->smr_mrule.mr_oid, RDN_MATCH_OID, strlen(RDN_MATCH_OID) ) != 0 )
+ return LDAP_INVALID_SYNTAX;
+ *allocated = 1;
+ rc = ConvertRDN2RFC2253((irRelativeDistinguishedName*)csi,bv);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ break;
+ case TelephoneNumber :
+ case FacsimileTelephoneNumber__telephoneNumber :
+ break;
+ case DirectoryString :
+ return LDAP_INVALID_SYNTAX;
+ case ASN_COMP_CERTIFICATE :
+ case ASNTYPE_END :
+ break;
+ default :
+ /*Only ASN Basic Type can be converted into LDAP string*/
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( csi->csi_syntax ) {
+ if ( csi->csi_syntax->ssyn_validate ) {
+ rc = csi->csi_syntax->ssyn_validate(csi->csi_syntax, bv);
+ if ( rc != LDAP_SUCCESS )
+ return LDAP_INVALID_SYNTAX;
+ }
+ if ( csi->csi_syntax->ssyn_pretty ) {
+ rc = csi->csi_syntax->ssyn_pretty(csi->csi_syntax, bv, &prettied , NULL );
+ if ( rc != LDAP_SUCCESS )
+ return LDAP_INVALID_SYNTAX;
+#if 0
+ free ( bv->bv_val );/*potential memory leak?*/
+#endif
+ bv->bv_val = prettied.bv_val;
+ bv->bv_len = prettied.bv_len;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * If <all> type component referenced is used
+ * more than one component will be tested
+ */
+#define IS_TERMINAL_COMPREF(cr) (cr->cr_curr->ci_next == NULL)
+int
+comp_test_all_components (
+ void* attr_mem_op,
+ void* assert_mem_op,
+ ComponentSyntaxInfo *csi_attr,
+ ComponentAssertion* ca )
+{
+ int rc;
+ ComponentSyntaxInfo *csi_temp = NULL, *csi_assert = NULL, *comp_elmt = NULL;
+ ComponentReference *cr = ca->ca_comp_ref;
+ struct berval *ca_val = &ca->ca_ma_value;
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_ALL:
+ if ( IS_TERMINAL_COMPREF(cr) ) {
+ FOR_EACH_LIST_ELMT( comp_elmt, &((ComponentList*)csi_attr)->comp_list )
+ {
+ rc = comp_test_one_component( attr_mem_op, assert_mem_op, comp_elmt, ca );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ break;
+ }
+ }
+ } else {
+ ComponentId *start_compid = ca->ca_comp_ref->cr_curr->ci_next;
+ FOR_EACH_LIST_ELMT( comp_elmt, &((ComponentList*)csi_attr)->comp_list )
+ {
+ cr->cr_curr = start_compid;
+ rc = comp_test_components ( attr_mem_op, assert_mem_op, comp_elmt, ca );
+ if ( rc != LDAP_COMPARE_FALSE ) {
+ break;
+ }
+#if 0
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ break;
+ }
+#endif
+ }
+ }
+ break;
+ case LDAP_COMPREF_CONTENT:
+ case LDAP_COMPREF_SELECT:
+ case LDAP_COMPREF_DEFINED:
+ case LDAP_COMPREF_UNDEFINED:
+ case LDAP_COMPREF_IDENTIFIER:
+ case LDAP_COMPREF_FROM_BEGINNING:
+ case LDAP_COMPREF_FROM_END:
+ case LDAP_COMPREF_COUNT:
+ rc = LDAP_OPERATIONS_ERROR;
+ break;
+ default:
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+ return rc;
+}
+
+void
+eat_bv_whsp ( struct berval* in )
+{
+ char* end = in->bv_val + in->bv_len;
+ for ( ; ( *in->bv_val == ' ' ) && ( in->bv_val < end ) ; ) {
+ in->bv_val++;
+ }
+}
+
+/*
+ * Perform matching one referenced component against assertion
+ * If the matching rule in a component filter is allComponentsMatch
+ * or its derivatives the extracted component's ASN.1 specification
+ * is applied to the assertion value as its syntax
+ * Otherwise, the matching rule's syntax is applied to the assertion value
+ * By RFC 3687
+ */
+int
+comp_test_one_component (
+ void* attr_mem_op,
+ void* assert_mem_op,
+ ComponentSyntaxInfo *csi_attr,
+ ComponentAssertion *ca )
+{
+ int len, rc;
+ ComponentSyntaxInfo *csi_assert = NULL;
+ char* oid = NULL;
+ MatchingRule* mr = ca->ca_ma_rule;
+
+ if ( mr->smr_usage & SLAP_MR_COMPONENT ) {
+ /* If allComponentsMatch or its derivatives */
+ if ( !ca->ca_comp_data.cd_tree ) {
+ comp_convert_assert_to_comp( assert_mem_op, csi_attr, &ca->ca_ma_value, &csi_assert, &len, DEC_ALLOC_MODE_0 );
+ ca->ca_comp_data.cd_tree = (void*)csi_assert;
+ } else {
+ csi_assert = ca->ca_comp_data.cd_tree;
+ }
+
+ if ( !csi_assert )
+ return LDAP_PROTOCOL_ERROR;
+
+ if ( strcmp( mr->smr_mrule.mr_oid, OID_ALL_COMP_MATCH ) != 0 )
+ {
+ /* allComponentMatch's derivatives */
+ oid = mr->smr_mrule.mr_oid;
+ }
+ return csi_attr->csi_comp_desc->cd_all_match(
+ oid, csi_attr, csi_assert );
+
+ } else {
+ /* LDAP existing matching rules */
+ struct berval attr_bv = BER_BVNULL;
+ struct berval n_attr_bv = BER_BVNULL;
+ struct berval* assert_bv = &ca->ca_ma_value;
+ int allocated = 0;
+ /*Attribute is converted to compatible LDAP encodings*/
+ if ( comp_convert_asn_to_ldap( mr, csi_attr, &attr_bv, &allocated ) != LDAP_SUCCESS )
+ return LDAP_INAPPROPRIATE_MATCHING;
+ /* extracted component value is not normalized */
+ if ( ca->ca_ma_rule->smr_normalize ) {
+ rc = ca->ca_ma_rule->smr_normalize (
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ NULL, ca->ca_ma_rule,
+ &attr_bv, &n_attr_bv, NULL );
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ if ( allocated && attr_bv.bv_val )
+ free (attr_bv.bv_val);
+ } else {
+ n_attr_bv = attr_bv;
+ }
+#if 0
+ /*Assertion value is validated by MR's syntax*/
+ if ( !ca->ca_comp_data.cd_tree ) {
+ ca->ca_comp_data.cd_tree = assert_bv;
+ }
+ else {
+ assert_bv = ca->ca_comp_data.cd_tree;
+ }
+#endif
+ if ( !n_attr_bv.bv_val )
+ return LDAP_COMPARE_FALSE;
+ rc = csi_value_match( mr, &n_attr_bv, assert_bv );
+ if ( n_attr_bv.bv_val )
+ free ( n_attr_bv.bv_val );
+ return rc;
+ }
+}
+
+int
+comp_test_components( void* attr_nm, void* assert_nm, ComponentSyntaxInfo* csi_attr, ComponentAssertion* ca) {
+ char* peek_head;
+ int mode, bytesDecoded = 0, rc;
+ GenBuf* b;
+ ExpBuf* buf;
+ OidDecoderMapping* odm;
+ struct berval bv;
+ char oid[MAX_OID_LEN];
+ void* contained_comp, *anytype_comp;
+ ComponentReference* cr = ca->ca_comp_ref;
+
+ if ( !cr )
+ return comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ /* Extracting the component referenced by ca->ca_comp_ref */
+ csi_attr = (ComponentSyntaxInfo*)csi_attr->csi_comp_desc->cd_extract_i( attr_nm, cr, csi_attr );
+ if ( !csi_attr ) return LDAP_INVALID_SYNTAX;
+ /* perform matching, considering the type of a Component Reference(CR)*/
+ switch( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_IDENTIFIER:
+ case LDAP_COMPREF_FROM_BEGINNING:
+ case LDAP_COMPREF_FROM_END:
+ case LDAP_COMPREF_COUNT:
+ /*
+ * Exactly one component is referenced
+ * Fast Path for matching for this case
+ */
+ rc = comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ break;
+ case LDAP_COMPREF_ALL:
+ /*
+ * If <all> type CR is used
+ * more than one component will be tested
+ */
+ rc = comp_test_all_components ( attr_nm, assert_nm, csi_attr, ca );
+ break;
+
+ case LDAP_COMPREF_CONTENT:
+ /*
+ * <content> type CR is used
+ * check if it is followed by <select> type CR.
+ * 1) If so, look up the corresponding decoder in the mapping
+ * table(OID to decoder) by <select>
+ * and then decode the OCTET/BIT STRING with the decoder
+ * Finally, extract the target component with the remaining CR.
+ * 2) If not, just return the current component, It SHOULD not be
+ * extracted further, because the component MUST be BIT/OCTET
+ * string.
+ */
+
+ cr->cr_curr = cr->cr_curr->ci_next;
+ if ( !cr->cr_curr ) {
+ /* case 2) in above description */
+ rc = comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ break;
+ }
+
+ if ( cr->cr_curr->ci_type == LDAP_COMPREF_SELECT ) {
+ /* Look up OID mapping table */
+ odm = RetrieveOidDecoderMappingbyBV( &cr->cr_curr->ci_val.ci_select_value );
+
+ if ( !odm || !odm->BER_Decode )
+ return LDAP_PROTOCOL_ERROR;
+
+ /* current component MUST be either BIT or OCTET STRING */
+ if ( csi_attr->csi_comp_desc->cd_type_id != BASICTYPE_BITSTRING ) {
+ bv.bv_val = ((ComponentBits*)csi_attr)->value.bits;
+ bv.bv_len = ((ComponentBits*)csi_attr)->value.bitLen;
+ }
+ else if ( csi_attr->csi_comp_desc->cd_type_id != BASICTYPE_BITSTRING ) {
+ bv.bv_val = ((ComponentOcts*)csi_attr)->value.octs;
+ bv.bv_len = ((ComponentOcts*)csi_attr)->value.octetLen;
+ }
+ else
+ return LDAP_PROTOCOL_ERROR;
+
+ buf = ExpBufAllocBuf();
+ ExpBuftoGenBuf( buf, &b );
+ ExpBufInstallDataInBuf ( buf, bv.bv_val, bv.bv_len );
+ BufResetInReadMode( b );
+ mode = DEC_ALLOC_MODE_2;
+
+ /* Try to decode with BER/DER decoder */
+ rc = odm->BER_Decode ( attr_nm, b, (ComponentSyntaxInfo*)&contained_comp, &bytesDecoded, mode );
+
+ ExpBufFreeBuf( buf );
+ GenBufFreeBuf( b );
+
+ if ( rc != LDAP_SUCCESS ) return LDAP_PROTOCOL_ERROR;
+
+ /* xxx.content.(x.xy.xyz).rfc822Name */
+ /* In the aboe Ex. move CR to the right to (x.xy.xyz)*/
+ cr->cr_curr = cr->cr_curr->ci_next;
+ if (!cr->cr_curr )
+ rc = comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ else
+ rc = comp_test_components( attr_nm, assert_nm, contained_comp, ca );
+ }
+ else {
+ /* Invalid Component reference */
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+ break;
+ case LDAP_COMPREF_SELECT:
+ if (csi_attr->csi_comp_desc->cd_type_id != BASICTYPE_ANY )
+ return LDAP_INVALID_SYNTAX;
+ rc = CheckSelectTypeCorrect( attr_nm, ((ComponentAny*)csi_attr)->cai, &cr->cr_curr->ci_val.ci_select_value );
+ if ( rc < 0 ) return LDAP_INVALID_SYNTAX;
+
+ /* point to the real component, not any type component */
+ csi_attr = ((ComponentAny*)csi_attr)->value;
+ cr->cr_curr = cr->cr_curr->ci_next;
+ if ( cr->cr_curr )
+ rc = comp_test_components( attr_nm, assert_nm, csi_attr, ca);
+ else
+ rc = comp_test_one_component( attr_nm, assert_nm, csi_attr, ca);
+ break;
+ default:
+ rc = LDAP_INVALID_SYNTAX;
+ }
+ return rc;
+}
+
+
+void*
+comp_nibble_memory_allocator ( int init_mem, int inc_mem ) {
+ void* nm;
+ nm = (void*)InitNibbleMemLocal( (unsigned long)init_mem, (unsigned long)inc_mem );
+ if ( !nm ) return NULL;
+ else return (void*)nm;
+}
+
+void
+comp_nibble_memory_free ( void* nm ) {
+ ShutdownNibbleMemLocal( nm );
+}
+
+void*
+comp_get_component_description ( int id ) {
+ if ( asntype_to_compdesc_mapping_tbl[id].atcd_typeId == id )
+ return &asntype_to_compdesc_mapping_tbl[id].atcd_cd;
+ else
+ return NULL;
+}
+
+int
+comp_component_encoder ( void* mem_op, ComponentSyntaxInfo* csi , struct berval* nval ) {
+ int size, rc;
+ GenBuf* b;
+ ExpBuf* buf;
+ struct berval bv;
+
+ buf = ExpBufAllocBufAndData();
+ ExpBufResetInWriteRvsMode(buf);
+ ExpBuftoGenBuf( buf, &b );
+
+ if ( !csi->csi_comp_desc->cd_gser_encoder && !csi->csi_comp_desc->cd_ldap_encoder )
+ return (-1);
+
+ /*
+ * if an LDAP specific encoder is provided :
+ * dn and rdn have their LDAP specific encoder
+ */
+ if ( csi->csi_comp_desc->cd_ldap_encoder ) {
+ rc = csi->csi_comp_desc->cd_ldap_encoder( csi, &bv );
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ if ( mem_op )
+ nval->bv_val = CompAlloc( mem_op, bv.bv_len );
+ else
+ nval->bv_val = malloc( size );
+ memcpy( nval->bv_val, bv.bv_val, bv.bv_len );
+ nval->bv_len = bv.bv_len;
+ /*
+ * This free will be eliminated by making ldap_encoder
+ * use nibble memory in it
+ */
+ free ( bv.bv_val );
+ GenBufFreeBuf( b );
+ BufFreeBuf( buf );
+ return LDAP_SUCCESS;
+ }
+
+ rc = csi->csi_comp_desc->cd_gser_encoder( b, csi );
+ if ( rc < 0 ) {
+ GenBufFreeBuf( b );
+ BufFreeBuf( buf );
+ return rc;
+ }
+
+ size = ExpBufDataSize( buf );
+ if ( size > 0 ) {
+ if ( mem_op )
+ nval->bv_val = CompAlloc ( mem_op, size );
+ else
+ nval->bv_val = malloc( size );
+ nval->bv_len = size;
+ BufResetInReadMode(b);
+ BufCopy( nval->bv_val, b, size );
+ }
+ ExpBufFreeBuf( buf );
+ GenBufFreeBuf( b );
+
+ return LDAP_SUCCESS;
+}
+
+#if SLAPD_COMP_MATCH == SLAPD_MOD_DYNAMIC
+
+#include "certificate.h"
+
+extern convert_attr_to_comp_func* attr_converter;
+extern convert_assert_to_comp_func* assert_converter;
+extern convert_asn_to_ldap_func* csi_converter;
+extern free_component_func* component_destructor;
+extern test_component_func* test_components;
+extern alloc_nibble_func* nibble_mem_allocator;
+extern free_nibble_func* nibble_mem_free;
+extern test_membership_func* is_aliased_attribute;
+extern get_component_info_func* get_component_description;
+extern component_encoder_func* component_encoder;
+
+
+int init_module(int argc, char *argv[]) {
+ /*
+ * Initialize function pointers in slapd
+ */
+ attr_converter = (convert_attr_to_comp_func*)comp_convert_attr_to_comp;
+ assert_converter = (convert_assert_to_comp_func*)comp_convert_assert_to_comp;
+ component_destructor = (free_component_func*)comp_free_component;
+ test_components = (test_component_func*)comp_test_components;
+ nibble_mem_allocator = (free_nibble_func*)comp_nibble_memory_allocator;
+ nibble_mem_free = (free_nibble_func*)comp_nibble_memory_free;
+ is_aliased_attribute = (test_membership_func*)comp_is_aliased_attribute;
+ get_component_description = (get_component_info_func*)comp_get_component_description;
+ component_encoder = (component_encoder_func*)comp_component_encoder;
+
+ /* file path needs to be */
+ load_derived_matching_rule ("derived_mr.cfg");
+
+ /* the initialization for example X.509 certificate */
+ init_module_AuthenticationFramework();
+ init_module_AuthorityKeyIdentifierDefinition();
+ init_module_CertificateRevokationList();
+ init_attribute_aliasing_table ();
+ init_component_description_table ();
+ return 0;
+}
+
+#endif /* SLAPD_PASSWD */
diff --git a/contrib/slapd-modules/datamorph/Makefile b/contrib/slapd-modules/datamorph/Makefile
new file mode 100644
index 0000000..82bce49
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/Makefile
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2017 Ondřej Kuzník, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_DATAMORPH=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = datamorph.la
+MANPAGES = slapo-datamorph.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+datamorph.la: datamorph.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/datamorph/datamorph.c b/contrib/slapd-modules/datamorph/datamorph.c
new file mode 100644
index 0000000..7767586
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/datamorph.c
@@ -0,0 +1,2091 @@
+/* datamorph.c - enumerated and native integer value support */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed in 2016 by Ondřej Kuzník for Symas Corp.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DATAMORPH
+
+#include <inttypes.h>
+#include <ac/stdlib.h>
+
+#if defined(__linux__)
+#include <endian.h>
+
+#elif defined(sun)
+
+#define be16toh(x) BE_16(x)
+#define le16toh(x) LE_16(x)
+#define htobe16(x) BE_16(x)
+#define htole16(x) LE_16(x)
+
+#define be32toh(x) BE_32(x)
+#define le32toh(x) LE_32(x)
+#define htobe32(x) BE_32(x)
+#define htole32(x) LE_32(x)
+
+#define be64toh(x) BE_64(x)
+#define le64toh(x) LE_64(x)
+#define htobe64(x) BE_64(x)
+#define htole64(x) LE_64(x)
+
+#elif defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/endian.h>
+
+#elif defined(__OpenBSD__)
+#include <sys/endian.h>
+
+#define be16toh(x) betoh16(x)
+#define le16toh(x) letoh16(x)
+
+#define be32toh(x) betoh32(x)
+#define le32toh(x) letoh32(x)
+
+#define be64toh(x) betoh64(x)
+#define le64toh(x) letoh64(x)
+
+#elif defined(__BYTE_ORDER__) && \
+ ( defined(__GNUC__) || defined(__clang__) )
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define be16toh(x) (x)
+#define le16toh(x) __builtin_bswap16(x)
+#define htobe16(x) (x)
+#define htole16(x) __builtin_bswap16(x)
+
+#define be32toh(x) (x)
+#define le32toh(x) __builtin_bswap32(x)
+#define htobe32(x) (x)
+#define htole32(x) __builtin_bswap32(x)
+
+#define be64toh(x) (x)
+#define le64toh(x) __builtin_bswap64(x)
+#define htobe64(x) (x)
+#define htole64(x) __builtin_bswap64(x)
+
+#else
+#error "Only support pure big and little endian at the moment"
+#endif
+
+#else
+#error "I lack the way to check my endianness and convert to/from big-endian"
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldap_queue.h"
+
+typedef enum datamorph_type_t {
+ DATAMORPH_UNSET,
+ DATAMORPH_ENUM,
+ DATAMORPH_INT,
+} datamorph_type;
+
+typedef enum datamorph_flags_t {
+ DATAMORPH_FLAG_SIGNED = 1 << 0,
+ DATAMORPH_FLAG_LOWER = 1 << 1,
+ DATAMORPH_FLAG_UPPER = 1 << 2,
+} datamorph_flags;
+
+typedef union datamorph_interval_bound_t {
+ int64_t i;
+ uint64_t u;
+} datamorph_interval_bound;
+
+typedef struct transformation_info_t {
+ AttributeDescription *attr;
+ datamorph_type type;
+ union {
+ struct {
+ Avlnode *to_db;
+ struct berval from_db[256];
+ } maps;
+#define ti_enum info.maps
+ struct {
+ datamorph_flags flags;
+ unsigned int size;
+ datamorph_interval_bound lower, upper;
+ } interval;
+#define ti_int info.interval
+ } info;
+} transformation_info;
+
+typedef struct datamorph_enum_mapping_t {
+ struct berval wire_value;
+ uint8_t db_value;
+ transformation_info *transformation;
+} datamorph_enum_mapping;
+
+typedef struct datamorph_info_t {
+ Avlnode *transformations;
+ transformation_info *wip_transformation;
+} datamorph_info;
+
+static int
+transformation_mapping_cmp( const void *l, const void *r )
+{
+ const datamorph_enum_mapping *left = l, *right = r;
+
+ return ber_bvcmp( &left->wire_value, &right->wire_value );
+}
+
+static int
+transformation_info_cmp( const void *l, const void *r )
+{
+ const transformation_info *left = l, *right = r;
+
+ return ( left->attr == right->attr ) ? 0 :
+ ( left->attr < right->attr ) ? -1 :
+ 1;
+}
+
+static int
+transform_to_db_format_one(
+ Operation *op,
+ transformation_info *definition,
+ struct berval *value,
+ struct berval *outval )
+{
+ switch ( definition->type ) {
+ case DATAMORPH_ENUM: {
+ datamorph_enum_mapping *mapping, needle = { .wire_value = *value };
+ struct berval db_value = { .bv_len = 1 };
+
+ mapping = ldap_avl_find( definition->ti_enum.to_db, &needle,
+ transformation_mapping_cmp );
+ if ( !mapping ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' not mapped\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ db_value.bv_val = (char *)&mapping->db_value;
+ ber_dupbv( outval, &db_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ case DATAMORPH_INT: {
+ union {
+ char s[8];
+ uint8_t be8;
+ uint16_t be16;
+ uint32_t be32;
+ uint64_t be64;
+ } buf;
+ struct berval db_value = { .bv_val = buf.s };
+ char *ptr = value->bv_val + value->bv_len;
+ uint64_t unsigned_value;
+ int64_t signed_value;
+
+ assert( definition->ti_int.size == 1 ||
+ definition->ti_int.size == 2 ||
+ definition->ti_int.size == 4 ||
+ definition->ti_int.size == 8 );
+
+ /* Read number */
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = strtoll( value->bv_val, &ptr, 10 );
+ } else {
+ unsigned_value = strtoull( value->bv_val, &ptr, 10 );
+ }
+ if ( *value->bv_val == '\0' || *ptr != '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' not an integer\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ /* Check it's within configured bounds */
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ if ( signed_value < definition->ti_int.lower.i ||
+ signed_value > definition->ti_int.upper.i ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' doesn't fit configured constraints\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ } else {
+ if ( unsigned_value < definition->ti_int.lower.u ||
+ unsigned_value > definition->ti_int.upper.u ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' doesn't fit configured constraints\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ db_value.bv_len = definition->ti_int.size;
+ switch ( definition->ti_int.size ) {
+ case 1: {
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ buf.be8 = (unsigned char)((char)signed_value);
+ } else {
+ buf.be8 = unsigned_value;
+ }
+ break;
+ }
+ case 2: {
+ uint16_t h16;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h16 = signed_value;
+ } else {
+ h16 = unsigned_value;
+ }
+ buf.be16 = htobe16( h16 );
+ break;
+ }
+ case 4: {
+ uint32_t h32;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h32 = signed_value;
+ } else {
+ h32 = unsigned_value;
+ }
+ buf.be32 = htobe32( h32 );
+ break;
+ }
+ case 8: {
+ uint64_t h64;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h64 = signed_value;
+ } else {
+ h64 = unsigned_value;
+ }
+ buf.be64 = htobe64( h64 );
+ break;
+ }
+ }
+ ber_dupbv( outval, &db_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+transform_to_db_format(
+ Operation *op,
+ transformation_info *definition,
+ BerVarray values,
+ int numvals,
+ BerVarray *out )
+{
+ struct berval *value;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( numvals == 0 ) {
+ for ( value = values; value; value++, numvals++ )
+ ; /* Count them */
+ }
+
+ assert( out );
+ *out = ch_calloc( numvals + 1, sizeof(struct berval) );
+
+ for ( i = 0; i < numvals; i++ ) {
+ rc = transform_to_db_format_one(
+ op, definition, &values[i], &(*out)[i] );
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( rc ) {
+ for ( ; i >= 0; i-- ) {
+ ch_free((*out)[i].bv_val);
+ }
+ ch_free(*out);
+ }
+
+ return rc;
+}
+
+static int
+transform_from_db_format_one(
+ Operation *op,
+ transformation_info *definition,
+ struct berval *value,
+ struct berval *outval )
+{
+ switch ( definition->type ) {
+ case DATAMORPH_ENUM: {
+ uint8_t index = value->bv_val[0];
+ struct berval *val = &definition->info.maps.from_db[index];
+
+ if ( !BER_BVISNULL( val ) ) {
+ ber_dupbv( outval, val );
+ assert( outval->bv_val );
+ } else {
+ Debug( LDAP_DEBUG_ANY, "transform_from_db_format_one: "
+ "DB value %d has no mapping!\n",
+ index );
+ /* FIXME: probably still need to return an error */
+ BER_BVZERO( outval );
+ }
+ break;
+ }
+
+ case DATAMORPH_INT: {
+ char buf[24];
+ struct berval wire_value = { .bv_val = buf };
+ union lens_t {
+ uint8_t be8;
+ uint16_t be16;
+ uint32_t be32;
+ uint64_t be64;
+ } *lens = (union lens_t *)value->bv_val;
+ uint64_t unsigned_value;
+ int64_t signed_value;
+
+ if ( value->bv_len != definition->ti_int.size ) {
+ Debug( LDAP_DEBUG_ANY, "transform_from_db_format_one(%s): "
+ "unexpected DB value of length %lu when configured "
+ "for %u!\n",
+ definition->attr->ad_cname.bv_val, value->bv_len,
+ definition->ti_int.size );
+ /* FIXME: probably still need to return an error */
+ BER_BVZERO( outval );
+ break;
+ }
+
+ assert( definition->ti_int.size == 1 ||
+ definition->ti_int.size == 2 ||
+ definition->ti_int.size == 4 ||
+ definition->ti_int.size == 8 );
+
+ switch ( definition->ti_int.size ) {
+ case 1: {
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int8_t)lens->be8;
+ } else {
+ unsigned_value = (uint8_t)lens->be8;
+ }
+ break;
+ }
+ case 2: {
+ uint16_t h16 = be16toh( lens->be16 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int16_t)h16;
+ } else {
+ unsigned_value = (uint16_t)h16;
+ }
+ break;
+ }
+ case 4: {
+ uint32_t h32 = be32toh( lens->be32 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int32_t)h32;
+ } else {
+ unsigned_value = (uint32_t)h32;
+ }
+ break;
+ }
+ case 8: {
+ uint64_t h64 = be64toh( lens->be64 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int64_t)h64;
+ } else {
+ unsigned_value = (uint64_t)h64;
+ }
+ break;
+ }
+ }
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ wire_value.bv_len = sprintf( buf, "%" PRId64, signed_value );
+ } else {
+ wire_value.bv_len = sprintf( buf, "%" PRIu64, unsigned_value );
+ }
+ ber_dupbv( outval, &wire_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+transform_from_db_format(
+ Operation *op,
+ transformation_info *definition,
+ BerVarray values,
+ int numvals,
+ BerVarray *out )
+{
+ struct berval *value;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( numvals == 0 ) {
+ for ( value = values; value; value++, numvals++ )
+ ; /* Count them */
+ }
+
+ assert( out );
+ *out = ch_calloc( numvals + 1, sizeof(struct berval) );
+
+ for ( i = 0; i < numvals; i++ ) {
+ struct berval bv;
+ rc = transform_from_db_format_one( op, definition, &values[i], &bv );
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_bvarray_add( out, &bv );
+ }
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( rc ) {
+ for ( ; i >= 0; i-- ) {
+ ch_free( (*out)[i].bv_val );
+ }
+ ch_free( *out );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_filter( Operation *op, datamorph_info *ov, Filter *f )
+{
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ /* The matching rules are not in place,
+ * so the filter will be ignored */
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ default:
+ break;
+ return LDAP_SUCCESS;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR: {
+ for ( f = f->f_and; f; f = f->f_next ) {
+ int rc = datamorph_filter( op, ov, f );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+ } break;
+
+ case LDAP_FILTER_NOT:
+ return datamorph_filter( op, ov, f->f_not );
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE: {
+ transformation_info *t, needle = { .attr = f->f_ava->aa_desc };
+
+ t = ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp );
+ if ( t ) {
+ struct berval new_val;
+ int rc = transform_to_db_format_one(
+ op, t, &f->f_ava->aa_value, &new_val );
+ ch_free( f->f_ava->aa_value.bv_val );
+
+ if ( rc != LDAP_SUCCESS ) {
+ f->f_choice = SLAPD_FILTER_COMPUTED;
+ f->f_result = SLAPD_COMPARE_UNDEFINED;
+ } else {
+ f->f_ava->aa_value = new_val;
+ }
+ }
+ } break;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e = op->ora_e;
+ Attribute *a, *next;
+ AttributeDescription *stop = NULL;
+ int rc = LDAP_SUCCESS;
+
+ if ( !BER_BVISNULL( &e->e_nname ) && !BER_BVISEMPTY( &e->e_nname ) ) {
+ LDAPRDN rDN;
+ const char *p;
+ int i;
+
+ rc = ldap_bv2rdn_x( &e->e_nname, &rDN, (char **)&p, LDAP_DN_FORMAT_LDAP,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_op_add: "
+ "can't parse rdn: dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ transformation_info needle = {};
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &needle.attr, &p ) ) {
+ continue;
+ }
+
+ if ( ldap_avl_find( ov->transformations, &needle,
+ transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_add: "
+ "attempted to add transformed attribute in RDN\n" );
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to add transformed attribute in RDN" );
+ return rc;
+ }
+ }
+
+ for ( a = e->e_attrs; a && a->a_desc != stop; a = next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ next = a->a_next;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_to_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:
+ send_ldap_error(
+ op, rs, rc, "datamorph: trying to add values outside definitions" );
+ return rc;
+}
+
+static int
+datamorph_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *t, needle = { .attr = op->orc_ava->aa_desc };
+ int rc = SLAP_CB_CONTINUE;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( t ) {
+ struct berval new_val;
+
+ rc = transform_to_db_format_one(
+ op, t, &op->orc_ava->aa_value, &new_val );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_compare: "
+ "transformation failed for '%s', rc=%d\n",
+ op->orc_ava->aa_value.bv_val, rc );
+ rs->sr_err = rc = LDAP_COMPARE_FALSE;
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ ch_free( op->orc_ava->aa_value.bv_val );
+ op->orc_ava->aa_value = new_val;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+datamorph_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Modifications *mod;
+ int rc = SLAP_CB_CONTINUE;
+
+ for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
+ transformation_info *t, needle = { .attr = mod->sml_desc };
+ BerVarray new_vals = NULL;
+
+ if ( mod->sml_numvals == 0 ) continue; /* Nothing to transform */
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ assert( !mod->sml_nvalues );
+ rc = transform_to_db_format(
+ op, t, mod->sml_values, mod->sml_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ ber_bvarray_free( mod->sml_values );
+ mod->sml_values = new_vals;
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_mod: "
+ "dn=%s failed rc=%d\n",
+ op->o_req_ndn.bv_val, rc );
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to operate on values outside definitions" );
+ return rc;
+}
+
+static int
+datamorph_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ LDAPRDN rDN;
+ const char *p;
+ int i, rc;
+
+ rc = ldap_bv2rdn_x( &op->orr_nnewrdn, &rDN, (char **)&p,
+ LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_op_modrdn: "
+ "can't parse rdn for dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ transformation_info needle = {};
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &needle.attr, &p ) ) {
+ continue;
+ }
+
+ if ( ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_modrdn: "
+ "attempted to add transformed values in RDN\n" );
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to put transformed values in RDN" );
+ return rc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+datamorph_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *e_orig = rs->sr_entry;
+ AttributeDescription *stop = NULL;
+ Attribute *a, *next = NULL;
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return rc;
+ }
+
+ for ( a = e_orig->e_attrs; a && a->a_desc != stop; a = next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ next = a->a_next;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_from_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( !e ) {
+ if ( rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+ e = e_orig;
+ } else {
+ e = entry_dup( e_orig );
+ }
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = SLAP_CB_CONTINUE;
+ if ( e && e != e_orig ) {
+ rs_replace_entry( op, rs, on, e );
+ rs->sr_flags &= ~REP_ENTRY_MASK;
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+ } else if ( e && e != e_orig ) {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ int rc = SLAP_CB_CONTINUE;
+
+ /*
+ * 1. check all requested attributes -> register callback if one matches
+ * 2. check filter: parse filter, traverse, for configured attributes:
+ * - presence -> do not touch
+ * - ava -> replace assertion value with db value if possible, assertion with undefined otherwise
+ * - inequality -> ???
+ * - anything else -> undefined
+ * - might just check for equality and leave the rest to syntax?
+ * 3. unparse filter
+ */
+ if ( datamorph_filter( op, ov, op->ors_filter ) ) {
+ send_ldap_error(
+ op, rs, LDAP_OTHER, "datamorph: failed to process filter" );
+ return LDAP_OTHER;
+ }
+
+ return rc;
+}
+
+static int
+datamorph_entry_release_rw( Operation *op, Entry *e, int rw )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int rc = LDAP_SUCCESS;
+
+ if ( on->on_next ) {
+ rc = overlay_entry_release_ov( op, e, rw, on->on_next );
+ } else if ( on->on_info->oi_orig->bi_entry_release_rw ) {
+ /* FIXME: there should be a better way */
+ rc = on->on_info->oi_orig->bi_entry_release_rw( op, e, rw );
+ } else {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_entry_get_rw(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ep )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e_orig, *e = NULL;
+ int rc;
+
+ if ( on->on_next ) {
+ rc = overlay_entry_get_ov( op, ndn, oc, at, rw, ep, on->on_next );
+ } else {
+ /* FIXME: there should be a better way */
+ rc = on->on_info->oi_orig->bi_entry_get_rw( op, ndn, oc, at, rw, ep );
+ }
+ e_orig = *ep;
+
+ if ( rc == LDAP_SUCCESS && e_orig ) {
+ AttributeDescription *stop = NULL;
+ Attribute *a;
+
+ for ( a = e_orig->e_attrs; a; a = a->a_next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ t = ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_from_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !e ) {
+ e = entry_dup( e_orig );
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+ }
+ if ( e ) {
+ datamorph_entry_release_rw( op, e_orig, rw );
+ *ep = e;
+ }
+
+ return rc;
+
+fail:
+ if ( e ) {
+ entry_free( e );
+ }
+ (void)datamorph_entry_release_rw( op, *ep, rw );
+ return rc;
+}
+
+/* Schema */
+
+static int
+datamorphBlobValidate( Syntax *syntax, struct berval *in )
+{
+ /* any value allowed */
+ return LDAP_SUCCESS;
+}
+
+int
+datamorphBinarySignedOrderingMatch( int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = assertedValue;
+ ber_len_t v_len = value->bv_len;
+ ber_len_t av_len = asserted->bv_len;
+
+ /* Ordering:
+ * 1. Negative always before non-negative
+ * 2. Shorter before longer
+ * 3. Rest ordered by memory contents (they are big-endian numbers)
+ */
+ int match = ( *value->bv_val >= 0 ) - ( *asserted->bv_val >= 0 );
+
+ if ( match == 0 ) match = (int)v_len - (int)av_len;
+
+ if ( match == 0 ) match = memcmp( value->bv_val, asserted->bv_val, v_len );
+
+ /* If used in extensible match filter, match if value < asserted */
+ if ( flags & SLAP_MR_EXT ) match = ( match >= 0 );
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphUnsignedIndexer( slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof(struct berval) * ( i + 1 ), ctx );
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ ber_dupbv_x( &keys[i], &values[i], ctx );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphUnsignedFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ BerValue *value = assertedValue;
+
+ keys = slap_sl_malloc( sizeof(struct berval) * 2, ctx );
+ ber_dupbv_x( &keys[0], value, ctx );
+
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphSignedIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof(struct berval) * ( i + 1 ), ctx );
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ keys[i].bv_len = values[i].bv_len + 1;
+ keys[i].bv_val = slap_sl_malloc( keys[i].bv_len, ctx );
+
+ /* if positive (highest bit is not set), note that in the first byte */
+ *keys[i].bv_val = ~( *values[i].bv_val & 0x80 );
+ AC_MEMCPY( keys[i].bv_val + 1, values[i].bv_val, values[i].bv_len );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphSignedFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ BerValue *value = assertedValue;
+
+ keys = slap_sl_malloc( sizeof(struct berval) * 2, ctx );
+
+ keys[0].bv_len = value->bv_len + 1;
+ keys[0].bv_val = slap_sl_malloc( keys[0].bv_len, ctx );
+
+ /* if positive (highest bit is not set), note that in the first byte */
+ *keys[0].bv_val = ~( *value->bv_val & 0x80 );
+ AC_MEMCPY( keys[0].bv_val + 1, value->bv_val, value->bv_len );
+
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+#define DATAMORPH_ARC "1.3.6.1.4.1.4203.666.11.12"
+
+#define DATAMORPH_SYNTAXES DATAMORPH_ARC ".1"
+#define DATAMORPH_SYNTAX_BASE DATAMORPH_SYNTAXES ".1"
+#define DATAMORPH_SYNTAX_ENUM DATAMORPH_SYNTAXES ".2"
+#define DATAMORPH_SYNTAX_INT DATAMORPH_SYNTAXES ".3"
+#define DATAMORPH_SYNTAX_SIGNED_INT DATAMORPH_SYNTAXES ".4"
+
+#define DATAMORPH_MATCHES DATAMORPH_ARC ".2"
+#define DATAMORPH_MATCH_EQUALITY DATAMORPH_MATCHES ".1"
+#define DATAMORPH_MATCH_SIGNED_EQUALITY DATAMORPH_MATCHES ".2"
+#define DATAMORPH_MATCH_ORDERING DATAMORPH_MATCHES ".3"
+#define DATAMORPH_MATCH_SIGNED_ORDERING DATAMORPH_MATCHES ".4"
+
+static char *datamorph_sups[] = {
+ DATAMORPH_SYNTAX_BASE,
+ NULL
+};
+
+static char *datamorphSyntaxes[] = {
+ DATAMORPH_SYNTAX_SIGNED_INT,
+ DATAMORPH_SYNTAX_ENUM,
+ DATAMORPH_SYNTAX_INT,
+
+ NULL
+};
+
+static slap_syntax_defs_rec datamorph_syntax_defs[] = {
+ { "( " DATAMORPH_SYNTAX_BASE " DESC 'Fixed size value' )",
+ 0, NULL, NULL, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_ENUM " DESC 'Enumerated value' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_INT " DESC 'Fixed-size integer' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_SIGNED_INT " DESC 'Fixed-size signed integer' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static Syntax *datamorph_base_syntax;
+
+static slap_mrule_defs_rec datamorph_mrule_defs[] = {
+ { "( " DATAMORPH_MATCH_EQUALITY
+ " NAME 'fixedSizeIntegerMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_INT " )",
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ datamorphSyntaxes + 1,
+ NULL, NULL, octetStringOrderingMatch,
+ datamorphUnsignedIndexer, datamorphUnsignedFilter,
+ NULL
+ },
+
+ { "( " DATAMORPH_MATCH_SIGNED_EQUALITY
+ " NAME 'fixedSizeSignedIntegerMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_SIGNED_INT " )",
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ NULL,
+ NULL, NULL, datamorphBinarySignedOrderingMatch,
+ datamorphSignedIndexer, datamorphSignedFilter,
+ NULL
+ },
+
+ { "( " DATAMORPH_MATCH_ORDERING
+ " NAME 'fixedSizeIntegerOrderingMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_INT " )",
+ SLAP_MR_ORDERING|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ datamorphSyntaxes + 1,
+ NULL, NULL, octetStringOrderingMatch,
+ datamorphUnsignedIndexer, datamorphUnsignedFilter,
+ "octetStringMatch" },
+
+ { "( " DATAMORPH_MATCH_SIGNED_ORDERING
+ " NAME 'fixedSizeSignedIntegerOrderingMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_SIGNED_INT " )",
+ SLAP_MR_ORDERING|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ NULL,
+ NULL, NULL, datamorphBinarySignedOrderingMatch,
+ datamorphSignedIndexer, datamorphSignedFilter,
+ "octetStringMatch" },
+
+ { NULL, SLAP_MR_NONE, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/* Configuration */
+
+static ConfigLDAPadd datamorph_ldadd_enum;
+static ConfigLDAPadd datamorph_ldadd_interval;
+static ConfigLDAPadd datamorph_ldadd_mapping;
+
+static ConfigDriver datamorph_set_attribute;
+static ConfigDriver datamorph_set_size;
+static ConfigDriver datamorph_set_signed;
+static ConfigDriver datamorph_set_bounds;
+static ConfigDriver datamorph_set_index;
+static ConfigDriver datamorph_set_value;
+static ConfigDriver datamorph_add_mapping;
+static ConfigDriver datamorph_add_transformation;
+
+static ConfigCfAdd datamorph_cfadd;
+
+enum {
+ DATAMORPH_INT_SIZE = 1,
+ DATAMORPH_INT_SIGNED,
+ DATAMORPH_INT_LOWER,
+ DATAMORPH_INT_UPPER,
+
+ DATAMORPH_INT_LAST,
+};
+
+static ConfigTable datamorph_cfg[] = {
+ { "datamorph_attribute", "attr", 2, 2, 0,
+ ARG_STRING|ARG_QUOTE|ARG_MAGIC,
+ datamorph_set_attribute,
+ "( OLcfgCtAt:7.1 NAME 'olcDatamorphAttribute' "
+ "DESC 'Attribute to transform' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_size", "<1|2|4|8>", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|DATAMORPH_INT_SIZE,
+ datamorph_set_size,
+ "( OLcfgCtAt:7.2 NAME 'olcDatamorphIntegerBytes' "
+ "DESC 'Integer size in bytes' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_signed", "TRUE|FALSE", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|DATAMORPH_INT_SIGNED,
+ datamorph_set_signed,
+ "( OLcfgCtAt:7.3 NAME 'olcDatamorphIntegerSigned' "
+ "DESC 'Whether integers maintain sign' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_lower_bound", "int", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|DATAMORPH_INT_LOWER,
+ datamorph_set_bounds,
+ "( OLcfgCtAt:7.4 NAME 'olcDatamorphIntegerLowerBound' "
+ "DESC 'Lowest valid value for the attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_upper_bound", "int", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|DATAMORPH_INT_UPPER,
+ datamorph_set_bounds,
+ "( OLcfgCtAt:7.5 NAME 'olcDatamorphIntegerUpperBound' "
+ "DESC 'Highest valid value for the attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ /* These have no equivalent in slapd.conf */
+ { "", NULL, 2, 2, 0,
+ ARG_INT|ARG_MAGIC,
+ datamorph_set_index,
+ "( OLcfgCtAt:7.6 NAME 'olcDatamorphIndex' "
+ "DESC 'Internal DB value' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ datamorph_set_value,
+ "( OLcfgCtAt:7.7 NAME 'olcDatamorphValue' "
+ "DESC 'Wire value' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ /* slapd.conf alternative for the two above */
+ { "datamorph_value", "int> <name", 3, 3, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ datamorph_add_mapping,
+ NULL, NULL, NULL
+ },
+
+ /* slapd.conf alternative for objectclasses below */
+ { "datamorph", "enum|int> <attr", 3, 3, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ datamorph_add_transformation,
+ NULL, NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs datamorph_ocs[] = {
+ { "( OLcfgCtOc:7.1 "
+ "NAME 'olcDatamorphConfig' "
+ "DESC 'Datamorph overlay configuration' "
+ "SUP olcOverlayConfig )",
+ Cft_Overlay, datamorph_cfg, NULL, datamorph_cfadd },
+ { "( OLcfgCtOc:7.2 "
+ "NAME 'olcTransformation' "
+ "DESC 'Transformation configuration' "
+ "MUST ( olcDatamorphAttribute ) "
+ "SUP top "
+ "ABSTRACT )",
+ Cft_Misc, datamorph_cfg, NULL },
+ { "( OLcfgCtOc:7.3 "
+ "NAME 'olcDatamorphEnum' "
+ "DESC 'Configuration for an enumerated attribute' "
+ "SUP olcTransformation "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_enum },
+ { "( OLcfgCtOc:7.4 "
+ "NAME 'olcDatamorphInteger' "
+ "DESC 'Configuration for a compact integer attribute' "
+ "MUST ( olcDatamorphIntegerBytes ) "
+ "MAY ( olcDatamorphIntegerLowerBound $ "
+ "olcDatamorphIntegerUpperBound $ "
+ "olcDatamorphIntegerSigned "
+ ") "
+ "SUP olcTransformation "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_interval },
+ { "( OLcfgCtOc:7.5 "
+ "NAME 'olcDatamorphEnumValue' "
+ "DESC 'Configuration for an enumerated attribute' "
+ "MUST ( olcDatamorphIndex $ "
+ "olcDatamorphValue "
+ ") "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_mapping },
+
+ { NULL, 0, NULL }
+};
+
+static void
+datamorph_mapping_free( void *arg )
+{
+ datamorph_enum_mapping *mapping = arg;
+
+ ch_free( mapping->wire_value.bv_val );
+ ch_free( mapping );
+}
+
+static void
+datamorph_info_free( void *arg )
+{
+ transformation_info *info = arg;
+
+ if ( info->type == DATAMORPH_ENUM ) {
+ ldap_avl_free( info->ti_enum.to_db, datamorph_mapping_free );
+ }
+ ch_free( info );
+}
+
+static int
+datamorph_set_attribute( ConfigArgs *ca )
+{
+ transformation_info needle = {}, *info = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ char *s = ca->value_string;
+ const char *text;
+ int rc = LDAP_SUCCESS;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_string = info->attr->ad_cname.bv_val;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info = ldap_avl_delete( &ov->transformations, info,
+ transformation_info_cmp );
+ assert( info );
+
+ info->attr = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( *s == '{' ) {
+ s = strchr( s, '}' );
+ if ( !s ) {
+ rc = LDAP_UNDEFINED_TYPE;
+ goto done;
+ }
+ s += 1;
+ }
+
+ rc = slap_str2ad( s, &info->attr, &text );
+ ch_free( ca->value_string );
+ if ( rc ) {
+ goto done;
+ }
+
+ /* The type has to be set appropriately */
+ if ( !info->attr->ad_type->sat_syntax->ssyn_sups ||
+ info->attr->ad_type->sat_syntax->ssyn_sups[0] !=
+ datamorph_base_syntax ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "improper syntax for attribute %s",
+ info->attr->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ needle.attr = info->attr;
+ if ( ldap_avl_find( ov->transformations, &needle, transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+done:
+ if ( rc ) {
+ ca->reply.err = rc;
+ }
+ return rc;
+}
+
+static int
+datamorph_set_size( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = info->ti_int.size;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.size = 0;
+ return LDAP_SUCCESS;
+ }
+
+ if ( ca->value_int != 1 &&
+ ca->value_int != 2 &&
+ ca->value_int != 4 &&
+ ca->value_int != 8 ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "invalid size %d",
+ ca->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ info->ti_int.size = ca->value_int;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_signed( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = info->ti_int.flags & DATAMORPH_FLAG_SIGNED;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.flags &= ~DATAMORPH_FLAG_SIGNED;
+ return LDAP_SUCCESS;
+ }
+
+ info->ti_int.flags &= ~DATAMORPH_FLAG_SIGNED;
+ if ( ca->value_int ) {
+ info->ti_int.flags |= DATAMORPH_FLAG_SIGNED;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_bounds( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+ datamorph_interval_bound *bound;
+ uint64_t unsigned_bound;
+ int64_t signed_bound;
+ char *ptr = ca->value_bv.bv_val + ca->value_bv.bv_len;
+ int flag;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ switch ( ca->type ) {
+ case DATAMORPH_INT_LOWER:
+ bound = &info->ti_int.lower;
+ flag = DATAMORPH_FLAG_LOWER;
+ break;
+ case DATAMORPH_INT_UPPER:
+ bound = &info->ti_int.upper;
+ flag = DATAMORPH_FLAG_UPPER;
+ break;
+ default:
+ assert(0);
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ char buf[24];
+ struct berval bv = { .bv_val = buf };
+
+ if ( !(info->ti_int.flags & flag) ) {
+ /* Bound not set, do not emit */
+ return LDAP_SUCCESS;
+ }
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bv.bv_len = sprintf( buf, "%" PRId64, bound->i );
+ } else {
+ bv.bv_len = sprintf( buf, "%" PRIu64, bound->u );
+ }
+ ber_dupbv_x( &ca->value_bv, &bv, ca->ca_op->o_tmpmemctx );
+
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.flags &= ~flag;
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bound->i = (flag == DATAMORPH_FLAG_LOWER) ? INT64_MIN : INT64_MAX;
+ } else {
+ bound->u = (flag == DATAMORPH_FLAG_LOWER) ? 0 : UINT64_MAX;
+ }
+ return LDAP_SUCCESS;
+ }
+
+ /* FIXME: if attributes in the Add operation come in the wrong order
+ * (signed=true after the bound definition), we can't check the interval
+ * sanity. */
+ /*
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_bound = strtoll( ca->value_bv.bv_val, &ptr, 10 );
+ } else {
+ unsigned_bound = strtoull( ca->value_bv.bv_val, &ptr, 10 );
+ }
+ */
+ /* Also, no idea what happens in the case of big-endian, hopefully,
+ * it behaves the same */
+ unsigned_bound = strtoull( ca->value_bv.bv_val, &ptr, 10 );
+ signed_bound = (int64_t)unsigned_bound;
+
+ if ( *ca->value_bv.bv_val == '\0' || *ptr != '\0' ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "failed to parse '%s' as integer",
+ ca->value_bv.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ ch_free( ca->value_bv.bv_val );
+
+ info->ti_int.flags |= flag;
+ switch ( info->ti_int.size ) {
+ case 1:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT8_MIN || signed_bound > INT8_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT8_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 2:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT16_MIN || signed_bound > INT16_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT16_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 4:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT32_MIN || signed_bound > INT32_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT32_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 8:
+ break;
+ default:
+ /* Should only happen in these two cases:
+ * 1. datamorph_size not yet encountered for this one (when
+ * processing slapd.conf)
+ * 2. When someone runs a fun modification on the config entry
+ * messing with more attributes at once
+ *
+ * The error message is expected to be helpful only for the former,
+ * so use the slapd.conf name.
+ */
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "datamorph_size has to be set first!" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bound->i = signed_bound;
+ } else {
+ bound->u = unsigned_bound;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_value( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ char *s = ca->value_bv.bv_val;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ /* We generate the value as part of the RDN, don't add anything */
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ch_free( mapping->wire_value.bv_val );
+ BER_BVZERO( &mapping->wire_value );
+ /* TODO: remove from info->ti_enum.to_db? */
+ return LDAP_SUCCESS;
+ }
+
+ /* As long as this attribute can be in the RDN,
+ * we have to expect the '{n}' prefix */
+ if ( *s == '{' ) {
+ ber_len_t len;
+ s = memchr( s, '}', ca->value_bv.bv_len );
+ if ( !s ) {
+ ca->reply.err = LDAP_UNDEFINED_TYPE;
+ return ca->reply.err;
+ }
+ s += 1;
+
+ len = ca->value_bv.bv_len - ( s - ca->value_bv.bv_val );
+ ber_str2bv( s, len, 1, &mapping->wire_value );
+ ch_free( ca->value_bv.bv_val );
+ } else {
+ mapping->wire_value = ca->value_bv;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_index( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ struct berval *from_db = mapping->transformation->ti_enum.from_db;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = mapping->db_value;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ BER_BVZERO( &from_db[mapping->db_value] );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ca->value_int < 0 || ca->value_int >= 256 ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ } else if ( !BER_BVISNULL( &from_db[ca->value_int] ) ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "duplicate index %d",
+ ca->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ mapping->db_value = ca->value_int;
+ from_db[ca->value_int] = mapping->wire_value;
+
+ return LDAP_SUCCESS;
+}
+
+/* Called when processing slapd.conf only,
+ * cn=config uses the objectclass to decide which type we're dealing with.
+ */
+static int
+datamorph_add_transformation( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info;
+
+ if ( ov->wip_transformation ) {
+ /* We checked everything as were processing the lines */
+ int rc = ldap_avl_insert( &ov->transformations, ov->wip_transformation,
+ transformation_info_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ info = ch_calloc( 1, sizeof(transformation_info) );
+ ov->wip_transformation = ca->ca_private = info;
+
+ if ( !strcasecmp( ca->argv[1], "enum" ) ) {
+ info->type = DATAMORPH_ENUM;
+ } else if ( !strcasecmp( ca->argv[1], "int" ) ) {
+ info->type = DATAMORPH_INT;
+ } else {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "unknown transformation type '%s'", ca->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ ca->value_string = strdup( ca->argv[2] );
+
+ return datamorph_set_attribute( ca );
+}
+
+static int
+datamorph_add_mapping( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info = ov->wip_transformation;
+ datamorph_enum_mapping *mapping;
+ int rc = LDAP_CONSTRAINT_VIOLATION;
+
+ if ( !info ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "no attribute configured" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ mapping = ch_calloc( 1, sizeof(datamorph_enum_mapping) );
+ mapping->transformation = info;
+ ca->ca_private = mapping;
+
+ ber_str2bv( ca->argv[2], 0, 1, &ca->value_bv );
+ rc = datamorph_set_value( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = lutil_atoix( &ca->value_int, ca->argv[1], 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "invalid integer %s",
+ ca->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ rc = datamorph_set_index( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_avl_insert( &info->ti_enum.to_db, mapping,
+ transformation_mapping_cmp, ldap_avl_dup_error );
+ }
+ if ( rc ) {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+datamorph_ldadd_info_cleanup( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info = ca->ca_private;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+fail:
+ ch_free( info );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ldap_avl_insert( &ov->transformations, info, transformation_info_cmp,
+ ldap_avl_dup_error ) ) {
+ goto fail;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_transformation(
+ CfEntryInfo *cei,
+ Entry *e,
+ ConfigArgs *ca,
+ datamorph_type type )
+{
+ transformation_info *info;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != datamorph_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ info = ch_calloc( 1, sizeof(transformation_info) );
+ info->type = type;
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = info;
+ config_push_cleanup( ca, datamorph_ldadd_info_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * enable the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_enum( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ return datamorph_ldadd_transformation( cei, e, ca, DATAMORPH_ENUM );
+}
+
+static int
+datamorph_ldadd_interval( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ return datamorph_ldadd_transformation( cei, e, ca, DATAMORPH_INT );
+}
+
+static int
+datamorph_ldadd_mapping_cleanup( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ transformation_info *info = mapping->transformation;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+fail:
+ datamorph_mapping_free( mapping );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ldap_avl_insert( &info->ti_enum.to_db, mapping, transformation_mapping_cmp,
+ ldap_avl_dup_error ) ) {
+ goto fail;
+ }
+ info->ti_enum.from_db[mapping->db_value] = mapping->wire_value;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_mapping( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ transformation_info *info;
+ datamorph_enum_mapping *mapping;
+ CfEntryInfo *parent = cei->ce_parent;
+
+ if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
+ parent->ce_bi->bi_cf_ocs != datamorph_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ info = cei->ce_private;
+
+ mapping = ch_calloc( 1, sizeof(datamorph_enum_mapping) );
+ mapping->transformation = info;
+
+ ca->ca_private = mapping;
+ config_push_cleanup( ca, datamorph_ldadd_mapping_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * enable the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+struct datamorph_cfadd_args {
+ Operation *op;
+ SlapReply *rs;
+ Entry *p;
+ ConfigArgs *ca;
+ int index;
+};
+
+static int
+datamorph_config_build_enum( void *item, void *arg )
+{
+ datamorph_enum_mapping *mapping = item;
+ struct datamorph_cfadd_args *args = arg;
+ struct berval rdn;
+ Entry *e;
+ char *p;
+ ber_len_t index;
+
+ rdn.bv_len = snprintf( args->ca->cr_msg, sizeof(args->ca->cr_msg),
+ "olcDatamorphValue={%d}", args->index++ );
+ rdn.bv_val = args->ca->cr_msg;
+ p = rdn.bv_val + rdn.bv_len;
+
+ rdn.bv_len += mapping->wire_value.bv_len;
+ for ( index = 0; index < mapping->wire_value.bv_len; index++ ) {
+ if ( RDN_NEEDSESCAPE(mapping->wire_value.bv_val[index]) ) {
+ rdn.bv_len++;
+ *p++ = '\\';
+ }
+ *p++ = mapping->wire_value.bv_val[index];
+ }
+ *p = '\0';
+
+ args->ca->ca_private = mapping;
+ args->ca->ca_op = args->op;
+ e = config_build_entry( args->op, args->rs, args->p->e_private, args->ca,
+ &rdn, &datamorph_ocs[4], NULL );
+ assert( e );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_config_build_attr( void *item, void *arg )
+{
+ transformation_info *info = item;
+ struct datamorph_cfadd_args *args = arg;
+ struct berval rdn;
+ ConfigOCs *oc;
+ Entry *e;
+
+ rdn.bv_len = snprintf( args->ca->cr_msg, sizeof(args->ca->cr_msg),
+ "olcDatamorphAttribute={%d}%s", args->index++,
+ info->attr->ad_cname.bv_val );
+ rdn.bv_val = args->ca->cr_msg;
+
+ switch ( info->type ) {
+ case DATAMORPH_ENUM:
+ oc = &datamorph_ocs[2];
+ break;
+ case DATAMORPH_INT:
+ oc = &datamorph_ocs[3];
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ args->ca->ca_private = info;
+ args->ca->ca_op = args->op;
+ e = config_build_entry(
+ args->op, args->rs, args->p->e_private, args->ca, &rdn, oc, NULL );
+ assert( e );
+
+ if ( info->type == DATAMORPH_ENUM ) {
+ struct datamorph_cfadd_args new_args = *args;
+ new_args.p = e;
+ new_args.index = 0;
+
+ return ldap_avl_apply( info->ti_enum.to_db, datamorph_config_build_enum,
+ &new_args, 1, AVL_PREORDER );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ struct datamorph_cfadd_args args = {
+ .op = op,
+ .rs = rs,
+ .p = p,
+ .ca = ca,
+ .index = 0,
+ };
+
+ if ( ov->wip_transformation ) {
+ /* There is one last item that is unfinished */
+ int rc = ldap_avl_insert( &ov->transformations, ov->wip_transformation,
+ transformation_info_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ return ldap_avl_apply( ov->transformations, &datamorph_config_build_attr, &args,
+ 1, AVL_PREORDER );
+}
+
+static slap_overinst datamorph;
+
+static int
+datamorph_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ datamorph_info *ov;
+
+ /* TODO: can this be global? */
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph overlay must be instantiated "
+ "within a database.\n" );
+ return 1;
+ }
+
+ ov = ch_calloc( 1, sizeof(datamorph_info) );
+ on->on_bi.bi_private = ov;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+
+ if ( ov ) {
+ ldap_avl_free( ov->transformations, datamorph_info_free );
+ }
+ ch_free( ov );
+
+ return LDAP_SUCCESS;
+}
+
+int
+datamorph_initialize()
+{
+ int rc, i;
+
+ datamorph.on_bi.bi_type = "datamorph";
+ datamorph.on_bi.bi_db_init = datamorph_db_init;
+ datamorph.on_bi.bi_db_destroy = datamorph_db_destroy;
+
+ datamorph.on_bi.bi_op_add = datamorph_op_add;
+ datamorph.on_bi.bi_op_compare = datamorph_op_compare;
+ datamorph.on_bi.bi_op_modify = datamorph_op_mod;
+ datamorph.on_bi.bi_op_modrdn = datamorph_op_modrdn;
+ datamorph.on_bi.bi_op_search = datamorph_op_search;
+ datamorph.on_response = datamorph_response;
+
+ datamorph.on_bi.bi_entry_release_rw = datamorph_entry_release_rw;
+ datamorph.on_bi.bi_entry_get_rw = datamorph_entry_get_rw;
+
+ datamorph.on_bi.bi_cf_ocs = datamorph_ocs;
+
+ for ( i = 0; datamorph_syntax_defs[i].sd_desc != NULL; i++ ) {
+ rc = register_syntax( &datamorph_syntax_defs[i] );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_initialize: "
+ "error registering syntax %s\n",
+ datamorph_syntax_defs[i].sd_desc );
+ return rc;
+ }
+ }
+
+ datamorph_base_syntax = syn_find( DATAMORPH_SYNTAX_BASE );
+ assert( datamorph_base_syntax );
+
+ for ( i = 0; datamorph_mrule_defs[i].mrd_desc != NULL; i++ ) {
+ rc = register_matching_rule( &datamorph_mrule_defs[i] );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_initialize: "
+ "error registering matching rule %s\n",
+ datamorph_mrule_defs[i].mrd_desc );
+ return rc;
+ }
+ }
+
+ rc = config_register_schema( datamorph_cfg, datamorph_ocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &datamorph );
+}
+
+#if SLAPD_OVER_DATAMORPH == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return datamorph_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_DATAMORPH */
diff --git a/contrib/slapd-modules/datamorph/slapo-datamorph.5 b/contrib/slapd-modules/datamorph/slapo-datamorph.5
new file mode 100644
index 0000000..0ce0c6a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/slapo-datamorph.5
@@ -0,0 +1,338 @@
+.TH SLAPO-DATAMORPH 5 "RELEASEDATE" "OpenLDAP"
+.\" Copyright 2016-2017 Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See LICENSE.
+.SH NAME
+slapo\-datamorph \- store enumerated values and fixed size integers
+.SH SYNOPSIS
+olcOverlay=datamorph
+.SH DESCRIPTION
+The
+.B datamorph
+overlay to
+.BR slapd (8)
+allows attributes with a few pre-defined values to be saved more
+space-efficiently as well as signed or unsigned integer attributes.
+
+.LP
+The overlay operates on configured attributes that must have their syntax
+compatible with
+.BR 1.3.6.1.4.1.4203.666.11.12.1.1 ,
+there are three such syntaxes defined by the overlay:
+.B 1.3.6.1.4.1.4203.666.11.12.1.2
+(Enumerated value),
+.B 1.3.6.1.4.1.4203.666.11.12.1.3
+(Fixed-size integer), and
+.B 1.3.6.1.4.1.4203.666.11.12.1.4
+(Fixed-size signed integer).
+.LP
+
+While transforming the request, if a value for an attribute is not permitted by the configuration, the behaviour depends on the operation:
+
+.RS
+.TP
+.B Search
+The affected value assertions in a
+.B Search
+request filter are replaced by a filter returning
+.B Undefined .
+.TP
+.B Compare
+Request returns
+.B Compare
+.BR False .
+.TP
+.B Add, Modify
+Requests are rejected with a
+.B Constraint
+.BR Violation .
+.RE
+
+The supported allowed matching rules for the attribute types above are:
+
+.RS
+.TP
+.B EQUALITY fixedSizeIntegerMatch
+Appropriate for syntaxes
+.B 1.3.6.1.4.1.4203.666.11.12.1.2
+(Enumerated value), and
+.B 1.3.6.1.4.1.4203.666.11.12.1.3
+(Fixed-size integer).
+.TP
+.B EQUALITY fixedSizeSignedIntegerMatch
+Appropriate for syntax
+.B 1.3.6.1.4.1.4203.666.11.12.1.4
+(Fixed-size signed integer) only.
+.TP
+.B ORDERING fixedSizeIntegerOrderingMatch
+Appropriate for syntaxes
+.B 1.3.6.1.4.1.4203.666.11.12.1.2
+(Enumerated value), and
+.B 1.3.6.1.4.1.4203.666.11.12.1.3
+(Fixed-size integer). Enumerated value attributes are compared according to
+their stored database value.
+.TP
+.B ORDERING fixedSizeSignedIntegerOrderingMatch
+Appropriate for syntax
+.B 1.3.6.1.4.1.4203.666.11.12.1.4
+(Fixed-size signed integer) only.
+
+
+.SH CONFIGURATION LAYOUT
+
+The overlay has to be instantiated under a database adding an entry of
+.B olcOverlay=datamorph
+with objectClass of
+.BR olcDatamorphConfig.
+
+The overlay configuration subtree consists of the following levels:
+.RS
+.TP
+.B objectClass=olcDatamorphConfig
+Main overlay configuration. Created directly under the database
+configuration entry.
+.TP
+.B objectClass=olcDatamorphInteger
+Specifies a
+.B fixed-size integer
+attribute and must be a child of an entry with
+.BR objectClass=olcDatamorphConfig .
+There may be as many such entries as necessary provided they all specify a
+different attribute in the
+.B olcDatamorphAttribute
+attribute.
+.TP
+.B objectClass=olcDatamorphEnum
+Specifies an
+.B enumerated
+attribute and must be a child of an entry with
+.BR objectClass=olcDatamorphConfig .
+There may be as many such entries as necessary provided they all specify a
+different attribute in the
+.B olcDatamorphAttribute
+attribute.
+.TP
+.B objectClass=olcDatamorphEnumValue
+Specifies a permitted value for the enumerated attribute and its database
+representation. Must be a child of an entry with
+.BR objectClass=olcDatamorphEnum .
+There may be as many such entries as necessary provided they all specify a
+different value and index in the corresponding fields.
+.RE
+
+In the case of
+.BR slapd.conf (5),
+the attribute definition is delimited by the keyword
+.B datamorph
+to define an integer or enumerated attribute followed by an arbitrary number of
+.B datamorph_value
+lines in the case of an enumerated one. Each new
+.B datamorph
+line starts configuring a new attribute.
+
+.SH ENUMERATED ATTRIBUTE CONFIGURATION ENTRY
+
+The enumerated attribute entry configuration
+.RB ( olcDatamorphEnum )
+only has the following option available:
+
+.RS
+.TP
+.B olcDatamorphAttribute: <attribute>
+Mandatory attribute, indicates that the named attribute is to be handled by the
+overlay. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph "int"
+.BR <attribute> .
+.RE
+
+The children of this entry then define how the string values map to the
+database values. They use the objectclass
+.BR olcDatamorphEnumValue ,
+which asks for the following attributes:
+
+.RS
+.TP
+.B olcDatamorphValue: <value>
+A permitted value for the attribute being configured.
+.TP
+.B olcDatamorphIndex: <0-255>
+The corresponding database value.
+.RE
+
+The
+.BR slapd.conf (5)
+equivalent of the above two is
+.B datamorph_value <0-255> <value>
+.RB .
+
+.SH FIXED-WIDTH INTEGER CONFIGURATION ENTRY
+
+The fixed-width integer configuration entry
+.RB ( olcDatamorphInteger )
+has the following options available:
+
+.RS
+.TP
+.B olcDatamorphAttribute: <attribute>
+Mandatory attribute, indicates that the named attribute is to be handled by the
+overlay. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph "int"
+.BR <attribute> .
+.TP
+.B olcDatamorphIntegerBytes: <1|2|4|8>
+Size of the integer as stored in the backend. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_size
+.BR <1|2|4|8> .
+.TP
+.B olcDatamorphIntegerSigned: <TRUE|FALSE>
+Whether the integer is to be treated as signed. Note that the overlay will not
+enforce consistency between this option and the attribute's syntax. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_signed
+.BR <TRUE|FALSE> .
+.TP
+.B olcDatamorphIntegerLowerBound: <number>
+The lowest value that the configured attribute will be allowed to have. This
+affects all operations where values are mentioned. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_lower_bound
+.BR <number> .
+.TP
+.B olcDatamorphIntegerUpperBound: <number>
+The highest value that the configured attribute will be allowed to have. This
+affects all operations where values are mentioned. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_upper_bound
+.BR <number> .
+.RE
+
+.SH EXAMPLE
+
+The following is an example of a configured overlay, substitute
+.B $DATABASE
+for the DN of the database it is attached to and
+.B {x}
+with the desired position of the overlay in the overlay stack.
+
+.nf
+dn: olcOverlay={x}datamorph,$DATABASE
+objectClass: olcDatamorphConfig
+olcOverlay: datamorph
+
+# to handle attribute 'enumeratedAttribute'
+dn: olcDatamorphAttribute=enumeratedAttribute,olcOverlay={x}datamorph,$DATABASE
+objectClass: olcDatamorphEnum
+
+# value 'value1' corresponds to 'AQ==' (0x01)
+dn: olcDatamorphValue=value1,olcDatamorphAttribute={0}enumeratedAttribute,olcOv
+ erlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 1
+
+# value 'value11' corresponds to 'Cw==' (0x0B)
+dn: olcDatamorphValue=value11,olcDatamorphAttribute={0}enumeratedAttribute,olcO
+ verlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 11
+
+# handle attribute 'signedInteger' as a 2-byte signed integer with values
+# between -20000 and 30000 (inclusive on both sides)
+dn: olcDatamorphAttribute=signedInteger,olcOverlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 2
+olcDatamorphIntegerSigned: TRUE
+olcDatamorphIntegerLowerBound: -20000
+olcDatamorphIntegerUpperBound: 30000
+
+# handle attribute 'shortInteger' as a 1-byte unsigned integer with only values
+# 0 and 1 allowed (effectively a true/false)
+dn: olcDatamorphAttribute=shortInteger,olcOverlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 1
+olcDatamorphIntegerUpperBound: 1
+olcDatamorphIntegerSigned: FALSE
+.fi
+
+The
+.BR slapd.conf (5)
+equivalent of the above follows:
+
+.nf
+overlay datamorph
+
+datamorph enum enumeratedAttribute
+datamorph_value 1 value1
+datamorph_value 11 value11
+
+datamorph int signedInteger
+datamorph_size 2
+datamorph_signed TRUE
+datamorph_lower_bound -20000
+datamorph_upper_bound 30000
+
+datamorph int shortInteger
+datamorph_size 1
+datamorph_signed no
+datamorph_upper_bound 1
+.fi
+
+.SH REPLICATION
+
+Given that there are syntaxes and matching rules provided by the overlay, it
+should be configured on each replica to guarantee consistency.
+
+.SH BUGS AND LIMITATIONS
+Due to the fact that overlays are not active in the
+.BR slapcat (8)
+nor
+.BR slapadd (8)
+processes, backups of the database will be made exactly as stored. This means
+that backups made using
+.BR ldapsearch (1)
+cannot be used by
+.BR slapadd (8)
+nor can backups made using
+.BR slapcat (8)
+be loaded using
+.BR ldapadd (8).
+
+Value based ACLs that involve values of the transformed attributes are not
+supported.
+
+The overlay will refuse operations that add or rename entries with any of the
+configured attributes in their RDN.
+
+No controls are explicitly handled in the overlay, attaching any controls that
+reference configured attributes might lead to unexpected behaviour and is
+therefore discouraged.
+
+Increment modification of the configured attributes is not supported either.
+
+If a transformation is configured to be signed yet the attribute's schema uses the
+unsigned syntax and matching rules, inequality matching will not work as
+intended and will treat negative numbers as higher than positive numbers.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd-config (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR slapd (8),
+.BR slapcat (8),
+.BR slapadd (8)
+.SH ACKNOWLEDGEMENTS
+This module was developed in 2016 by Ondřej Kuzník for Symas Corp.
diff --git a/contrib/slapd-modules/datamorph/tests/Rules.mk b/contrib/slapd-modules/datamorph/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/datamorph/tests/data/config.ldif b/contrib/slapd-modules/datamorph/tests/data/config.ldif
new file mode 100644
index 0000000..91f2e60
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/config.ldif
@@ -0,0 +1,108 @@
+dn: cn=datamorph,cn=schema,cn=config
+changetype: add
+objectClass: olcSchemaConfig
+olcAttributeTypes: ( 1.3.6.1.4.1.4203.666.11.12.123.1
+ NAME 'enumerated'
+ DESC 'Enumerated attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.2 )
+olcAttributeTypes: ( 1.3.6.1.4.1.4203.666.11.12.123.2
+ NAME 'number'
+ DESC 'Integer attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.3 )
+olcAttributeTypes: ( 1.3.6.1.4.1.4203.666.11.12.123.3
+ NAME 'signed'
+ DESC 'Signed integer attribute'
+ EQUALITY fixedSizeSignedIntegerMatch
+ ORDERING fixedSizeSignedIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.4 )
+olcObjectClasses: ( 1.3.6.1.4.1.4203.666.11.12.123.4
+ NAME 'transformedObject'
+ DESC 'Testing objectclass'
+ SUP top AUXILIARY
+ MAY ( enumerated $ number $ signed ) )
+
+dn: olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectclass: olcDatamorphConfig
+
+# a basic enum
+dn: olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnum
+
+dn: olcDatamorphValue=bjensen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 1
+
+dn: olcDatamorphValue=bjorn,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 11
+
+dn: olcDatamorphValue=dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 12
+
+dn: olcDatamorphValue=jaj,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 13
+
+dn: olcDatamorphValue=jjones,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 14
+
+dn: olcDatamorphValue=jdoe,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 10
+
+dn: olcDatamorphValue=jen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 101
+
+dn: olcDatamorphValue=johnd,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 20
+
+dn: olcDatamorphValue=melliot,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 51
+
+dn: olcDatamorphValue=uham,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 31
+
+dn: olcDatamorphValue=\5Cno \22name\22,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 200
+
+# an interval
+dn: olcDatamorphAttribute=signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 2
+olcDatamorphIntegerSigned: TRUE
+olcDatamorphIntegerLowerBound: -20000
+olcDatamorphIntegerUpperBound: 30000
+
+# an number interval (essentially TRUE/FALSE)
+dn: olcDatamorphAttribute=number,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 1
+olcDatamorphIntegerUpperBound: 1
+olcDatamorphIntegerSigned: FALSE
diff --git a/contrib/slapd-modules/datamorph/tests/data/datamorph.conf b/contrib/slapd-modules/datamorph/tests/data/datamorph.conf
new file mode 100644
index 0000000..7cc4899
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/datamorph.conf
@@ -0,0 +1,49 @@
+overlay datamorph
+
+# they depend on the syntaxes defined by the overlay
+attributetype ( 1.3.6.1.4.1.4203.666.11.12.123.1 NAME 'enumerated'
+ DESC 'Enumerated attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.2 )
+
+attributetype ( 1.3.6.1.4.1.4203.666.11.12.123.2 NAME 'number'
+ DESC 'Integer attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.3 )
+
+attributetype ( 1.3.6.1.4.1.4203.666.11.12.123.3 NAME 'signed'
+ DESC 'Signed integer attribute'
+ EQUALITY fixedSizeSignedIntegerMatch
+ ORDERING fixedSizeSignedIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.4 )
+
+objectclass ( 1.3.6.1.4.1.4203.666.11.12.123.4 NAME 'transformedObject'
+ DESC 'Testing objectclass'
+ SUP top AUXILIARY
+ MAY ( enumerated $ number $ signed ) )
+
+datamorph eNuM enumerated
+datamorph_value 1 bjensen
+datamorph_value 11 bjorn
+datamorph_value 12 dots
+datamorph_value "13" jaj
+datamorph_value 14 jjones
+datamorph_value 10 jdoe
+datamorph_value 101 jen
+datamorph_value 20 johnd
+datamorph_value 51 "melliot"
+datamorph_value 31 uham
+datamorph_value 200 "\\no \"name\""
+
+datamorph int signed
+datamorph_size 2
+datamorph_signed TRUE
+datamorph_lower_bound -20000
+datamorph_upper_bound 30000
+
+datamorph iNT number
+datamorph_size 1
+datamorph_signed no
+datamorph_upper_bound 1
diff --git a/contrib/slapd-modules/datamorph/tests/data/test.ldif b/contrib/slapd-modules/datamorph/tests/data/test.ldif
new file mode 100644
index 0000000..67971f3
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test.ldif
@@ -0,0 +1,434 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+signed:: sm4=
+number:: AA==
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupofnames
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+enumerated:: AQ==
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+enumerated:: Cw==
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+enumerated:: DA==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupofuniquenames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+enumerated:: DQ==
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+enumerated:: Dg==
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+enumerated:: Cg==
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+enumerated:: ZQ==
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+enumerated:: FA==
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+enumerated:: Mw==
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+enumerated:: Hw==
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif
new file mode 100644
index 0000000..b9ba88f
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif
@@ -0,0 +1,3 @@
+dn: olcDatamorphAttribute=enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnum
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif
new file mode 100644
index 0000000..1dac5dc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif
@@ -0,0 +1,4 @@
+dn: olcDatamorphValue=nope,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 1
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif
new file mode 100644
index 0000000..f31ab0a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif
@@ -0,0 +1,4 @@
+dn: olcDatamorphValue={0}bjensen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 11
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif
new file mode 100644
index 0000000..01f21c2
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif
@@ -0,0 +1,3 @@
+dn: olcDatamorphAttribute=uid,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnum
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-config.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-config.ldif
new file mode 100644
index 0000000..2aed906
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-config.ldif
@@ -0,0 +1,9 @@
+dn: olcDatamorphValue=gjensen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 55
+
+dn: olcDatamorphAttribute={1}signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+delete: olcDatamorphIntegerUpperBound
+olcDatamorphIntegerUpperBound: 30000
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif
new file mode 100644
index 0000000..0df14c4
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif
@@ -0,0 +1,31 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=
+ com
+changetype: add
+objectClass: testPerson
+objectClass: transformedObject
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postalAddress: ITD $ 535 W. William St $ Anytown, MI 48103
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homePostalAddress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimileTelephonenumber: +1 313 555 7557
+telephoneNumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homePhone: +1 313 555 8844
+testTime: 20050304001801.234Z
+enumerated: gjensen
+
+dn: ou=New Unit,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: New Unit
+uidNumber: 32345
+gidNumber: 1
+signed: 32345
+number: 1
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif
new file mode 100644
index 0000000..f834997
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif
@@ -0,0 +1,23 @@
+dn: uid=bjensen+cn=Barbara Jensen+enumerated=bjensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: add
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif
new file mode 100644
index 0000000..cbcb14a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif
@@ -0,0 +1,5 @@
+dn: ou=New Unit,dc=www+number=1,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: New Unit
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test003-config.ldif b/contrib/slapd-modules/datamorph/tests/data/test003-config.ldif
new file mode 100644
index 0000000..f74717c
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test003-config.ldif
@@ -0,0 +1,30 @@
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 110
+
+dn: olcDatamorphValue={3}jaj,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 12
+
+dn: olcDatamorphValue={4}jjones,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 13
+
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 14
+
+dn: olcDatamorphAttribute={1}signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIntegerSigned
+olcDatamorphIntegerSigned: FALSE
+-
+replace: olcDatamorphIntegerUpperBound
+olcDatamorphIntegerUpperBound: 50000
+-
+replace: olcDatamorphIntegerLowerBound
+olcDatamorphIntegerLowerBound: 50
diff --git a/contrib/slapd-modules/datamorph/tests/data/test003-out.ldif b/contrib/slapd-modules/datamorph/tests/data/test003-out.ldif
new file mode 100644
index 0000000..3dbd8bc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test003-out.ldif
@@ -0,0 +1,125 @@
+# List regular entries
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+
+# List entries with transformed attributes
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+enumerated: johnd
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+enumerated: bjorn
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+enumerated: bjensen
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+enumerated: jjones
+
+
+# Search for transformed attributes listing only those
+dn: ou=People,dc=example,dc=com
+signed: -19858
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+enumerated: bjensen
+
+
+# Search for transformed attributes after reconfiguring mapping
+dn: ou=People,dc=example,dc=com
+signed: 45678
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+enumerated: jaj
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif
new file mode 100644
index 0000000..694aacc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif
@@ -0,0 +1,5 @@
+# invalid enum value
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: enumerated
+enumerated: 2dots
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif
new file mode 100644
index 0000000..1ce687f
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif
@@ -0,0 +1,5 @@
+# enums are case sensitive
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: enumerated
+enumerated: Dots
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif
new file mode 100644
index 0000000..54bb9b5
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif
@@ -0,0 +1,5 @@
+# value does not exist in entry
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+delete: enumerated
+enumerated: uham
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif
new file mode 100644
index 0000000..601d895
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif
@@ -0,0 +1,5 @@
+# value does not exist in entry
+dn: ou=People,dc=example,dc=com
+changetype: modify
+delete: signed
+signed: 2
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif
new file mode 100644
index 0000000..d97effc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif
@@ -0,0 +1,10 @@
+# a value outside the bounds
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: signed
+signed: 2
+-
+replace: number
+number: -1
+-
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif
new file mode 100644
index 0000000..63b3263
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif
@@ -0,0 +1,6 @@
+# a value outside the bounds
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: signed
+signed: 32000
+-
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif
new file mode 100644
index 0000000..17a72da
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif
@@ -0,0 +1,30 @@
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: signed
+signed: -1
+-
+replace: number
+number: 1
+-
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: enumerated
+enumerated: jaj
+-
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+add: enumerated
+enumerated: bjorn
+enumerated: uham
+-
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+delete: enumerated
+enumerated: johnd
+-
+add: enumerated
+enumerated: melliot
+-
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-out.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-out.ldif
new file mode 100644
index 0000000..4c8c360
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-out.ldif
@@ -0,0 +1,212 @@
+# Test1: list entries that should have been changed by ldapmodify
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+signed: -1
+number: 1
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+enumerated: bjensen
+enumerated: bjorn
+enumerated: uham
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+enumerated: bjorn
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+enumerated: jaj
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+enumerated: jaj
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+enumerated: jjones
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+enumerated: jdoe
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+enumerated: jen
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+enumerated: melliot
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+enumerated: melliot
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+enumerated: uham
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test007-config.ldif b/contrib/slapd-modules/datamorph/tests/data/test007-config.ldif
new file mode 100644
index 0000000..3820831
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test007-config.ldif
@@ -0,0 +1,30 @@
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 110
+
+dn: olcDatamorphValue={4}jjones,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 14
+
+dn: olcDatamorphValue={3}jaj,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 13
+
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 12
+
+dn: olcDatamorphAttribute={1}signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIntegerSigned
+olcDatamorphIntegerSigned: TRUE
+-
+replace: olcDatamorphIntegerLowerBound
+olcDatamorphIntegerLowerBound: -20000
+-
+replace: olcDatamorphIntegerUpperBound
+olcDatamorphIntegerUpperBound: 30000
diff --git a/contrib/slapd-modules/datamorph/tests/run b/contrib/slapd-modules/datamorph/tests/run
new file mode 100755
index 0000000..6a38431
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/run
@@ -0,0 +1,229 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+TOPSRCDIR="${SRCDIR-$LDAP_SRC}"
+SRCDIR="${TOPSRCDIR}/tests"
+eval `grep EGREP_CMD= ${LDAP_BUILD}/tests/run`
+eval `$EGREP_CMD -e '^LN_S=' ${LDAP_BUILD}/tests/run`
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+. "${SRCDIR}/scripts/defines.sh"
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+eval `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run`
+export `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run | sed 's/=.*//'`
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+ ndb) INDEXDB=indexdb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA \
+ SRCDIR
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${TOPDIR}/tests/scripts"
+
+export SCRIPTDIR
+
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+if test ! -r ./data; then
+ ${LN_S} ${TOPDIR}/tests/data ./
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ $SCRIPT $*
+ RC=$?
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/all b/contrib/slapd-modules/datamorph/tests/scripts/all
new file mode 100755
index 0000000..d6d6dc7
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/all
@@ -0,0 +1,102 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+ if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/common.sh b/contrib/slapd-modules/datamorph/tests/scripts/common.sh
new file mode 100755
index 0000000..a468732
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/common.sh
@@ -0,0 +1,152 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+OVERLAY_CONFIG=${OVERLAY_CONFIG-data/config.ldif}
+
+mkdir -p $TESTDIR $DBDIR1
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1 for configuration..."
+$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 $SLEEP0
+
+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 ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading test datamorph configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ echo "Configure indexing for transformed attributes..."
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: enumerated pres,eq
+olcDbIndex: number pres,eq
+olcDbIndex: signed pres,eq
+EOMOD
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+else
+ echo "Skipping indexing setup for this database"
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+
+echo "Running slapadd to build slapd database..."
+$SLAPADD -F $TESTDIR/confdir -l data/test.ldif
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -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 $SLEEP0
+
+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 ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test001-config b/contrib/slapd-modules/datamorph/tests/scripts/test001-config
new file mode 100755
index 0000000..c4bfdf0
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test001-config
@@ -0,0 +1,248 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying invalid changes to config (should fail)..."
+for CHANGE in data/test001-*.ldif; do
+ echo "... $CHANGE"
+ . $CONFFILTER $BACKEND $MONITORDB < $CHANGE | \
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 80)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+# We run this search after the changes above and before restart so we can also
+# check the reconfiguration attempts actually had no side effects
+echo "Saving search output before server restart..."
+echo "# search output from dynamically configured server..." >> $SERVER6OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER6OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -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 $SLEEP0
+
+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 ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Testing slapd.conf support..."
+mkdir $TESTDIR/conftest $DBDIR2
+. $CONFFILTER $BACKEND $MONITORDB < $CONFTWO \
+ | sed -e '/^argsfile.*/a\
+moduleload ../datamorph.la' \
+ -e '/database.*monitor/i\
+include data/datamorph.conf' \
+ > $CONF2
+echo "database config" >>$CONF2
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF2
+
+$SLAPADD -f $CONF2 -l data/test.ldif
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "# search output from server running from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT2..."
+kill -HUP $PID
+
+$SLAPD -Tt -f $CONF2 -F $TESTDIR/conftest -d $LVL >> $LOG3 2>&1
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -F $TESTDIR/conftest -h $URI2 -d $LVL >> $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Gathering overlay configuration from both servers..."
+echo "# overlay configuration from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}datamorph,olcDatabase={1}$BACKEND,cn=config" \
+ | sed -e "s/ {[0-9]*}/ /" -e "s/={[0-9]*}/=/g" \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# overlay configuration from server configured from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -D cn=config -H $URI2 -y $CONFIGPWF \
+ -b "olcOverlay={0}datamorph,olcDatabase={1}$BACKEND,cn=config" \
+ | sed -e "s/ {[0-9]*}/ /" -e "s/={[0-9]*}/=/g" \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# We've already filtered out the ordering markers, now sort the entries
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+echo "Comparing filter output..."
+$CMP $SERVER3FLT $SERVER1FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SERVER1OUT $SERVER3OUT
+
+echo "Comparing search output on both servers..."
+echo "# search output from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# search output from server configured from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+$LDIFFILTER -s e < $SERVER2OUT > $SERVER2FLT
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER6OUT > $SERVER6FLT
+echo "Comparing filter output..."
+$CMP $SERVER6FLT $SERVER1FLT > $CMPOUT && \
+$CMP $SERVER6FLT $SERVER2FLT > $CMPOUT && \
+$CMP $SERVER6FLT $SERVER3FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test002-add-delete b/contrib/slapd-modules/datamorph/tests/scripts/test002-add-delete
new file mode 100755
index 0000000..f947d09
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test002-add-delete
@@ -0,0 +1,147 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Adding entries (should fail this time)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -c -f data/test002-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Adding other entries (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-fail.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-transformed-rdn.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+32)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Configuring new value..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test002-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding some of the entries again..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Saving search output..."
+$LDAPSEARCH -H $URI1 -b "$BASEDN" \
+ "(|(cn=Gern Jensen)(ou=New Unit))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Removing entry..."
+$LDAPDELETE -D $MANAGERDN -H $URI1 -w $PASSWD \
+ "cn=Gern Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ "ou=New Unit,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test002-entry.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ae < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s ae < $LDIF | grep -v '^changetype:' > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test003-search b/contrib/slapd-modules/datamorph/tests/scripts/test003-search
new file mode 100755
index 0000000..9afe677
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test003-search
@@ -0,0 +1,106 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 "(|(ou=Groups)(st=*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,$BASEDN" -s one \
+ -H $URI1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(enumerated=bjensen)(&(signed=-19858)(signed<=0)(signed>=-20000)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring transformation definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on the new values..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on the new values..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(enumerated=not a value)(enumerated=jaj)(&(signed=45678)(!(signed>=50000))(signed>=44444)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test004-compare b/contrib/slapd-modules/datamorph/tests/scripts/test004-compare
new file mode 100755
index 0000000..d4b535b
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test004-compare
@@ -0,0 +1,62 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing a regular entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "cn:Mark Elliot" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a transformed enum entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Jane Doe,ou=Alumni Association,ou=People,$BASEDN" \
+ "enumerated:jdoe" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a transformed interval entry..."
+$LDAPCOMPARE -H $URI1 "ou=People,$BASEDN" \
+ "signed:-19858" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test005-modify b/contrib/slapd-modules/datamorph/tests/scripts/test005-modify
new file mode 100755
index 0000000..94cf1c0
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test005-modify
@@ -0,0 +1,89 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Applying invalid changes (should fail)..."
+for CHANGE in data/test005-*fail.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 16|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(objectClass=OpenLDAPperson)(ou=people))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test006-modrdn b/contrib/slapd-modules/datamorph/tests/scripts/test006-modrdn
new file mode 100755
index 0000000..56e2f2a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test006-modrdn
@@ -0,0 +1,52 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Renaming an entry to add new value (should fail)..."
+$LDAPMODRDN -D $MANAGERDN -H $URI1 -w $PASSWD \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "cn=Mark Elliot+enumerated=melliot" \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodrdn should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodrdn failed ($RC)"
+ ;;
+*)
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication b/contrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication
new file mode 100755
index 0000000..5b2ea4d
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication
@@ -0,0 +1,296 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+mkdir $DBDIR4 $TESTDIR/confdir-consumer
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+
+echo "database config" >>$CONF4
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF4
+
+$SLAPD -f $CONF4 -F $TESTDIR/confdir-consumer -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+echo "Setting up overlay on consumer..."
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncprov on provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular replicated entries..."
+echo "# Testing searches against regular replicated entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 "(|(ou=Groups)(st=*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,$BASEDN" \
+ -s one -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=bjensen)(&(signed=-19858)(signed<=0)(signed>=-20000)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring transformation definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on the new replicated values..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on the new replicated values..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=not a value)(enumerated=jaj)(&(signed=45678)(!(signed>=50000))(signed>=44444)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SEARCHOUT
+
+echo "Reverting part of the above configuration for remainder of the test..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Applying invalid changes (should fail)..."
+for CHANGE in data/test005-*fail.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 16|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(objectClass=OpenLDAPperson)(ou=people))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication b/contrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication
new file mode 100755
index 0000000..a1fcb71
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication
@@ -0,0 +1,299 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+mkdir $DBDIR4 $TESTDIR/confdir-consumer
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+
+echo "database config" >>$CONF4
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF4
+
+$SLAPD -f $CONF4 -F $TESTDIR/confdir-consumer -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+echo "Setting up overlay on consumer..."
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncprov on provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular replicated entries..."
+echo "# Testing searches against regular replicated entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 "(|(ou=Groups)(st=*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,$BASEDN" -s one \
+ -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=bjensen)(&(signed=-19858)(signed<=0)(signed>=-20000)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring transformation definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+sed 's/{0}datamorph/{1}datamorph/' | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on the new replicated values..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on the new replicated values..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=not a value)(enumerated=jaj)(&(signed=45678)(!(signed>=50000))(signed>=44444)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SEARCHOUT
+
+echo "Reverting part of the above configuration for remainder of the test..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+sed 's/{0}datamorph/{1}datamorph/' | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Applying invalid changes (should fail)..."
+for CHANGE in data/test005-*fail.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 16|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(objectClass=OpenLDAPperson)(ou=people))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/denyop/Makefile b/contrib/slapd-modules/denyop/Makefile
new file mode 100644
index 0000000..2cea18b
--- /dev/null
+++ b/contrib/slapd-modules/denyop/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_DENYOP=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = denyop.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+denyop.la: denyop.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/denyop/denyop.c b/contrib/slapd-modules/denyop/denyop.c
new file mode 100644
index 0000000..dd3e13c
--- /dev/null
+++ b/contrib/slapd-modules/denyop/denyop.c
@@ -0,0 +1,260 @@
+/* denyop.c - Denies operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DENYOP
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+/* This overlay provides a quick'n'easy way to deny selected operations
+ * for a database whose backend implements the operations. It is intended
+ * to be less expensive than ACLs because its evaluation occurs before
+ * any backend specific operation is actually even initiated.
+ */
+
+enum {
+ denyop_add = 0,
+ denyop_bind,
+ denyop_compare,
+ denyop_delete,
+ denyop_extended,
+ denyop_modify,
+ denyop_modrdn,
+ denyop_search,
+ denyop_unbind
+} denyop_e;
+
+typedef struct denyop_info {
+ int do_op[denyop_unbind + 1];
+} denyop_info;
+
+static int
+denyop_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ denyop_info *oi = (denyop_info *)on->on_bi.bi_private;
+ int deny = 0;
+
+ switch( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ deny = oi->do_op[denyop_bind];
+ break;
+
+ case LDAP_REQ_ADD:
+ deny = oi->do_op[denyop_add];
+ break;
+
+ case LDAP_REQ_DELETE:
+ deny = oi->do_op[denyop_delete];
+ break;
+
+ case LDAP_REQ_MODRDN:
+ deny = oi->do_op[denyop_modrdn];
+ break;
+
+ case LDAP_REQ_MODIFY:
+ deny = oi->do_op[denyop_modify];
+ break;
+
+ case LDAP_REQ_COMPARE:
+ deny = oi->do_op[denyop_compare];
+ break;
+
+ case LDAP_REQ_SEARCH:
+ deny = oi->do_op[denyop_search];
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ deny = oi->do_op[denyop_extended];
+ break;
+
+ case LDAP_REQ_UNBIND:
+ deny = oi->do_op[denyop_unbind];
+ break;
+ }
+
+ if ( !deny ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not allowed within namingContext" );
+
+ return 0;
+}
+
+static int
+denyop_over_init(
+ BackendDB *be, ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ denyop_info *oi;
+
+ oi = (denyop_info *)ch_malloc(sizeof(denyop_info));
+ memset(oi, 0, sizeof(denyop_info));
+ on->on_bi.bi_private = oi;
+
+ return 0;
+}
+
+static int
+denyop_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ denyop_info *oi = (denyop_info *)on->on_bi.bi_private;
+
+ if ( strcasecmp( argv[0], "denyop" ) == 0 ) {
+ char *op;
+
+ if ( argc != 2 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "operation list missing in "
+ "\"denyop <op-list>\" line.\n",
+ fname, lineno );
+ return( 1 );
+ }
+
+ /* The on->on_bi.bi_private pointer can be used for
+ * anything this instance of the overlay needs.
+ */
+
+ op = argv[1];
+ do {
+ char *next = strchr( op, ',' );
+
+ if ( next ) {
+ next[0] = '\0';
+ next++;
+ }
+
+ if ( strcmp( op, "add" ) == 0 ) {
+ oi->do_op[denyop_add] = 1;
+
+ } else if ( strcmp( op, "bind" ) == 0 ) {
+ oi->do_op[denyop_bind] = 1;
+
+ } else if ( strcmp( op, "compare" ) == 0 ) {
+ oi->do_op[denyop_compare] = 1;
+
+ } else if ( strcmp( op, "delete" ) == 0 ) {
+ oi->do_op[denyop_delete] = 1;
+
+ } else if ( strcmp( op, "extended" ) == 0 ) {
+ oi->do_op[denyop_extended] = 1;
+
+ } else if ( strcmp( op, "modify" ) == 0 ) {
+ oi->do_op[denyop_modify] = 1;
+
+ } else if ( strcmp( op, "modrdn" ) == 0 ) {
+ oi->do_op[denyop_modrdn] = 1;
+
+ } else if ( strcmp( op, "search" ) == 0 ) {
+ oi->do_op[denyop_search] = 1;
+
+ } else if ( strcmp( op, "unbind" ) == 0 ) {
+ oi->do_op[denyop_unbind] = 1;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unknown operation \"%s\" at "
+ "\"denyop <op-list>\" line.\n",
+ fname, lineno, op );
+ return( 1 );
+ }
+
+ op = next;
+ } while ( op );
+
+ } else {
+ return SLAP_CONF_UNKNOWN;
+ }
+ return 0;
+}
+
+static int
+denyop_destroy(
+ BackendDB *be, ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ denyop_info *oi = (denyop_info *)on->on_bi.bi_private;
+
+ if ( oi ) {
+ ch_free( oi );
+ }
+
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst denyop;
+
+int
+denyop_initialize( void )
+{
+ memset( &denyop, 0, sizeof( slap_overinst ) );
+ denyop.on_bi.bi_type = "denyop";
+ denyop.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ denyop.on_bi.bi_db_init = denyop_over_init;
+ denyop.on_bi.bi_db_config = denyop_config;
+ denyop.on_bi.bi_db_destroy = denyop_destroy;
+
+ denyop.on_bi.bi_op_bind = denyop_func;
+ denyop.on_bi.bi_op_search = denyop_func;
+ denyop.on_bi.bi_op_compare = denyop_func;
+ denyop.on_bi.bi_op_modify = denyop_func;
+ denyop.on_bi.bi_op_modrdn = denyop_func;
+ denyop.on_bi.bi_op_add = denyop_func;
+ denyop.on_bi.bi_op_delete = denyop_func;
+ denyop.on_bi.bi_extended = denyop_func;
+ denyop.on_bi.bi_op_unbind = denyop_func;
+
+ denyop.on_response = NULL /* denyop_response */ ;
+
+ return overlay_register( &denyop );
+}
+
+#if SLAPD_OVER_DENYOP == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return denyop_initialize();
+}
+#endif /* SLAPD_OVER_DENYOP == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_DENYOP) */
diff --git a/contrib/slapd-modules/dsaschema/Makefile b/contrib/slapd-modules/dsaschema/Makefile
new file mode 100644
index 0000000..3a88fc0
--- /dev/null
+++ b/contrib/slapd-modules/dsaschema/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = dsaschema.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+dsaschema.la: dsaschema.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/dsaschema/README b/contrib/slapd-modules/dsaschema/README
new file mode 100644
index 0000000..fdf932e
--- /dev/null
+++ b/contrib/slapd-modules/dsaschema/README
@@ -0,0 +1,23 @@
+Copyright 2004-2022 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.
+
+This directory contains a native slapd plugin, dsaschema, that permits the
+loading of DSA-specific schema from configuration files (including operational
+attributes).
+
+To use the plugin, add:
+
+moduleload dsaschema.so
+ /etc/openldap/schema/foo1.schema
+ ...etc...
+ /etc/openldap/schema/fooN.schema
+
+to your slapd configuration file.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -Wall -g -o dsaschema.so dsaschema.c
+
diff --git a/contrib/slapd-modules/dsaschema/dsaschema.c b/contrib/slapd-modules/dsaschema/dsaschema.c
new file mode 100644
index 0000000..31defae
--- /dev/null
+++ b/contrib/slapd-modules/dsaschema/dsaschema.c
@@ -0,0 +1,369 @@
+/* dsaschema.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 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>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/errno.h>
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <stdio.h>
+
+/*
+ * Schema reader that allows us to define DSA schema (including
+ * operational attributes and non-user object classes)
+ *
+ * A kludge, at best, and in order to avoid including slapd
+ * headers we use fprintf() rather than slapd's native logging,
+ * which may confuse users...
+ *
+ */
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include <slap.h>
+#include <slap-config.h>
+
+#define ARGS_STEP 512
+
+static char *fp_getline(FILE *fp, int *lineno);
+static void fp_getline_init(int *lineno);
+static int fp_parse_line(int lineno, char *line);
+static char *strtok_quote( char *line, char *sep );
+
+static char **cargv = NULL;
+static int cargv_size = 0;
+static int cargc = 0;
+static char *strtok_quote_ptr;
+
+int init_module(int argc, char *argv[]);
+
+static int dsaschema_parse_cr(const char *fname, int lineno, char *line, char **argv)
+{
+ struct config_args_s c = { .line = line };
+
+ if ( parse_cr( &c, NULL ) ) {
+ Debug( LDAP_DEBUG_ANY, "dsaschema_parse_cr: "
+ "ditcontentrule definition invalid at %s:%d\n",
+ fname, lineno );
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dsaschema_read_config(const char *fname, int depth)
+{
+ FILE *fp;
+ char *line, *savefname, *saveline = NULL;
+ int savelineno, lineno;
+ int rc;
+
+ if (depth == 0) {
+ cargv = ch_calloc(ARGS_STEP + 1, sizeof(*cargv));
+ cargv_size = ARGS_STEP + 1;
+ }
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ char ebuf[128];
+ int saved_errno = errno;
+ fprintf(stderr, "could not open config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)), saved_errno);
+ return 1;
+ }
+ fp_getline_init(&lineno);
+
+ while ((line = fp_getline(fp, &lineno)) != NULL) {
+ /* skip comments and blank lines */
+ if (line[0] == '#' || line[0] == '\0') {
+ continue;
+ }
+
+ saveline = ch_strdup(line);
+
+ if (fp_parse_line(lineno, line) != 0) {
+ rc = 1;
+ break;
+ }
+
+ if (cargc < 1) {
+ continue;
+ }
+
+ if (strcasecmp(cargv[0], "attributetype") == 0 ||
+ strcasecmp(cargv[0], "attribute") == 0) {
+ if (cargc < 2) {
+ fprintf(stderr, "%s: line %d: illegal attribute type format\n",
+ fname, lineno);
+ rc = 1;
+ break;
+ } else if (*cargv[1] == '(' /*')'*/) {
+ char *p;
+
+ p = strchr(saveline, '(' /*')'*/);
+ rc = register_at(p, NULL, 0);
+ if (rc != 0) {
+ Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
+ "attribute definition invalid at %s:%d\n",
+ fname, lineno );
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s: line %d: old attribute type format not supported\n",
+ fname, lineno);
+ }
+ } else if (strcasecmp(cargv[0], "ditcontentrule") == 0) {
+ char *p;
+ p = strchr(saveline, '(' /*')'*/);
+ rc = dsaschema_parse_cr(fname, lineno, p, cargv);
+ if (rc != 0)
+ break;
+ } else if (strcasecmp(cargv[0], "objectclass") == 0) {
+ if (cargc < 2) {
+ fprintf(stderr, "%s: line %d: illegal objectclass format\n",
+ fname, lineno);
+ rc = 1;
+ break;
+ } else if (*cargv[1] == '(' /*')'*/) {
+ char *p;
+
+ p = strchr(saveline, '(' /*')'*/);
+ rc = register_oc(p, NULL, 0);
+ if (rc != 0) {
+ Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
+ "objectclass definition invalid at %s:%d\n",
+ fname, lineno );
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s: line %d: object class format not supported\n",
+ fname, lineno);
+ }
+ } else if (strcasecmp(cargv[0], "include") == 0) {
+ if (cargc < 2) {
+ fprintf(stderr, "%s: line %d: missing file name in \"include <filename>\" line",
+ fname, lineno);
+ rc = 1;
+ break;
+ }
+ savelineno = lineno;
+ savefname = ch_strdup(cargv[1]);
+
+ rc = dsaschema_read_config(savefname, depth + 1);
+ ch_free(savefname);
+ lineno = savelineno - 1;
+ if (rc != 0) {
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s: line %d: unknown directive \"%s\" (ignored)\n",
+ fname, lineno, cargv[0]);
+ }
+
+ ch_free(saveline);
+ saveline = NULL;
+ }
+
+ fclose(fp);
+
+ if (depth == 0)
+ ch_free(cargv);
+
+ if (saveline != NULL)
+ ch_free(saveline);
+
+ return rc;
+}
+
+int init_module(int argc, char *argv[])
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < argc; i++) {
+ rc = dsaschema_read_config(argv[i], 0);
+ if (rc != 0) {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+fp_parse_line(
+ int lineno,
+ char *line
+)
+{
+ char * token;
+
+ cargc = 0;
+ token = strtok_quote( line, " \t" );
+
+ if ( strtok_quote_ptr ) {
+ *strtok_quote_ptr = ' ';
+ }
+
+ if ( strtok_quote_ptr ) {
+ *strtok_quote_ptr = '\0';
+ }
+
+ for ( ; token != NULL; token = strtok_quote( NULL, " \t" ) ) {
+ if ( cargc == cargv_size - 1 ) {
+ char **tmp;
+ tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
+ sizeof(*cargv) );
+ cargv = tmp;
+ cargv_size += ARGS_STEP;
+ }
+ cargv[cargc++] = token;
+ }
+ cargv[cargc] = NULL;
+ return 0;
+}
+
+static char *
+strtok_quote( char *line, char *sep )
+{
+ int inquote;
+ char *tmp;
+ static char *next;
+
+ strtok_quote_ptr = NULL;
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return( NULL );
+ }
+ tmp = next;
+
+ for ( inquote = 0; *next; ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ break;
+
+ case '\\':
+ if ( next[1] )
+ AC_MEMCPY( next,
+ next + 1, strlen( next + 1 ) + 1 );
+ next++; /* dont parse the escaped character */
+ break;
+
+ default:
+ if ( ! inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ strtok_quote_ptr = next;
+ *next++ = '\0';
+ return( tmp );
+ }
+ }
+ next++;
+ break;
+ }
+ }
+
+ return( tmp );
+}
+
+static char buf[BUFSIZ];
+static char *line;
+static size_t lmax, lcur;
+
+#define CATLINE( buf ) \
+ do { \
+ size_t len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += BUFSIZ; \
+ line = (char *) ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+ } while( 0 )
+
+static char *
+fp_getline( FILE *fp, int *lineno )
+{
+ char *p;
+
+ lcur = 0;
+ CATLINE( buf );
+ (*lineno)++;
+
+ /* hack attack - keeps us from having to keep a stack of bufs... */
+ if ( strncasecmp( line, "include", 7 ) == 0 ) {
+ buf[0] = '\0';
+ return( line );
+ }
+
+ while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
+ /* trim off \r\n or \n */
+ if ( (p = strchr( buf, '\n' )) != NULL ) {
+ if( p > buf && p[-1] == '\r' ) --p;
+ *p = '\0';
+ }
+
+ /* trim off trailing \ and append the next line */
+ if ( line[ 0 ] != '\0'
+ && (p = line + strlen( line ) - 1)[ 0 ] == '\\'
+ && p[ -1 ] != '\\' ) {
+ p[ 0 ] = '\0';
+ lcur--;
+
+ } else {
+ if ( ! isspace( (unsigned char) buf[0] ) ) {
+ return( line );
+ }
+
+ /* change leading whitespace to a space */
+ buf[0] = ' ';
+ }
+
+ CATLINE( buf );
+ (*lineno)++;
+ }
+ buf[0] = '\0';
+
+ return( line[0] ? line : NULL );
+}
+
+static void
+fp_getline_init( int *lineno )
+{
+ *lineno = -1;
+ buf[0] = '\0';
+}
+
diff --git a/contrib/slapd-modules/dupent/Makefile b/contrib/slapd-modules/dupent/Makefile
new file mode 100644
index 0000000..6b35438
--- /dev/null
+++ b/contrib/slapd-modules/dupent/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_DUPENT=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = dupent.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+dupent.la: dupent.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/dupent/dupent.c b/contrib/slapd-modules/dupent/dupent.c
new file mode 100644
index 0000000..89ad622
--- /dev/null
+++ b/contrib/slapd-modules/dupent/dupent.c
@@ -0,0 +1,558 @@
+/* dupent.c - LDAP Control for a Duplicate Entry Representation of Search Results */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAP Control for a Duplicate Entry Representation of Search Results
+ * <draft-ietf-ldapext-ldapv3-dupent-08.txt> (EXPIRED)
+ * <http://tools.ietf.org/id/draft-ietf-ldapext-ldapv3-dupent-08.txt>
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_DUPENT=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_DUPENT
+
+/*
+ * The macros
+ *
+ * LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1"
+ * LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2"
+ * LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3"
+ *
+ * are already defined in <ldap.h>
+ */
+
+/*
+ * support for no attrs and "*" in AttributeDescriptionList is missing
+ */
+
+#include "slap.h"
+#include "ac/string.h"
+
+#define o_dupent o_ctrlflag[dupent_cid]
+#define o_ctrldupent o_controls[dupent_cid]
+
+static int dupent_cid;
+static slap_overinst dupent;
+
+typedef struct dupent_t {
+ AttributeName *ds_an;
+ ber_len_t ds_nattrs;
+ slap_mask_t ds_flags;
+ ber_int_t ds_paa;
+} dupent_t;
+
+static int
+dupent_parseCtrl (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_len_t len;
+ BerVarray AttributeDescriptionList = NULL;
+ ber_len_t cnt = sizeof(struct berval);
+ ber_len_t off = 0;
+ ber_int_t PartialApplicationAllowed = 1;
+ dupent_t *ds = NULL;
+ int i;
+
+ if ( op->o_dupent != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Dupent control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Dupent control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Dupent control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+ /*
+
+ DuplicateEntryRequest ::= SEQUENCE {
+ AttributeDescriptionList, -- from [RFC2251]
+ PartialApplicationAllowed BOOLEAN DEFAULT TRUE }
+
+ AttributeDescriptionList ::= SEQUENCE OF
+ AttributeDescription
+
+ AttributeDescription ::= LDAPString
+
+ attributeDescription = AttributeType [ ";" <options> ]
+
+ */
+
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ if ( ber_scanf( ber, "{M}", &AttributeDescriptionList, &cnt, off )
+ == LBER_ERROR )
+ {
+ rs->sr_text = "Dupent control: dupentSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == LBER_BOOLEAN ) {
+ /* NOTE: PartialApplicationAllowed is ignored, since the control
+ * can always be honored
+ */
+ if ( ber_scanf( ber, "b", &PartialApplicationAllowed ) == LBER_ERROR )
+ {
+ rs->sr_text = "Dupent control: dupentSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ tag = ber_skip_tag( ber, &len );
+ }
+ if ( len || tag != LBER_DEFAULT ) {
+ rs->sr_text = "Dupent control: dupentSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ ds = (dupent_t *)op->o_tmpcalloc( 1,
+ sizeof(dupent_t) + sizeof(AttributeName)*cnt,
+ op->o_tmpmemctx );
+
+ ds->ds_paa = PartialApplicationAllowed;
+
+ if ( cnt == 0 ) {
+ ds->ds_flags |= SLAP_USERATTRS_YES;
+
+ } else {
+ int c;
+
+ ds->ds_an = (AttributeName *)&ds[ 1 ];
+
+ for ( i = 0, c = 0; i < cnt; i++ ) {
+ const char *text;
+ int j;
+ int rc;
+ AttributeDescription *ad = NULL;
+
+ if ( bvmatch( &AttributeDescriptionList[i],
+ slap_bv_all_user_attrs ) )
+ {
+ if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
+ rs->sr_text = "Dupent control: AttributeDescription decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ ds->ds_flags |= SLAP_USERATTRS_YES;
+ continue;
+ }
+
+ rc = slap_bv2ad( &AttributeDescriptionList[i], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ ds->ds_an[c].an_desc = ad;
+ ds->ds_an[c].an_name = ad->ad_cname;
+
+ /* FIXME: not specified; consider this an error, just in case */
+ for ( j = 0; j < c; j++ ) {
+ if ( ds->ds_an[c].an_desc == ds->ds_an[j].an_desc ) {
+ rs->sr_text = "Dupent control: AttributeDescription must be unique within AttributeDescriptionList";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ c++;
+ }
+
+ ds->ds_nattrs = c;
+
+ if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
+ /* purge user attrs */
+ for ( i = 0; i < ds->ds_nattrs; ) {
+ if ( is_at_operational( ds->ds_an[i].an_desc->ad_type ) ) {
+ i++;
+ continue;
+ }
+
+ ds->ds_nattrs--;
+ if ( i < ds->ds_nattrs ) {
+ ds->ds_an[i] = ds->ds_an[ds->ds_nattrs];
+ }
+ }
+ }
+ }
+
+ op->o_ctrldupent = (void *)ds;
+
+ op->o_dupent = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ op->o_tmpfree( ds, op->o_tmpmemctx );
+ }
+
+ if ( AttributeDescriptionList != NULL ) {
+ ber_memfree_x( AttributeDescriptionList, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+typedef struct dupent_cb_t {
+ slap_overinst *dc_on;
+ dupent_t *dc_ds;
+ int dc_skip;
+} dupent_cb_t;
+
+typedef struct valnum_t {
+ Attribute *ap;
+ Attribute a;
+ struct berval vals[2];
+ struct berval nvals[2];
+ int cnt;
+} valnum_t;
+
+static int
+dupent_response_done( Operation *op, SlapReply *rs )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ struct berval ctrlval;
+ LDAPControl *ctrl, *ctrlsp[2];
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ /*
+
+ DuplicateEntryResponseDone ::= SEQUENCE {
+ resultCode, -- From [RFC2251]
+ errorMessage [0] LDAPString OPTIONAL,
+ attribute [1] AttributeDescription OPTIONAL }
+
+ */
+
+ ber_printf( ber, "{i}", rs->sr_err );
+ if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+ ber_free_buf( ber );
+ if ( op->o_dupent == SLAP_CONTROL_CRITICAL ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ ctrl = op->o_tmpcalloc( 1,
+ sizeof( LDAPControl ) + ctrlval.bv_len + 1,
+ op->o_tmpmemctx );
+ ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
+ ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_len = ctrlval.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
+ ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
+
+ ber_free_buf( ber );
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dupent_response_entry_1level(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ valnum_t *valnum,
+ int nattrs,
+ int level )
+{
+ int i, rc = LDAP_SUCCESS;
+
+ for ( i = 0; i < valnum[level].ap->a_numvals; i++ ) {
+ LDAPControl *ctrl = NULL, *ctrlsp[2];
+
+ valnum[level].a.a_vals[0] = valnum[level].ap->a_vals[i];
+ if ( valnum[level].ap->a_nvals != valnum[level].ap->a_vals ) {
+ valnum[level].a.a_nvals[0] = valnum[level].ap->a_nvals[i];
+ }
+
+ if ( level < nattrs - 1 ) {
+ rc = dupent_response_entry_1level( op, rs,
+ e, valnum, nattrs, level + 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+
+ continue;
+ }
+
+ /* NOTE: add the control all times, under the assumption
+ * send_search_entry() honors the REP_CTRLS_MUSTBEFREED
+ * set by slap_add_ctrls(); this is not true (ITS#6629)
+ */
+ ctrl = op->o_tmpcalloc( 1, sizeof( LDAPControl ), op->o_tmpmemctx );
+ ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY;
+ ctrl->ldctl_iscritical = 0;
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+
+ /* do the real send */
+ rs->sr_entry = e;
+ rc = send_search_entry( op, rs );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static void
+dupent_attr_prepare( dupent_t *ds, Entry *e, valnum_t *valnum, int nattrs, int c, Attribute **app, Attribute **ap_listp )
+{
+ valnum[c].ap = *app;
+ *app = (*app)->a_next;
+
+ valnum[c].ap->a_next = *ap_listp;
+ *ap_listp = valnum[c].ap;
+
+ valnum[c].a = *valnum[c].ap;
+ if ( c < nattrs - 1 ) {
+ valnum[c].a.a_next = &valnum[c + 1].a;
+ } else {
+ valnum[c].a.a_next = NULL;
+ }
+ valnum[c].a.a_numvals = 1;
+ valnum[c].a.a_vals = valnum[c].vals;
+ BER_BVZERO( &valnum[c].vals[1] );
+ if ( valnum[c].ap->a_nvals != valnum[c].ap->a_vals ) {
+ valnum[c].a.a_nvals = valnum[c].nvals;
+ BER_BVZERO( &valnum[c].nvals[1] );
+ } else {
+ valnum[c].a.a_nvals = valnum[c].a.a_vals;
+ }
+}
+
+static int
+dupent_response_entry( Operation *op, SlapReply *rs )
+{
+ dupent_cb_t *dc = (dupent_cb_t *)op->o_callback->sc_private;
+ int nattrs = 0;
+ valnum_t *valnum = NULL;
+ Attribute **app, *ap_list = NULL;
+ int i, c;
+ Entry *e = NULL;
+ int rc;
+
+ assert( rs->sr_type == REP_SEARCH );
+
+ for ( i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
+ Attribute *ap;
+
+ ap = attr_find( rs->sr_entry->e_attrs,
+ dc->dc_ds->ds_an[ i ].an_desc );
+ if ( ap && ap->a_numvals > 1 ) {
+ nattrs++;
+ }
+ }
+
+ if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
+ Attribute *ap;
+
+ for ( ap = rs->sr_entry->e_attrs; ap != NULL; ap = ap->a_next ) {
+ if ( !is_at_operational( ap->a_desc->ad_type ) && ap->a_numvals > 1 ) {
+ nattrs++;
+ }
+ }
+ }
+
+ if ( !nattrs ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rs_entry2modifiable( op, rs, dc->dc_on );
+ rs->sr_flags &= ~(REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED);
+ e = rs->sr_entry;
+
+ valnum = op->o_tmpcalloc( sizeof(valnum_t), nattrs, op->o_tmpmemctx );
+
+ for ( c = 0, i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
+ for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
+ if ( (*app)->a_desc == dc->dc_ds->ds_an[ i ].an_desc ) {
+ break;
+ }
+ }
+
+ if ( *app != NULL && (*app)->a_numvals > 1 ) {
+ assert( c < nattrs );
+ dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
+ c++;
+ }
+ }
+
+ if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
+ for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
+ if ( !is_at_operational( (*app)->a_desc->ad_type ) && (*app)->a_numvals > 1 ) {
+ assert( c < nattrs );
+ dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
+ c++;
+ }
+ }
+ }
+
+ for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next )
+ /* goto tail */ ;
+
+ *app = &valnum[0].a;
+
+ /* NOTE: since send_search_entry() does not honor the
+ * REP_CTRLS_MUSTBEFREED flag set by slap_add_ctrls(),
+ * the control could be added here once for all (ITS#6629)
+ */
+
+ dc->dc_skip = 1;
+ rc = dupent_response_entry_1level( op, rs, e, valnum, nattrs, 0 );
+ dc->dc_skip = 0;
+
+ *app = ap_list;
+
+ entry_free( e );
+
+ op->o_tmpfree( valnum, op->o_tmpmemctx );
+
+ return rc;
+}
+
+static int
+dupent_response( Operation *op, SlapReply *rs )
+{
+ dupent_cb_t *dc = (dupent_cb_t *)op->o_callback->sc_private;
+
+ if ( dc->dc_skip ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ switch ( rs->sr_type ) {
+ case REP_RESULT:
+ return dupent_response_done( op, rs );
+
+ case REP_SEARCH:
+ return dupent_response_entry( op, rs );
+
+ case REP_SEARCHREF:
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dupent_cleanup( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ op->o_tmpfree( op->o_ctrldupent, op->o_tmpmemctx );
+ op->o_ctrldupent = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dupent_op_search( Operation *op, SlapReply *rs )
+{
+ if ( op->o_dupent != SLAP_CONTROL_NONE ) {
+ slap_callback *sc;
+ dupent_cb_t *dc;
+
+ sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( dupent_cb_t ), op->o_tmpmemctx );
+
+ dc = (dupent_cb_t *)&sc[ 1 ];
+ dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
+ dc->dc_ds = (dupent_t *)op->o_ctrldupent;
+ dc->dc_skip = 0;
+
+ sc->sc_response = dupent_response;
+ sc->sc_cleanup = dupent_cleanup;
+ sc->sc_private = (void *)dc;
+
+ sc->sc_next = op->o_callback->sc_next;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+#if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
+int
+dupent_initialize( void )
+{
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_DUPENT,
+ SLAP_CTRL_SEARCH, NULL,
+ dupent_parseCtrl, &dupent_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "dupent_initialize: Failed to register control (%d)\n",
+ rc );
+ return -1;
+ }
+
+ dupent.on_bi.bi_type = "dupent";
+
+ dupent.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ dupent.on_bi.bi_op_search = dupent_op_search;
+
+ return overlay_register( &dupent );
+}
+
+#if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return dupent_initialize();
+}
+#endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_DUPENT */
diff --git a/contrib/slapd-modules/emptyds/Makefile b/contrib/slapd-modules/emptyds/Makefile
new file mode 100644
index 0000000..654f856
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/Makefile
@@ -0,0 +1,78 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+#
+# 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_EDS=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = emptyds.la
+MANPAGES = slapo-emptyds.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+emptyds.la: emptyds.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/emptyds/README b/contrib/slapd-modules/emptyds/README
new file mode 100644
index 0000000..914d4e7
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/README
@@ -0,0 +1,66 @@
+emptyds Overlay README
+
+DESCRIPTION
+ This package contains an OpenLDAP overlay called "emptyds" (empty
+ directory string) that eliminates empty values of type directory string
+ (OID 1.3.6.1.4.1.1466.115.121.1.15) from the list of the values in the
+ following manner:
+
+ - add: All empty attribute values will be removed before the add request
+ is executed
+ - mod-replace: A replace with empty values will be modified to a replace
+ without values. As result the attribute will be deleted
+ - mod-add: All empty attribute values will be removed before the mod-add
+ request is executed
+ - mod-delete: All empty attribute values will be removed before the
+ mod-delete request is executed
+
+ If removing all empty values from a modification makes it a no-op, that
+ modification is removed from the list.
+
+ At module load time the emptyds overlay manipulates the syntax checking
+ so that it intercepts the syntax check and allows empty values for
+ attributes of type directory string only. Non-empty values continue to
+ go through the normal check routines. It is therefore very important to
+ configure the overlays in a way that ensures that the emptyds overlay gets
+ the control over the operation before any other overlay. Otherwise it
+ could come to the situation with empty attribute values in the data base.
+
+ David Hawes' addpartial overlay has been used as starting point for this
+ overlay.
+
+BUILDING
+ A Makefile is included, please set your LDAP_SRC directory properly.
+
+INSTALLATION
+ After compiling the emptyds overlay, add the following to your
+ slapd.conf:
+
+ ### slapd.conf
+ ...
+ moduleload emptyds.la
+ ...
+ overlay emptyds
+ ...
+ # before database directive...
+ # this overlay must be the last overlay in the config file to ensure that
+ # requests are modified before other overlays get them.
+ ...
+ ### end slapd.conf
+
+CAVEATS
+ - In order to ensure that emptyds does what it needs to do, it must be
+ the last overlay configured so it will run before the other overlays.
+
+---
+Copyright 2014-2022 The OpenLDAP Foundation.
+Portions Copyright (C) DAASI International GmbH, Tamim Ziai.
+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 file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+http://www.OpenLDAP.org/license.html.
diff --git a/contrib/slapd-modules/emptyds/emptyds.c b/contrib/slapd-modules/emptyds/emptyds.c
new file mode 100644
index 0000000..bb3202e
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/emptyds.c
@@ -0,0 +1,325 @@
+/* emptyds.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2014-2022 The OpenLDAP Foundation.
+ * Portions Copyright (C) 2014 DAASI International GmbH, Tamim Ziai.
+ * Portions Copyright (C) 2022 Ondřej Kuzník, Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * http://www.OpenLDAP.org/license.html.
+ */
+/* ACKNOLEDGEDMENTS:
+ * This work was initially developed by Tamim Ziai of DAASI International GmbH
+ * for inclusion in OpenLDAP Software.
+ */
+/* slapo-emptyds
+ *
+ * This is an OpenLDAP overlay that accepts empty strings as attribute values
+ * without syntax violation but never actually stores them. This allows
+ * applications that used to work with LDAP implementations allowing empty
+ * strings (such as Novel eDirectory) to continue to work with OpenLDAP without
+ * any modifications. Add and modify change types will be proceeded as follows,
+ * other operations will be forwarded without modifications:
+ *
+ * changeType: add changeType: add
+ * sn: <empty> --> sn: blah
+ * sn: blah
+ *
+ * changeType: modify changeType: modify
+ * add: sn --> add: sn
+ * sn: <empty> sn: blah
+ * sn: blah
+ *
+ * changeType: modify changeType: modify
+ * delete: sn --> delete: sn
+ * sn: <empty> sn: blah
+ * sn: blah
+ *
+ * changeType: modify changeType: modify
+ * replace: sn --> replace: sn
+ * sn: <empty>
+ *
+ */
+
+#include "portable.h"
+#include "slap.h"
+
+static slap_overinst emptyds;
+
+static const char ds_oid[] = "1.3.6.1.4.1.1466.115.121.1.15";
+
+static slap_syntax_validate_func *ssyn_validate_original = NULL;
+static slap_syntax_transform_func *ssyn_pretty_original = NULL;
+static int emptyds_instances = 0;
+
+static unsigned int
+remove_empty_values( Modification *m, Attribute *a )
+{
+ BerVarray vals = m ? m->sm_values : a->a_vals,
+ nvals = m ? m->sm_nvalues : a->a_nvals;
+ unsigned int i, j, numvals = m ? m->sm_numvals : a->a_numvals;
+
+ for ( i = 0; i < numvals && !BER_BVISEMPTY( &vals[i] ); i++ )
+ /* Find first empty */;
+
+ if ( i == numvals ) return i;
+
+ /*
+ * We have an empty value at index i, move all of them to the end of the
+ * list, preserving the order of non-empty values.
+ */
+ j = i + 1;
+ for ( j = i + 1; j < numvals; j++ ) {
+ struct berval tmp;
+
+ if ( BER_BVISEMPTY( &vals[j] ) ) continue;
+
+ tmp = vals[i];
+ vals[i] = vals[j];
+ vals[j] = tmp;
+
+ if ( nvals && vals != nvals ) {
+ tmp = nvals[i];
+ nvals[i] = nvals[j];
+ nvals[j] = tmp;
+ }
+
+ if ( m && a && m->sm_values != a->a_vals ) {
+ tmp = a->a_vals[i];
+ a->a_vals[i] = a->a_vals[j];
+ a->a_vals[j] = tmp;
+
+ if ( a->a_nvals && a->a_vals != a->a_nvals ) {
+ tmp = a->a_nvals[i];
+ a->a_nvals[i] = a->a_nvals[j];
+ a->a_nvals[j] = tmp;
+ }
+ }
+ i++;
+ }
+
+ /* Free empty vals */
+ for ( ; j && i < j--; ) {
+ ber_memfree( vals[j].bv_val );
+ if ( nvals && vals != nvals ) {
+ ber_memfree( nvals[j].bv_val );
+ BER_BVZERO( &nvals[j] );
+ }
+
+ if ( m && a && m->sm_values != a->a_vals ) {
+ if ( m->sm_values[j].bv_val != a->a_vals[j].bv_val ) {
+ ber_memfree( a->a_vals[j].bv_val );
+ BER_BVZERO( &a->a_vals[j] );
+
+ if ( a->a_nvals && a->a_vals != a->a_nvals ) {
+ ber_memfree( a->a_nvals[j].bv_val );
+ BER_BVZERO( &a->a_nvals[j] );
+ }
+ }
+ }
+ BER_BVZERO( &vals[j] );
+ }
+
+ return i;
+}
+
+/**
+ * Remove all operations with empty strings.
+ */
+static int
+emptyds_op_add( Operation *op, SlapReply *rs )
+{
+ Attribute **ap, **nexta, *a;
+ Modifications **mlp, **nextp = NULL, *ml;
+ Entry *e = op->ora_e;
+
+ /*
+ * op->ora_modlist can be NULL, at least accesslog doesn't always populate
+ * it on an add.
+ */
+ for ( ap = &e->e_attrs, a = e->e_attrs, mlp = &op->ora_modlist,
+ ml = op->ora_modlist;
+ a != NULL;
+ ap = nexta, a = *ap, mlp = nextp, ml = ml ? *mlp : NULL ) {
+ AttributeType *at = a->a_desc->ad_type;
+ unsigned int remaining;
+
+ nexta = &a->a_next;
+ if ( ml ) {
+ nextp = &ml->sml_next;
+ }
+
+ if ( at->sat_syntax != slap_schema.si_syn_directoryString ||
+ at->sat_atype.at_usage != LDAP_SCHEMA_USER_APPLICATIONS )
+ continue;
+
+ remaining = remove_empty_values( &ml->sml_mod, a );
+ if ( remaining == a->a_numvals ) continue;
+ /* Empty values found */
+
+ if ( !remaining ) {
+ /* All values are empty */
+ *ap = a->a_next;
+ a->a_next = NULL;
+ nexta = ap;
+
+ if ( ml ) {
+ *mlp = ml->sml_next;
+ ml->sml_next = NULL;
+ nextp = mlp;
+ /* Values are generally shared with attribute */
+ slap_mods_free( ml, ml->sml_values != a->a_vals );
+ }
+ attr_free( a );
+ } else {
+ a->a_numvals = remaining;
+ if ( ml ) {
+ ml->sml_mod.sm_numvals = remaining;
+ }
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+emptyds_op_modify( Operation *op, SlapReply *rs )
+{
+ Modifications **mlp, **nextp, *ml;
+
+ for ( mlp = &op->orm_modlist, ml = op->orm_modlist; ml != NULL;
+ mlp = nextp, ml = *mlp ) {
+ AttributeType *at = ml->sml_desc->ad_type;
+ unsigned int remaining;
+
+ nextp = &ml->sml_next;
+
+ if ( at->sat_syntax != slap_schema.si_syn_directoryString ||
+ at->sat_atype.at_usage != LDAP_SCHEMA_USER_APPLICATIONS )
+ continue;
+
+ remaining = remove_empty_values( &ml->sml_mod, NULL );
+ if ( remaining == ml->sml_numvals ) continue;
+
+ if ( !remaining ) {
+ /* All values are empty */
+ if ( ml->sml_op == LDAP_MOD_REPLACE ) {
+ /* Replace is kept */
+ if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
+ ber_bvarray_free( ml->sml_nvalues );
+ }
+ if ( ml->sml_values ) {
+ ber_bvarray_free( ml->sml_values );
+ }
+
+ ml->sml_numvals = 0;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ } else {
+ /* Remove modification */
+ *mlp = ml->sml_next;
+ ml->sml_next = NULL;
+ nextp = mlp;
+ slap_mods_free( ml, 1 );
+ }
+ } else {
+ ml->sml_numvals = remaining;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+emptyds_ssyn_validate( Syntax *syntax, struct berval *in )
+{
+ if ( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
+ return LDAP_SUCCESS;
+ }
+ return ssyn_validate_original( syntax, in );
+}
+
+static int
+emptyds_ssyn_pretty( Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *memctx )
+{
+ if ( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
+ return LDAP_SUCCESS;
+ }
+ return ssyn_pretty_original( syntax, in, out, memctx );
+}
+
+static int
+emptyds_db_init( BackendDB *be, ConfigReply *cr )
+{
+ Syntax *syntax = syn_find( ds_oid );
+
+ if ( syntax == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "emptyds_db_init: "
+ "Syntax %s not found\n",
+ ds_oid );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "emptyds_db_init: "
+ "Found syntax: %s\n",
+ syntax->ssyn_bvoid.bv_val );
+ if ( ssyn_validate_original == NULL && syntax->ssyn_validate != NULL ) {
+ ssyn_validate_original = syntax->ssyn_validate;
+ syntax->ssyn_validate = emptyds_ssyn_validate;
+ }
+ if ( ssyn_pretty_original == NULL && syntax->ssyn_pretty != NULL ) {
+ ssyn_pretty_original = syntax->ssyn_pretty;
+ syntax->ssyn_pretty = &emptyds_ssyn_pretty;
+ }
+ }
+
+ emptyds_instances++;
+ return LDAP_SUCCESS;
+}
+
+static int
+emptyds_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ Syntax *syntax = syn_find( ds_oid );
+
+ if ( --emptyds_instances == 0 && syntax != NULL ) {
+ if ( syntax->ssyn_validate == emptyds_ssyn_validate ) {
+ syntax->ssyn_validate = ssyn_validate_original;
+ }
+ ssyn_validate_original = NULL;
+
+ if ( syntax->ssyn_pretty == emptyds_ssyn_pretty ) {
+ syntax->ssyn_pretty = ssyn_pretty_original;
+ }
+ ssyn_pretty_original = NULL;
+ }
+
+ assert( emptyds_instances >= 0 );
+ return LDAP_SUCCESS;
+}
+
+int
+emptyds_init()
+{
+ emptyds.on_bi.bi_type = "emptyds";
+ emptyds.on_bi.bi_op_add = emptyds_op_add;
+ emptyds.on_bi.bi_op_modify = emptyds_op_modify;
+ emptyds.on_bi.bi_db_init = emptyds_db_init;
+ emptyds.on_bi.bi_db_destroy = emptyds_db_destroy;
+
+ return overlay_register( &emptyds );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return emptyds_init();
+}
diff --git a/contrib/slapd-modules/emptyds/slapo-emptyds.5 b/contrib/slapd-modules/emptyds/slapo-emptyds.5
new file mode 100644
index 0000000..75b1059
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/slapo-emptyds.5
@@ -0,0 +1,68 @@
+.TH SLAPO-EDS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copyright 2018 Tamim Ziai
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-emptyds \- Remove Empty values from Directory String attributes
+Overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+Some non-conformant clients will provide empty values for Directory String
+attributes with certain operations. This overlay makes empty values acceptable
+for the Directory String syntax and will adjust all operations to make sure
+these values are never actually stored in the database.
+.LP
+.nf
+.ft tt
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: add changeType: add
+ sn: <empty> --> sn: blah
+ sn: blah
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ add: sn --> add: sn
+ sn: <empty> sn: blah
+ sn: blah
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ delete: sn --> delete: sn
+ sn: <empty> sn: blah
+ sn: blah
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ replace: sn --> replace: sn
+ sn: <empty>
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ replace: sn --> replace: sn
+ sn: <empty> sn: blah
+ sn: blah
+.ft
+.fi
+.LP
+.SH CONFIGURATION
+This overlay has no specific configuration, however in order to ensure that it
+does what it needs to do, it should be the last overlay configured so it will
+run before the other overlays.
+.SH EXAMPLES
+.LP
+.RS
+.nf
+overlay emptyds
+.RE
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
+.SH ACKNOWLEDGEMENTS
+This module was written in 2014 by Tamim Ziai for DAASI International and
+updated in 2022 by Ondřej Kuzník for inclusion in the OpenLDAP project.
+.so ../Project
diff --git a/contrib/slapd-modules/emptyds/tests/Rules.mk b/contrib/slapd-modules/emptyds/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/emptyds/tests/data/emptyds.conf b/contrib/slapd-modules/emptyds/tests/data/emptyds.conf
new file mode 100644
index 0000000..221fe81
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/data/emptyds.conf
@@ -0,0 +1,54 @@
+# basic slapd config -- for testing of slapo-emptyds
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#accesslogmod#modulepath ../servers/slapd/overlays/
+#accesslogmod#moduleload accesslog.la
+moduleload ../emptyds.la
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+
+overlay accesslog
+logdb cn=log
+logops writes
+logsuccess true
+
+overlay emptyds
+
+database @BACKEND@
+suffix "cn=log"
+rootdn "cn=Manager,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.b
+
+## This one makes no difference except we want to make sure we can
+## safely instantiate the overlay on multiple databases
+overlay emptyds
+
+database monitor
diff --git a/contrib/slapd-modules/emptyds/tests/data/test001.ldif b/contrib/slapd-modules/emptyds/tests/data/test001.ldif
new file mode 100644
index 0000000..b7f289a
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/data/test001.ldif
@@ -0,0 +1,71 @@
+# slapd prevents us from adding the same value multiple times
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: description
+description: one
+description:
+description: two
+description: three
+description: four
+# a space is distinct from an empty value
+description:: ICAg
+-
+replace: drink
+drink: Earl Grey, hot
+-
+delete: description
+description:
+-
+replace: drink
+drink: Earl Grey, hot
+
+# there is no such restriction on deletes, so we exercise this part of the overlay here
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+delete: description
+description:
+description: four
+description:
+description: three
+description: two
+description:
+description:
+description: one
+description:
+-
+add: description
+description:
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: drink
+drink:
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: member
+-
+add: member
+# an empty DN should not be stripped
+member:
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title:
+postaladdress: ITD $ 535 W. William St $ Anytown, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+description:
+description: More than you think
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+testTime: 20050304001801.234Z
diff --git a/contrib/slapd-modules/emptyds/tests/data/test001.out b/contrib/slapd-modules/emptyds/tests/data/test001.out
new file mode 100644
index 0000000..6f41247
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/data/test001.out
@@ -0,0 +1,54 @@
+dn: reqStart=timestamp,cn=log
+reqDN: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+reqMod: description:+ one
+reqMod: description:+ two
+reqMod: description:+ three
+reqMod: description:+ four
+# "description:+ " that's a space, then 3 spaces for value
+reqMod:: ZGVzY3JpcHRpb246KyAgICA=
+reqMod: drink:= Earl Grey, hot
+# second mod was removed, so we have two replaces in succession now and need
+# to separate them (":")
+reqMod:: Og==
+reqMod: drink:= Earl Grey, hot
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+reqMod: description:- four
+reqMod: description:- three
+reqMod: description:- two
+reqMod: description:- one
+# second mod is removed
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+reqMod: drink:=
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=All Staff,ou=Groups,dc=example,dc=com
+reqMod: member:-
+# "member:+ " adding an empty DN
+reqMod:: bWVtYmVyOisg
+reqMod: member:+ cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example
+ ,dc=com
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+reqMod: objectClass:+ testPerson
+reqMod: cn:+ Gern Jensen
+reqMod: sn:+ Jensen
+reqMod: uid:+ gjensen
+reqMod: postalAddress:+ ITD $ 535 W. William St $ Anytown, MI 48103
+reqMod: seeAlso:+ cn=All Staff,ou=Groups,dc=example,dc=com
+reqMod: drink:+ Coffee
+reqMod: homePostalAddress:+ 844 Brown St. Apt. 4 $ Anytown, MI 48104
+reqMod: description:+ Very odd
+reqMod: description:+ More than you think
+reqMod: facsimileTelephoneNumber:+ +1 313 555 7557
+reqMod: telephoneNumber:+ +1 313 555 8343
+reqMod: mail:+ gjensen@mailgw.example.com
+reqMod: homePhone:+ +1 313 555 8844
+reqMod: testTime:+ 20050304001801.234Z
+reqMod: structuralObjectClass:+ testPerson
+
diff --git a/contrib/slapd-modules/emptyds/tests/run b/contrib/slapd-modules/emptyds/tests/run
new file mode 100755
index 0000000..e28820c
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/run
@@ -0,0 +1,218 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+TOPSRCDIR="${SRCDIR-$LDAP_SRC}"
+SRCDIR="${TOPSRCDIR}/tests"
+eval `grep EGREP_CMD= ${LDAP_BUILD}/tests/run`
+eval `$EGREP_CMD -e '^LN_S=' ${LDAP_BUILD}/tests/run`
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+. "${SRCDIR}/scripts/defines.sh"
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+eval `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run`
+export `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run | sed 's/=.*//'`
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA \
+ SRCDIR
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${TOPDIR}/tests/scripts"
+
+export SCRIPTDIR
+
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+if test ! -r ./data; then
+ ${LN_S} ${TOPDIR}/tests/data ./
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ $SCRIPT $*
+ RC=$?
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/contrib/slapd-modules/emptyds/tests/scripts/all b/contrib/slapd-modules/emptyds/tests/scripts/all
new file mode 100755
index 0000000..a5c1774
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/scripts/all
@@ -0,0 +1,92 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/emptyds/tests/scripts/test001-emptyds b/contrib/slapd-modules/emptyds/tests/scripts/test001-emptyds
new file mode 100755
index 0000000..b8d715a
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/scripts/test001-emptyds
@@ -0,0 +1,137 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2019 by Tamim Ziai for DAASI International
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+LDIF=${TOPDIR}/tests/data/test001.out
+
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B
+
+. $CONFFILTER $BACKEND < "${TOPDIR}/tests/data/emptyds.conf" > $CONF1
+
+echo "Running slapadd to build slapd database... "
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+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 ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Checking add/modify handling... "
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT -f "${TOPDIR}/tests/data/test001.ldif"
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking modrdn handling (should still fail with invalidDNSyntax)... "
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=
+deleteoldrdn: 0
+EOMOD
+RC=$?
+case $RC in
+34)
+ echo " ldapmodify failed ($RC)"
+ ;;
+0)
+ echo " ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo " ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Dumping accesslog..."
+
+$LDAPSEARCH -b "cn=log" -H $URI1 \
+ 'objectClass=auditWriteObject' reqDN reqMod | \
+ grep -v -e 'entryCSN' -e '\(create\|modify\)Timestamp' \
+ -e '\(modifier\|creator\)sName' -e 'entryUUID' | \
+ sed -e 's/reqStart=[^,]*,/reqStart=timestamp,/' \
+ > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+# Expectations:
+# - all empty values for directoryString pruned
+# - empty adds/deletes removed, empty replaces kept
+# - remaining values keep the same order as submitted
+# - other syntaxes (especially DNs) are kept intact
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < $LDIF > $LDIFFLT
+
+echo "Comparing filter output..."
+$CMP $LDIFFLT $SEARCHFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/kinit/Makefile b/contrib/slapd-modules/kinit/Makefile
new file mode 100644
index 0000000..7b25ced
--- /dev/null
+++ b/contrib/slapd-modules/kinit/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB) -lkrb5
+
+PROGRAMS = kinit.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+kinit.la: kinit.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/kinit/README b/contrib/slapd-modules/kinit/README
new file mode 100644
index 0000000..7e3ebe8
--- /dev/null
+++ b/contrib/slapd-modules/kinit/README
@@ -0,0 +1,36 @@
+This directory contains the "kinit" slapd module. It is a simple plugin to
+have slapd request a Kerberos TGT and keep it renewed as long as slapd is
+running.
+
+The current implementation has only been tested against the MIT variant of
+the Kerberos libraries. (Heimdal support might come later)
+
+To use the overlay just load it into the slapd process:
+
+ moduleload </path/to>/kinit.so <principal> </path/to/key.tab>
+
+The module accepts two arguments. The first one being the principal for which
+to request the TGT (it defaults to "ldap/<your hostname>@<DEFAULTREALM>")
+and the second one is the path to the keytab file to use for
+authentication, defaulting to whatever your system wide kerberos settings
+default to).
+
+Use Makefile or the following commands should work to
+build it from inside the unpacked slapd sources, provided the required KRB5
+header files and libraries are installed on your system:
+
+ gcc -fPIC -c -I ../../../include/ -I ../../../servers/slapd kinit.c
+ gcc -shared -o kinit.so kinit.o -lkrb5
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2010-2022 The OpenLDAP Foundation.
+
+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>.
diff --git a/contrib/slapd-modules/kinit/kinit.c b/contrib/slapd-modules/kinit/kinit.c
new file mode 100644
index 0000000..630b6bf
--- /dev/null
+++ b/contrib/slapd-modules/kinit/kinit.c
@@ -0,0 +1,295 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 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>.
+ */
+
+#include <portable.h>
+
+#ifndef SLAPD_MOD_KINIT
+#define SLAPD_MOD_KINIT SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_MOD_KINIT
+
+#include <slap.h>
+#include "ldap_rq.h"
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <krb5/krb5.h>
+
+typedef struct kinit_data {
+ krb5_context ctx;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_principal princ;
+ krb5_get_init_creds_opt *opts;
+} kinit_data;
+
+static char* principal;
+static char* kt_name;
+static kinit_data *kid;
+
+static void
+log_krb5_errmsg( krb5_context ctx, const char* func, krb5_error_code rc )
+{
+ const char* errmsg = krb5_get_error_message(ctx, rc);
+ Log(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, "slapd-kinit: %s: %s\n", func, errmsg );
+ krb5_free_error_message(ctx, errmsg);
+ return;
+}
+
+static int
+kinit_check_tgt(kinit_data *kid, int *remaining)
+{
+ int ret=3;
+ krb5_principal princ;
+ krb5_error_code rc;
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ char *name;
+ time_t now=time(NULL);
+
+ rc = krb5_cc_get_principal(kid->ctx, kid->ccache, &princ);
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "krb5_cc_get_principal", rc);
+ return 2;
+ } else {
+ if (!krb5_principal_compare(kid->ctx, kid->princ, princ)) {
+ Log(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "Principal in ccache does not match requested principal\n");
+ krb5_free_principal(kid->ctx, princ);
+ return 2;
+ }
+ }
+
+ rc = krb5_cc_start_seq_get(kid->ctx, kid->ccache, &cursor);
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "krb5_cc_start_seq_get", rc);
+ krb5_free_principal(kid->ctx, princ);
+ return -1;
+ }
+
+ while (!(rc = krb5_cc_next_cred(kid->ctx, kid->ccache, &cursor, &creds))) {
+ if (krb5_is_config_principal(kid->ctx, creds.server)) {
+ krb5_free_cred_contents(kid->ctx, &creds);
+ continue;
+ }
+
+ if (creds.server->length==2 &&
+ (!strcmp(creds.server->data[0].data, "krbtgt")) &&
+ (!strcmp(creds.server->data[1].data, princ->realm.data))) {
+
+ krb5_unparse_name(kid->ctx, creds.server, &name);
+
+ *remaining = (time_t)creds.times.endtime-now;
+ if ( *remaining <= 0) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: TGT (%s) expired\n", name);
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: TGT (%s) expires in %dh:%02dm:%02ds\n",
+ name, *remaining/3600, (*remaining%3600)/60, *remaining%60);
+ }
+ free(name);
+
+ if (*remaining <= 30) {
+ if (creds.times.renew_till-60 > now) {
+ int renewal=creds.times.renew_till-now;
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Time remaining for renewal: %dh:%02dm:%02ds\n",
+ renewal/3600, (renewal%3600)/60, renewal%60);
+ ret = 1;
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Only short time left for renewal. "
+ "Trying to re-init.\n");
+ ret = 2;
+ }
+ } else {
+ ret=0;
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+ break;
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+
+ }
+ krb5_cc_end_seq_get(kid->ctx, kid->ccache, &cursor);
+ krb5_free_principal(kid->ctx, princ);
+ return ret;
+}
+
+void*
+kinit_qtask( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ kinit_data *kid = (kinit_data*)rtask->arg;
+ krb5_error_code rc;
+ krb5_creds creds;
+ int nextcheck, remaining, renew=0;
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_qtask: running TGT check\n" );
+
+ memset(&creds, 0, sizeof(creds));
+
+ renew = kinit_check_tgt(kid, &remaining);
+
+ if (renew > 0) {
+ if (renew==1) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Trying to renew TGT: ");
+ rc = krb5_get_renewed_creds(kid->ctx, &creds, kid->princ, kid->ccache, NULL);
+ if (rc!=0) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n" );
+ log_krb5_errmsg( kid->ctx,
+ "kinit_qtask, Renewal failed: krb5_get_renewed_creds", rc );
+ renew++;
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n" );
+ krb5_cc_initialize(kid->ctx, kid->ccache, creds.client);
+ krb5_cc_store_cred(kid->ctx, kid->ccache, &creds);
+ krb5_free_cred_contents(kid->ctx, &creds);
+ renew=kinit_check_tgt(kid, &remaining);
+ }
+ }
+ if (renew > 1) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Trying to get new TGT: ");
+ rc = krb5_get_init_creds_keytab( kid->ctx, &creds, kid->princ,
+ kid->keytab, 0, NULL, kid->opts);
+ if (rc) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n" );
+ log_krb5_errmsg(kid->ctx, "krb5_get_init_creds_keytab", rc);
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n" );
+ renew=kinit_check_tgt(kid, &remaining);
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+ }
+ }
+ if (renew == 0) {
+ nextcheck = remaining-30;
+ } else {
+ nextcheck = 60;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Next TGT check in %dh:%02dm:%02ds\n",
+ nextcheck/3600, (nextcheck%3600)/60, nextcheck%60);
+ rtask->interval.tv_sec = nextcheck;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ slap_wake_listener();
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+int
+kinit_initialize(void)
+{
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_initialize\n" );
+ krb5_error_code rc;
+ struct re_s *task = NULL;
+
+ kid = ch_calloc(1, sizeof(kinit_data) );
+
+ rc = krb5_init_context( &kid->ctx );
+ if ( !rc )
+ rc = krb5_cc_default(kid->ctx, &kid->ccache );
+
+ if ( !rc ) {
+ if (!principal) {
+ int len=STRLENOF("ldap/")+global_host_bv.bv_len+1;
+ principal=ch_calloc(len, 1);
+ snprintf(principal, len, "ldap/%s", global_host_bv.bv_val);
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Principal <%s>\n", principal );
+
+ }
+ rc = krb5_parse_name(kid->ctx, principal, &kid->princ);
+ }
+
+ if ( !rc && kt_name) {
+ rc = krb5_kt_resolve(kid->ctx, kt_name, &kid->keytab);
+ }
+
+ if ( !rc )
+ rc = krb5_get_init_creds_opt_alloc(kid->ctx, &kid->opts);
+
+ if ( !rc )
+ rc = krb5_get_init_creds_opt_set_out_ccache( kid->ctx, kid->opts, kid->ccache);
+
+ if ( !rc ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ task = ldap_pvt_runqueue_insert( &slapd_rq, 10, kinit_qtask, (void*)kid,
+ "kinit_qtask", "ldap/bronsted.g17.lan@G17.LAN" );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "kinit_initialize", rc);
+ rc = -1;
+ }
+ return rc;
+}
+
+#if SLAPD_MOD_KINIT == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ if (argc > 0) {
+ principal = ch_strdup(argv[0]);
+ }
+ if (argc > 1) {
+ kt_name = ch_strdup(argv[1]);
+ }
+ if (argc > 2) {
+ return -1;
+ }
+ return kinit_initialize();
+}
+
+int
+term_module() {
+ if (principal)
+ ch_free(principal);
+ if (kt_name)
+ ch_free(kt_name);
+ if (kid) {
+ struct re_s *task;
+
+ task=ldap_pvt_runqueue_find( &slapd_rq, kinit_qtask, (void*)kid);
+ if (task) {
+ if ( ldap_pvt_runqueue_isrunning(&slapd_rq, task) ) {
+ ldap_pvt_runqueue_stoptask(&slapd_rq, task);
+ }
+ ldap_pvt_runqueue_remove(&slapd_rq, task);
+ }
+ if ( kid->ctx ) {
+ if ( kid->princ )
+ krb5_free_principal(kid->ctx, kid->princ);
+ if ( kid->ccache )
+ krb5_cc_close(kid->ctx, kid->ccache);
+ if ( kid->keytab )
+ krb5_kt_close(kid->ctx, kid->keytab);
+ if ( kid->opts )
+ krb5_get_init_creds_opt_free(kid->ctx, kid->opts);
+ krb5_free_context(kid->ctx);
+ }
+ ch_free(kid);
+ }
+ return 0;
+}
+#endif
+
+#endif /* SLAPD_MOD_KINIT */
+
diff --git a/contrib/slapd-modules/lastbind/Makefile b/contrib/slapd-modules/lastbind/Makefile
new file mode 100644
index 0000000..c273cd3
--- /dev/null
+++ b/contrib/slapd-modules/lastbind/Makefile
@@ -0,0 +1,68 @@
+# $OpenLDAP$
+# Copyright 2009 Jonathan Clarke <jonathan@phillipoux.net>.
+# 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_LASTBIND=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = lastbind.la
+MANPAGES = slapo-lastbind.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+lastbind.la: lastbind.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/lastbind/lastbind.c b/contrib/slapd-modules/lastbind/lastbind.c
new file mode 100644
index 0000000..bd8e782
--- /dev/null
+++ b/contrib/slapd-modules/lastbind/lastbind.c
@@ -0,0 +1,320 @@
+/* lastbind.c - Record timestamp of the last successful bind to entries */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2009 Jonathan Clarke <jonathan@phillipoux.net>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work is loosely derived from the ppolicy overlay.
+ */
+
+#include "portable.h"
+
+/*
+ * This file implements an overlay that stores the timestamp of the
+ * last successful bind operation in a directory entry.
+ *
+ * Optimization: to avoid performing a write on each bind,
+ * a precision for this timestamp may be configured, causing it to
+ * only be updated if it is older than a given number of seconds.
+ */
+
+#ifdef SLAPD_OVER_LASTBIND
+
+#include <ldap.h>
+#include "lutil.h"
+#include "slap.h"
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "slap-config.h"
+
+/* Per-instance configuration information */
+typedef struct lastbind_info {
+ /* precision to update timestamp in authTimestamp attribute */
+ int timestamp_precision;
+ int forward_updates; /* use frontend for authTimestamp updates */
+} lastbind_info;
+
+/* Operational attributes */
+static AttributeDescription *ad_authTimestamp;
+
+/* This is the definition used by ISODE, as supplied to us in
+ * ITS#6238 Followup #9
+ */
+static struct schema_info {
+ char *def;
+ AttributeDescription **ad;
+} lastBind_OpSchema[] = {
+ { "( 1.3.6.1.4.1.453.16.2.188 "
+ "NAME 'authTimestamp' "
+ "DESC 'last successful authentication using any method/mech' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
+ &ad_authTimestamp},
+ { NULL, NULL }
+};
+
+/* configuration attribute and objectclass */
+static ConfigTable lastbindcfg[] = {
+ { "lastbind-precision", "seconds", 2, 2, 0,
+ ARG_INT|ARG_OFFSET,
+ (void *)offsetof(lastbind_info, timestamp_precision),
+ "( OLcfgCtAt:5.1 "
+ "NAME 'olcLastBindPrecision' "
+ "DESC 'Precision of authTimestamp attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "lastbind_forward_updates", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(lastbind_info,forward_updates),
+ "( OLcfgAt:5.2 NAME 'olcLastBindForwardUpdates' "
+ "DESC 'Allow authTimestamp updates to be forwarded via updateref' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs lastbindocs[] = {
+ { "( OLcfgCtOc:5.1 "
+ "NAME 'olcLastBindConfig' "
+ "DESC 'Last Bind configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcLastBindPrecision $ olcLastBindForwardUpdates) )",
+ Cft_Overlay, lastbindcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static time_t
+parse_time( char *atm )
+{
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ time_t ret = (time_t)-1;
+
+ if ( lutil_parsetime( atm, &tm ) == 0) {
+ lutil_tm2time( &tm, &tt );
+ ret = tt.tt_sec;
+ }
+ return ret;
+}
+
+static int
+lastbind_bind_response( Operation *op, SlapReply *rs )
+{
+ Modifications *mod = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ Entry *e;
+ int rc;
+
+ /* we're only interested if the bind was successful */
+ if ( rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd->bd_info = bi;
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ {
+ lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private;
+
+ time_t now, bindtime = (time_t)-1;
+ Attribute *a;
+ Modifications *m;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+
+ /* get the current time */
+ now = slap_get_time();
+
+ /* get authTimestamp attribute, if it exists */
+ if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL) {
+ bindtime = parse_time( a->a_nvals[0].bv_val );
+
+ if (bindtime != (time_t)-1) {
+ /* if the recorded bind time is within our precision, we're done
+ * it doesn't need to be updated (save a write for nothing) */
+ if ((now - bindtime) < lbi->timestamp_precision) {
+ goto done;
+ }
+ }
+ }
+
+ /* update the authTimestamp in the user's entry with the current time */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+ m->sml_next = mod;
+ mod = m;
+ }
+
+done:
+ be_entry_release_r( op, e );
+
+ /* perform the update, if necessary */
+ if ( mod ) {
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ LDAPControl c, *ca[2];
+ lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private;
+
+ /* This is a DSA-specific opattr, it never gets replicated. */
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = mod;
+ op2.orm_no_opattrs = 0;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ /*
+ * Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy
+ *
+ * If this server is a shadow and forward_updates is true,
+ * use the frontend to perform this modify. That will trigger
+ * the update referral, which can then be forwarded by the
+ * chain overlay. Obviously the updateref and chain overlay
+ * must be configured appropriately for this to be useful.
+ */
+ if ( SLAP_SHADOW( op->o_bd ) && lbi->forward_updates ) {
+ op2.o_bd = frontendDB;
+
+ /* Must use Relax control since these are no-user-mod */
+ op2.o_relax = SLAP_CONTROL_CRITICAL;
+ op2.o_ctrls = ca;
+ ca[0] = &c;
+ ca[1] = NULL;
+ BER_BVZERO( &c.ldctl_value );
+ c.ldctl_iscritical = 1;
+ c.ldctl_oid = LDAP_CONTROL_RELAX;
+ } else {
+ /* If not forwarding, don't update opattrs and don't replicate */
+ if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
+ op2.orm_no_opattrs = 1;
+ op2.o_dont_replicate = 1;
+ }
+ /* TODO: not sure what this does in slapo-ppolicy */
+ /*
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ */
+ }
+
+ rc = op2.o_bd->be_modify( &op2, &r2 );
+ slap_mods_free( mod, 1 );
+ }
+
+ op->o_bd->bd_info = bi;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lastbind_bind( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ /* setup a callback to intercept result of this bind operation
+ * and pass along the lastbind_info struct */
+ cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx );
+ cb->sc_response = lastbind_bind_response;
+ cb->sc_next = op->o_callback->sc_next;
+ cb->sc_private = on->on_bi.bi_private;
+ op->o_callback->sc_next = cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lastbind_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ /* initialize private structure to store configuration */
+ on->on_bi.bi_private = ch_calloc( 1, sizeof(lastbind_info) );
+
+ return 0;
+}
+
+static int
+lastbind_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ lastbind_info *lbi = (lastbind_info *) on->on_bi.bi_private;
+
+ /* free private structure to store configuration */
+ free( lbi );
+
+ return 0;
+}
+
+static slap_overinst lastbind;
+
+int lastbind_initialize()
+{
+ int i, code;
+
+ /* register operational schema for this overlay (authTimestamp attribute) */
+ for (i=0; lastBind_OpSchema[i].def; i++) {
+ code = register_at( lastBind_OpSchema[i].def, lastBind_OpSchema[i].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "lastbind_initialize: register_at failed\n" );
+ return code;
+ }
+ }
+
+ ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
+
+ lastbind.on_bi.bi_type = "lastbind";
+ lastbind.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ lastbind.on_bi.bi_db_init = lastbind_db_init;
+ lastbind.on_bi.bi_db_close = lastbind_db_close;
+ lastbind.on_bi.bi_op_bind = lastbind_bind;
+
+ /* register configuration directives */
+ lastbind.on_bi.bi_cf_ocs = lastbindocs;
+ code = config_register_schema( lastbindcfg, lastbindocs );
+ if ( code ) return code;
+
+ return overlay_register( &lastbind );
+}
+
+#if SLAPD_OVER_LASTBIND == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return lastbind_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_LASTBIND) */
diff --git a/contrib/slapd-modules/lastbind/slapo-lastbind.5 b/contrib/slapd-modules/lastbind/slapo-lastbind.5
new file mode 100644
index 0000000..d0da8b8
--- /dev/null
+++ b/contrib/slapd-modules/lastbind/slapo-lastbind.5
@@ -0,0 +1,108 @@
+.TH SLAPO-LASTBIND 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2009 Jonathan Clarke, All Rights Reserved.
+.\" $OpenLDAP$
+.SH NAME
+slapo-lastbind \- lastbind overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B lastbind
+overlay to
+.BR slapd (8)
+allows recording the timestamp of the last successful bind to entries
+in the directory, in the
+.B authTimestamp
+attribute.
+The overlay can be configured to update this timestamp only if it is
+older than a given value, thus avoiding large numbers of write
+operations penalizing performance.
+One sample use for this overlay would be to detect unused accounts.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B lastbind
+overlay must be prefixed by
+.BR lastbind\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay lastbind
+This directive adds the
+.B lastbind
+overlay to the current database, see
+.BR slapd.conf (5)
+for details.
+
+.LP
+This
+.B slapd.conf
+configuration option is defined for the lastbind overlay. It must
+appear after the
+.B overlay
+directive:
+.TP
+.B lastbind-precision <seconds>
+The value
+.B <seconds>
+is the number of seconds after which to update the
+.B authTimestamp
+attribute in an entry. If the existing value of
+.B authTimestamp
+is less than
+.B <seconds>
+old, it will not be changed.
+If this configuration option is omitted, the
+.B authTimestamp
+attribute is updated on each successful bind operation.
+.TP
+.B lastbind_forward_updates
+Specify that updates of the authTimestamp attribute
+on a consumer should be forwarded
+to a provider instead of being written directly into the consumer's local
+database. This setting is only useful on a replication consumer, and
+also requires the
+.B updateref
+setting and
+.B chain
+overlay to be appropriately configured.
+
+.SH EXAMPLE
+This example configures the
+.B lastbind
+overlay to store
+.B authTimestamp
+in all entries in a database, with a 1 week precision.
+Add the following to
+.BR slapd.conf (5):
+
+.LP
+.nf
+ database <database>
+ # ...
+
+ overlay lastbind
+ lastbind-precision 604800
+.fi
+.LP
+.B slapd
+must also load
+.B lastbind.la,
+if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-lastbind (5)
+overlay supports dynamic configuration via
+.BR back-config.
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2009 by Jonathan Clarke. It is loosely
+derived from the password policy overlay.
diff --git a/contrib/slapd-modules/lastmod/Makefile b/contrib/slapd-modules/lastmod/Makefile
new file mode 100644
index 0000000..9b06c28
--- /dev/null
+++ b/contrib/slapd-modules/lastmod/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_LASTMOD=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = lastmod.la
+MANPAGES = slapo-lastmod.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+lastmod.la: lastmod.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/lastmod/lastmod.c b/contrib/slapd-modules/lastmod/lastmod.c
new file mode 100644
index 0000000..116b9ae
--- /dev/null
+++ b/contrib/slapd-modules/lastmod/lastmod.c
@@ -0,0 +1,963 @@
+/* lastmod.c - returns last modification info */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_LASTMOD
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+typedef struct lastmod_info_t {
+ struct berval lmi_rdnvalue;
+ Entry *lmi_e;
+ ldap_pvt_thread_mutex_t lmi_entry_mutex;
+ int lmi_enabled;
+} lastmod_info_t;
+
+struct lastmod_schema_t {
+ ObjectClass *lms_oc_lastmod;
+ AttributeDescription *lms_ad_lastmodDN;
+ AttributeDescription *lms_ad_lastmodType;
+ AttributeDescription *lms_ad_lastmodEnabled;
+} lastmod_schema;
+
+enum lastmodType_e {
+ LASTMOD_ADD = 0,
+ LASTMOD_DELETE,
+ LASTMOD_EXOP,
+ LASTMOD_MODIFY,
+ LASTMOD_MODRDN,
+ LASTMOD_UNKNOWN
+};
+
+struct berval lastmodType[] = {
+ BER_BVC( "add" ),
+ BER_BVC( "delete" ),
+ BER_BVC( "exop" ),
+ BER_BVC( "modify" ),
+ BER_BVC( "modrdn" ),
+ BER_BVC( "unknown" ),
+ BER_BVNULL
+};
+
+static struct m_s {
+ char *schema;
+ slap_mask_t flags;
+ int offset;
+} moc[] = {
+ { "( 1.3.6.1.4.1.4203.666.3.13"
+ "NAME 'lastmod' "
+ "DESC 'OpenLDAP per-database last modification monitoring' "
+ "STRUCTURAL "
+ "SUP top "
+ "MUST cn "
+ "MAY ( "
+ "lastmodDN "
+ "$ lastmodType "
+ "$ description "
+ "$ seeAlso "
+ ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof( struct lastmod_schema_t, lms_oc_lastmod ) },
+ { NULL }
+}, mat[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.28"
+ "NAME 'lastmodDN' "
+ "DESC 'DN of last modification' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )", SLAP_AT_HIDE,
+ offsetof( struct lastmod_schema_t, lms_ad_lastmodDN ) },
+ { "( 1.3.6.1.4.1.4203.666.1.29"
+ "NAME 'lastmodType' "
+ "DESC 'Type of last modification' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "EQUALITY caseIgnoreMatch "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )", SLAP_AT_HIDE,
+ offsetof( struct lastmod_schema_t, lms_ad_lastmodType ) },
+ { "( 1.3.6.1.4.1.4203.666.1.30"
+ "NAME 'lastmodEnabled' "
+ "DESC 'Lastmod overlay state' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "EQUALITY booleanMatch "
+ "SINGLE-VALUE )", 0,
+ offsetof( struct lastmod_schema_t, lms_ad_lastmodEnabled ) },
+ { NULL }
+
+ /* FIXME: what about UUID of last modified entry? */
+};
+
+static int
+lastmod_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ int rc;
+
+ /* if we get here, it must be a success */
+ rs->sr_err = LDAP_SUCCESS;
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+ rc = test_filter( op, lmi->lmi_e, op->oq_search.rs_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ rs->sr_entry = lmi->lmi_e;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_flags = 0;
+ rs->sr_attrs = NULL;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ send_ldap_result( op, rs );
+
+ return 0;
+}
+
+static int
+lastmod_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Attribute *a;
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, lmi->lmi_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, lmi->lmi_e, op->oq_compare.rs_ava->aa_desc,
+ &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+
+ for ( a = attr_find( lmi->lmi_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
+ a != NULL;
+ a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
+ {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+
+return_results:;
+
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ send_ldap_result( op, rs );
+
+ if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ return rs->sr_err;
+}
+
+static int
+lastmod_exop( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ /* Temporary */
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+
+ return -1;
+}
+
+static int
+lastmod_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Modifications *ml;
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+ if ( !acl_check_modlist( op, lmi->lmi_e, op->orm_modlist ) ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto cleanup;
+ }
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ Attribute *a;
+
+ if ( ml->sml_desc != lastmod_schema.lms_ad_lastmodEnabled ) {
+ continue;
+ }
+
+ if ( ml->sml_op != LDAP_MOD_REPLACE ) {
+ rs->sr_text = "unsupported mod type";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto cleanup;
+ }
+
+ a = attr_find( lmi->lmi_e->e_attrs, ml->sml_desc );
+
+ if ( a == NULL ) {
+ rs->sr_text = "lastmod overlay internal error";
+ rs->sr_err = LDAP_OTHER;
+ goto cleanup;
+ }
+
+ ch_free( a->a_vals[ 0 ].bv_val );
+ ber_dupbv( &a->a_vals[ 0 ], &ml->sml_values[ 0 ] );
+ if ( a->a_nvals ) {
+ ch_free( a->a_nvals[ 0 ].bv_val );
+ if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_nvalues[ 0 ] );
+ } else {
+ ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_values[ 0 ] );
+ }
+ }
+
+ if ( strcmp( ml->sml_values[ 0 ].bv_val, "TRUE" ) == 0 ) {
+ lmi->lmi_enabled = 1;
+ } else if ( strcmp( ml->sml_values[ 0 ].bv_val, "FALSE" ) == 0 ) {
+ lmi->lmi_enabled = 0;
+ } else {
+ assert( 0 );
+ }
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+cleanup:;
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+
+ return rs->sr_err;
+}
+
+static int
+lastmod_op_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Modifications *ml;
+
+ if ( dn_match( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
+ switch ( op->o_tag ) {
+ case LDAP_REQ_SEARCH:
+ if ( op->ors_scope != LDAP_SCOPE_BASE ) {
+ goto return_referral;
+ }
+ /* process */
+ return lastmod_search( op, rs );
+
+ case LDAP_REQ_COMPARE:
+ return lastmod_compare( op, rs );
+
+ case LDAP_REQ_EXTENDED:
+ /* if write, reject; otherwise process */
+ if ( exop_is_write( op )) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ goto return_error;
+ }
+ return lastmod_exop( op, rs );
+
+ case LDAP_REQ_MODIFY:
+ /* allow only changes to overlay status */
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifiersName ) != 0
+ && ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) != 0
+ && ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) != 0
+ && ad_cmp( ml->sml_desc, lastmod_schema.lms_ad_lastmodEnabled ) != 0 )
+ {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ goto return_error;
+ }
+ }
+ return lastmod_modify( op, rs );
+
+ default:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ goto return_error;
+ }
+ }
+
+ if ( dnIsSuffix( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
+ goto return_referral;
+ }
+
+ return SLAP_CB_CONTINUE;
+
+return_referral:;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, op->ors_scope );
+
+ if ( !rs->sr_ref ) {
+ rs->sr_ref = default_referral;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref != default_referral ) {
+ ber_bvarray_free( rs->sr_ref );
+ }
+ rs->sr_ref = NULL;
+
+ return -1;
+
+return_error:;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+
+ return -1;
+}
+
+static int
+best_guess( Operation *op,
+ struct berval *bv_entryCSN, struct berval *bv_nentryCSN,
+ struct berval *bv_modifyTimestamp, struct berval *bv_nmodifyTimestamp,
+ struct berval *bv_modifiersName, struct berval *bv_nmodifiersName )
+{
+ if ( bv_entryCSN ) {
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval entryCSN;
+
+ entryCSN.bv_val = csnbuf;
+ entryCSN.bv_len = sizeof( csnbuf );
+ slap_get_csn( NULL, &entryCSN, 0 );
+
+ ber_dupbv( bv_entryCSN, &entryCSN );
+ ber_dupbv( bv_nentryCSN, &entryCSN );
+ }
+
+ if ( bv_modifyTimestamp ) {
+ char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+ time_t currtime;
+
+ /* best guess */
+#if 0
+ currtime = slap_get_time();
+#endif
+ /* maybe we better use the time the operation was initiated */
+ currtime = op->o_time;
+
+ timestamp.bv_val = tmbuf;
+ timestamp.bv_len = sizeof(tmbuf);
+ slap_timestamp( &currtime, &timestamp );
+
+ ber_dupbv( bv_modifyTimestamp, &timestamp );
+ ber_dupbv( bv_nmodifyTimestamp, bv_modifyTimestamp );
+ }
+
+ if ( bv_modifiersName ) {
+ /* best guess */
+ ber_dupbv( bv_modifiersName, &op->o_dn );
+ ber_dupbv( bv_nmodifiersName, &op->o_ndn );
+ }
+
+ return 0;
+}
+
+static int
+lastmod_update( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Attribute *a;
+ Modifications *ml = NULL;
+ struct berval bv_entryCSN = BER_BVNULL,
+ bv_nentryCSN = BER_BVNULL,
+ bv_modifyTimestamp = BER_BVNULL,
+ bv_nmodifyTimestamp = BER_BVNULL,
+ bv_modifiersName = BER_BVNULL,
+ bv_nmodifiersName = BER_BVNULL,
+ bv_name = BER_BVNULL,
+ bv_nname = BER_BVNULL;
+ enum lastmodType_e lmt = LASTMOD_UNKNOWN;
+ Entry *e = NULL;
+ int rc = -1;
+
+ /* FIXME: timestamp? modifier? */
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ lmt = LASTMOD_ADD;
+ e = op->ora_e;
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_entryCSN, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nentryCSN, &a->a_vals[0] );
+ }
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
+ }
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
+ ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
+ }
+ ber_dupbv( &bv_name, &e->e_name );
+ ber_dupbv( &bv_nname, &e->e_nname );
+ break;
+
+ case LDAP_REQ_DELETE:
+ lmt = LASTMOD_DELETE;
+
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+ &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+ &bv_modifiersName, &bv_nmodifiersName );
+
+ ber_dupbv( &bv_name, &op->o_req_dn );
+ ber_dupbv( &bv_nname, &op->o_req_ndn );
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ lmt = LASTMOD_EXOP;
+
+ /* actually, password change is wrapped around a backend
+ * call to modify, so it never shows up as an exop... */
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+ &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+ &bv_modifiersName, &bv_nmodifiersName );
+
+ ber_dupbv( &bv_name, &op->o_req_dn );
+ ber_dupbv( &bv_nname, &op->o_req_ndn );
+ break;
+
+ case LDAP_REQ_MODIFY:
+ lmt = LASTMOD_MODIFY;
+ rc = 3;
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ad_cmp( ml->sml_desc , slap_schema.si_ad_modifiersName ) == 0 ) {
+ ber_dupbv( &bv_modifiersName, &ml->sml_values[0] );
+ ber_dupbv( &bv_nmodifiersName, &ml->sml_nvalues[0] );
+
+ rc--;
+ if ( !rc ) {
+ break;
+ }
+
+ } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) == 0 ) {
+ ber_dupbv( &bv_entryCSN, &ml->sml_values[0] );
+ if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
+ ber_dupbv( &bv_nentryCSN, &ml->sml_nvalues[0] );
+ } else {
+ ber_dupbv( &bv_nentryCSN, &ml->sml_values[0] );
+ }
+
+ rc --;
+ if ( !rc ) {
+ break;
+ }
+
+ } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) == 0 ) {
+ ber_dupbv( &bv_modifyTimestamp, &ml->sml_values[0] );
+ if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
+ ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_nvalues[0] );
+ } else {
+ ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_values[0] );
+ }
+
+ rc --;
+ if ( !rc ) {
+ break;
+ }
+ }
+ }
+
+ /* if rooted at global overlay, opattrs are not yet in place */
+ if ( BER_BVISNULL( &bv_modifiersName ) ) {
+ best_guess( op, NULL, NULL, NULL, NULL, &bv_modifiersName, &bv_nmodifiersName );
+ }
+
+ if ( BER_BVISNULL( &bv_entryCSN ) ) {
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN, NULL, NULL, NULL, NULL );
+ }
+
+ if ( BER_BVISNULL( &bv_modifyTimestamp ) ) {
+ best_guess( op, NULL, NULL, &bv_modifyTimestamp, &bv_nmodifyTimestamp, NULL, NULL );
+ }
+
+ ber_dupbv( &bv_name, &op->o_req_dn );
+ ber_dupbv( &bv_nname, &op->o_req_ndn );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ lmt = LASTMOD_MODRDN;
+ e = NULL;
+
+ if ( op->orr_newSup && !BER_BVISNULL( op->orr_newSup ) ) {
+ build_new_dn( &bv_name, op->orr_newSup, &op->orr_newrdn, NULL );
+ build_new_dn( &bv_nname, op->orr_nnewSup, &op->orr_nnewrdn, NULL );
+
+ } else {
+ struct berval pdn;
+
+ dnParent( &op->o_req_dn, &pdn );
+ build_new_dn( &bv_name, &pdn, &op->orr_newrdn, NULL );
+
+ dnParent( &op->o_req_ndn, &pdn );
+ build_new_dn( &bv_nname, &pdn, &op->orr_nnewrdn, NULL );
+ }
+
+ if ( on->on_info->oi_orig->bi_entry_get_rw ) {
+ BackendInfo *bi = op->o_bd->bd_info;
+ int rc;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
+ rc = op->o_bd->bd_info->bi_entry_get_rw( op, &bv_name, NULL, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS ) {
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
+ ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_entryCSN, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nentryCSN, &a->a_vals[0] );
+ }
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
+ }
+ }
+
+ assert( dn_match( &bv_name, &e->e_name ) );
+ assert( dn_match( &bv_nname, &e->e_nname ) );
+
+ op->o_bd->bd_info->bi_entry_release_rw( op, e, 0 );
+ }
+
+ op->o_bd->bd_info = bi;
+
+ }
+
+ /* if !bi_entry_get_rw || bi_entry_get_rw failed for any reason... */
+ if ( e == NULL ) {
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+ &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+ &bv_modifiersName, &bv_nmodifiersName );
+ }
+
+ break;
+
+ default:
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+#if 0
+ fprintf( stderr, "### lastmodDN: %s %s\n", bv_name.bv_val, bv_nname.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodDN );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_name;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nname;
+
+#if 0
+ fprintf( stderr, "### lastmodType: %s %s\n", lastmodType[ lmt ].bv_val, lastmodType[ lmt ].bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodType );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
+ ch_free( a->a_nvals[0].bv_val );
+ ber_dupbv( &a->a_nvals[0], &lastmodType[ lmt ] );
+
+#if 0
+ fprintf( stderr, "### modifiersName: %s %s\n", bv_modifiersName.bv_val, bv_nmodifiersName.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_modifiersName;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nmodifiersName;
+
+#if 0
+ fprintf( stderr, "### modifyTimestamp: %s %s\n", bv_nmodifyTimestamp.bv_val, bv_modifyTimestamp.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_modifyTimestamp;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nmodifyTimestamp;
+
+#if 0
+ fprintf( stderr, "### entryCSN: %s %s\n", bv_nentryCSN.bv_val, bv_entryCSN.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_entryCSN;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nentryCSN;
+
+ rc = 0;
+
+error_return:;
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ return rc;
+}
+
+static int
+lastmod_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+
+ /* don't record failed operations */
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ /* FIXME: other cases? */
+ break;
+
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* record only write operations */
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_DELETE:
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ /* if write, process */
+ if ( exop_is_write( op ))
+ break;
+
+ /* fall thru */
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* skip if disabled */
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+ if ( !lmi->lmi_enabled ) {
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ (void)lastmod_update( op, rs );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lastmod_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ lastmod_info_t *lmi;
+
+ if ( lastmod_schema.lms_oc_lastmod == NULL ) {
+ int i;
+ const char *text;
+
+ /* schema integration */
+ for ( i = 0; mat[i].schema; i++ ) {
+ int code;
+ AttributeDescription **ad =
+ ((AttributeDescription **)&(((char *)&lastmod_schema)[mat[i].offset]));
+ ad[0] = NULL;
+
+ code = register_at( mat[i].schema, ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "lastmod_init: register_at failed\n" );
+ return -1;
+ }
+ (*ad)->ad_type->sat_flags |= mat[i].flags;
+ }
+
+ for ( i = 0; moc[i].schema; i++ ) {
+ int code;
+ ObjectClass **Oc =
+ ((ObjectClass **)&(((char *)&lastmod_schema)[moc[i].offset]));
+
+ code = register_oc( moc[i].schema, Oc, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "lastmod_init: register_oc failed\n" );
+ return -1;
+ }
+ (*Oc)->soc_flags |= moc[i].flags;
+ }
+ }
+
+ lmi = (lastmod_info_t *)ch_malloc( sizeof( lastmod_info_t ) );
+
+ memset( lmi, 0, sizeof( lastmod_info_t ) );
+ lmi->lmi_enabled = 1;
+
+ on->on_bi.bi_private = lmi;
+
+ return 0;
+}
+
+static int
+lastmod_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+
+ if ( strcasecmp( argv[ 0 ], "lastmod-rdnvalue" ) == 0 ) {
+ if ( lmi->lmi_rdnvalue.bv_val ) {
+ /* already defined! */
+ ch_free( lmi->lmi_rdnvalue.bv_val );
+ }
+
+ ber_str2bv( argv[ 1 ], 0, 1, &lmi->lmi_rdnvalue );
+
+ } else if ( strcasecmp( argv[ 0 ], "lastmod-enabled" ) == 0 ) {
+ if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+ lmi->lmi_enabled = 1;
+
+ } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+ lmi->lmi_enabled = 0;
+
+ } else {
+ return -1;
+ }
+
+ } else {
+ return SLAP_CONF_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static int
+lastmod_db_open( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ char buf[ 8192 ];
+ static char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval entryCSN;
+ struct berval timestamp;
+
+ if ( !SLAP_LASTMOD( be ) ) {
+ fprintf( stderr, "set \"lastmod on\" to make this overlay effective\n" );
+ return -1;
+ }
+
+ /*
+ * Start
+ */
+ timestamp.bv_val = tmbuf;
+ timestamp.bv_len = sizeof(tmbuf);
+ slap_timestamp( &starttime, &timestamp );
+
+ entryCSN.bv_val = csnbuf;
+ entryCSN.bv_len = sizeof( csnbuf );
+ slap_get_csn( NULL, &entryCSN, 0 );
+
+ if ( BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
+ ber_str2bv( "Lastmod", 0, 1, &lmi->lmi_rdnvalue );
+ }
+
+ snprintf( buf, sizeof( buf ),
+ "dn: cn=%s%s%s\n"
+ "objectClass: %s\n"
+ "structuralObjectClass: %s\n"
+ "cn: %s\n"
+ "description: This object contains the last modification to this database\n"
+ "%s: cn=%s%s%s\n"
+ "%s: %s\n"
+ "%s: %s\n"
+ "createTimestamp: %s\n"
+ "creatorsName: %s\n"
+ "entryCSN: %s\n"
+ "modifyTimestamp: %s\n"
+ "modifiersName: %s\n"
+ "hasSubordinates: FALSE\n",
+ lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
+ lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
+ lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
+ lmi->lmi_rdnvalue.bv_val,
+ lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val,
+ lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
+ lastmod_schema.lms_ad_lastmodType->ad_cname.bv_val, lastmodType[ LASTMOD_ADD ].bv_val,
+ lastmod_schema.lms_ad_lastmodEnabled->ad_cname.bv_val, lmi->lmi_enabled ? "TRUE" : "FALSE",
+ tmbuf,
+ BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val,
+ entryCSN.bv_val,
+ tmbuf,
+ BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val );
+
+#if 0
+ fprintf( stderr, "# entry:\n%s\n", buf );
+#endif
+
+ lmi->lmi_e = str2entry( buf );
+ if ( lmi->lmi_e == NULL ) {
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_init( &lmi->lmi_entry_mutex );
+
+ return 0;
+}
+
+static int
+lastmod_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+
+ if ( lmi ) {
+ if ( !BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
+ ch_free( lmi->lmi_rdnvalue.bv_val );
+ }
+
+ if ( lmi->lmi_e ) {
+ entry_free( lmi->lmi_e );
+
+ ldap_pvt_thread_mutex_destroy( &lmi->lmi_entry_mutex );
+ }
+
+ ch_free( lmi );
+ }
+
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst lastmod;
+
+int
+lastmod_initialize()
+{
+ lastmod.on_bi.bi_type = "lastmod";
+ lastmod.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ lastmod.on_bi.bi_db_init = lastmod_db_init;
+ lastmod.on_bi.bi_db_config = lastmod_db_config;
+ lastmod.on_bi.bi_db_destroy = lastmod_db_destroy;
+ lastmod.on_bi.bi_db_open = lastmod_db_open;
+
+ lastmod.on_bi.bi_op_add = lastmod_op_func;
+ lastmod.on_bi.bi_op_compare = lastmod_op_func;
+ lastmod.on_bi.bi_op_delete = lastmod_op_func;
+ lastmod.on_bi.bi_op_modify = lastmod_op_func;
+ lastmod.on_bi.bi_op_modrdn = lastmod_op_func;
+ lastmod.on_bi.bi_op_search = lastmod_op_func;
+ lastmod.on_bi.bi_extended = lastmod_op_func;
+
+ lastmod.on_response = lastmod_response;
+
+ return overlay_register( &lastmod );
+}
+
+#if SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return lastmod_initialize();
+}
+#endif /* SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_LASTMOD) */
diff --git a/contrib/slapd-modules/lastmod/slapo-lastmod.5 b/contrib/slapd-modules/lastmod/slapo-lastmod.5
new file mode 100644
index 0000000..ea0ca23
--- /dev/null
+++ b/contrib/slapd-modules/lastmod/slapo-lastmod.5
@@ -0,0 +1,185 @@
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.TH SLAPO_LASTMOD 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.SH NAME
+slapo-lastmod \- Last Modification overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+.LP
+The
+.B lastmod
+overlay creates a service entry rooted at the suffix of the database
+it's stacked onto, which holds the DN, the modification type,
+the modifiersName and the modifyTimestamp of the last write operation
+performed on that database.
+The lastmod overlay cannot be used when the "lastmod" feature
+is disabled, i.e. "lastmod off" is used.
+.P
+All operations targeted at the DN of the lastmod entry are rejected,
+except reads, i.e. searches with
+.B base
+scope.
+Regular operations are ignored, unless they result in writing; then,
+in case of success, the lastmod entry is updated accordingly,
+if possible.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+configuration options apply to the lastmod overlay. They must appear
+after the
+.B overlay
+directive.
+.TP
+.B lastmod-rdnvalue <RDN value>
+Specify the value of the RDN used for the service entry. By default
+.I Lastmod
+is used.
+.TP
+.B lastmod-enabled {yes|no}
+Specify whether the overlay must be enabled or not at startup.
+By default, the overlay is enabled; however, by changing the boolean
+value of the attribute
+.IR lastmodEnabled ,
+one can affect the status of the overlay.
+This is useful, for instance, to inhibit the overlay from keeping track
+of large bulk loads or deletions.
+
+.SH OBJECT CLASS
+The
+.B lastmod
+overlay depends on the
+.B lastmod
+objectClass. The definition of that class is as follows:
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.3.13 "
+ NAME 'lastmod'
+ DESC 'OpenLDAP per-database last modification monitoring'
+ STRUCTURAL
+ SUP top
+ MUST ( cn $ lastmodDN $ lastmodType )
+ MAY ( description $ seeAlso ) )
+.RE
+
+.SH ATTRIBUTES
+.P
+Each one of the sections below details the meaning and use of a particular
+attribute of this
+.B lastmod
+objectClass.
+Most of the attributes that are specific to the lastmod objectClass are
+operational, since they can logically be altered only by the DSA.
+The most notable exception is the
+.I lastmodEnabled
+attributeType, which can be altered via protocol to change the status
+of the overlay.
+.P
+
+.B lastmodEnabled
+.P
+This attribute contains a boolean flag that determines the status
+of the overlay. It can be altered via protocol by issuing a modify
+operation that replaces the value of the attribute.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.1.30
+ NAME 'lastmodEnabled'
+ DESC 'Lastmod overlay state'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ EQUALITY booleanMatch
+ SINGLE-VALUE )
+.RE
+
+.SH OPERATIONAL ATTRIBUTES
+.P
+Each one of the sections below details the meaning and use of a particular
+attribute of this
+.B lastmod
+objectClass.
+Most of the attributes that are specific to the lastmod objectClass are
+operational, since they can logically be altered only by the DSA.
+.P
+
+.B lastmodDN
+.P
+This attribute contains the distinguished name of the entry
+that was last modified within the naming context of a database.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.1.28
+ NAME 'lastmodDN'
+ DESC 'DN of last modification'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ NO-USER-MODIFICATION
+ USAGE directoryOperation )
+.RE
+
+.B lastmodType
+.P
+This attribute contains the type of the modification that occurred
+to the last modified entry. Legal values are
+.BR add ,
+.BR delete ,
+.BR exop ,
+.BR modify ,
+.B modrdn
+and
+.BR unknown .
+The latter should only be used as a fall-thru in case of unhandled
+request types that are considered equivalent to a write operation.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.1.29
+ NAME 'lastmodType'
+ DESC 'Type of last modification'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ EQUALITY caseIgnoreMatch
+ SINGLE-VALUE
+ NO-USER-MODIFICATION
+ USAGE directoryOperation )
+.RE
+
+
+.SH EXAMPLES
+.LP
+.RS
+.nf
+database mdb
+suffix dc=example,dc=com
+\...
+overlay lastmod
+lastmod-rdnvalue "Last Modification"
+.fi
+.RE
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd.conf (5),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH BUGS
+It is unclear whether this overlay can safely interoperate
+with other overlays.
+If the underlying backend does not implement entry_get/entry_release
+handlers, modrdn update can become tricky.
+The code needs some cleanup and more consistent error handling.
+So far, the OIDs for the schema haven't been assigned yet.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2004 by Pierangelo Masarati in fulfillment
+of requirements from SysNet s.n.c.; this man page has been copied
+from
+.BR slapo-ppolicy (5),
+and most of the overlays ever written are copied from Howard Chu's
+first overlays.
+.P
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/noopsrch/Makefile b/contrib/slapd-modules/noopsrch/Makefile
new file mode 100644
index 0000000..2934a32
--- /dev/null
+++ b/contrib/slapd-modules/noopsrch/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2 -Wall
+DEFS = -DSLAPD_OVER_NOOPSRCH=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = noopsrch.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+noopsrch.la: noopsrch.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/noopsrch/noopsrch.c b/contrib/slapd-modules/noopsrch/noopsrch.c
new file mode 100644
index 0000000..24f0f53
--- /dev/null
+++ b/contrib/slapd-modules/noopsrch/noopsrch.c
@@ -0,0 +1,255 @@
+/* noopsrch.c - LDAP Control that counts entries a search would return */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_NOOPSRCH=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_NOOPSRCH
+
+/*
+ * Control OID
+ */
+#define LDAP_CONTROL_X_NOOPSRCH "1.3.6.1.4.1.4203.666.5.18"
+
+#include "slap.h"
+#include "ac/string.h"
+
+#define o_noopsrch o_ctrlflag[noopsrch_cid]
+#define o_ctrlnoopsrch o_controls[noopsrch_cid]
+
+static int noopsrch_cid;
+static slap_overinst noopsrch;
+
+static int
+noopsrch_parseCtrl (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_noopsrch != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "No-op Search control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "No-op Search control value is present";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_ctrlnoopsrch = (void *)NULL;
+
+ op->o_noopsrch = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ return rs->sr_err;
+}
+
+int dummy;
+
+typedef struct noopsrch_cb_t {
+ slap_overinst *nc_on;
+ ber_int_t nc_nentries;
+ ber_int_t nc_nsearchref;
+ AttributeName *nc_save_attrs;
+ int *nc_pdummy;
+ int nc_save_slimit;
+} noopsrch_cb_t;
+
+static int
+noopsrch_response( Operation *op, SlapReply *rs )
+{
+ noopsrch_cb_t *nc = (noopsrch_cb_t *)op->o_callback->sc_private;
+
+ /* if the control is global, limits are not computed yet */
+ if ( nc->nc_pdummy == &dummy ) {
+ nc->nc_save_slimit = op->ors_slimit;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ nc->nc_pdummy = NULL;
+ }
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ nc->nc_nentries++;
+#ifdef NOOPSRCH_DEBUG
+ Debug( LDAP_DEBUG_TRACE, "noopsrch_response(REP_SEARCH): nentries=%d\n", nc->nc_nentries );
+#endif
+ return 0;
+
+ } else if ( rs->sr_type == REP_SEARCHREF ) {
+ nc->nc_nsearchref++;
+ return 0;
+
+ } else if ( rs->sr_type == REP_RESULT ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ struct berval ctrlval;
+ LDAPControl *ctrl, *ctrlsp[2];
+ int rc = rs->sr_err;
+
+ if ( nc->nc_save_slimit >= 0 && nc->nc_nentries >= nc->nc_save_slimit ) {
+ rc = LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+#ifdef NOOPSRCH_DEBUG
+ Debug( LDAP_DEBUG_TRACE, "noopsrch_response(REP_RESULT): err=%d nentries=%d nref=%d\n", rc, nc->nc_nentries, nc->nc_nsearchref );
+#endif
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ ber_printf( ber, "{iii}", rc, nc->nc_nentries, nc->nc_nsearchref );
+ if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+ ber_free_buf( ber );
+ if ( op->o_noopsrch == SLAP_CONTROL_CRITICAL ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ ctrl = op->o_tmpcalloc( 1,
+ sizeof( LDAPControl ) + ctrlval.bv_len + 1,
+ op->o_tmpmemctx );
+ ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
+ ctrl->ldctl_oid = LDAP_CONTROL_X_NOOPSRCH;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_len = ctrlval.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
+ ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
+
+ ber_free_buf( ber );
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+noopsrch_cleanup( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
+ noopsrch_cb_t *nc = (noopsrch_cb_t *)op->o_callback->sc_private;
+ op->ors_attrs = nc->nc_save_attrs;
+ if ( nc->nc_pdummy == NULL ) {
+ op->ors_slimit = nc->nc_save_slimit;
+ }
+
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+noopsrch_op_search( Operation *op, SlapReply *rs )
+{
+ if ( op->o_noopsrch != SLAP_CONTROL_NONE ) {
+ slap_callback *sc;
+ noopsrch_cb_t *nc;
+
+ sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( noopsrch_cb_t ), op->o_tmpmemctx );
+
+ nc = (noopsrch_cb_t *)&sc[ 1 ];
+ nc->nc_on = (slap_overinst *)op->o_bd->bd_info;
+ nc->nc_nentries = 0;
+ nc->nc_nsearchref = 0;
+ nc->nc_save_attrs = op->ors_attrs;
+ nc->nc_pdummy = &dummy;
+
+ sc->sc_response = noopsrch_response;
+ sc->sc_cleanup = noopsrch_cleanup;
+ sc->sc_private = (void *)nc;
+
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ sc->sc_next = op->o_callback->sc_next;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int noopsrch_cnt;
+
+static int
+noopsrch_db_init( BackendDB *be, ConfigReply *cr)
+{
+ if ( noopsrch_cnt++ == 0 ) {
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_X_NOOPSRCH,
+ SLAP_CTRL_SEARCH | SLAP_CTRL_GLOBAL_SEARCH, NULL,
+ noopsrch_parseCtrl, &noopsrch_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "noopsrch_initialize: Failed to register control '%s' (%d)\n",
+ LDAP_CONTROL_X_NOOPSRCH, rc );
+ return rc;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+noopsrch_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ assert( noopsrch_cnt > 0 );
+
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_X_NOOPSRCH );
+ if ( --noopsrch_cnt == 0 ) {
+ unregister_supported_control( LDAP_CONTROL_X_NOOPSRCH );
+ }
+
+#endif /* SLAP_CONFIG_DELETE */
+
+ return 0;
+}
+
+#if SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC */
+int
+noopsrch_initialize( void )
+{
+
+ noopsrch.on_bi.bi_type = "noopsrch";
+
+ noopsrch.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ noopsrch.on_bi.bi_db_init = noopsrch_db_init;
+ noopsrch.on_bi.bi_db_destroy = noopsrch_db_destroy;
+ noopsrch.on_bi.bi_op_search = noopsrch_op_search;
+
+ return overlay_register( &noopsrch );
+}
+
+#if SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return noopsrch_initialize();
+}
+#endif /* SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_NOOPSRCH */
diff --git a/contrib/slapd-modules/nops/Makefile b/contrib/slapd-modules/nops/Makefile
new file mode 100644
index 0000000..94a5bcd
--- /dev/null
+++ b/contrib/slapd-modules/nops/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_NOPS=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = nops.la
+MANPAGES = slapo-nops.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+nops.la: nops.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/nops/nops.c b/contrib/slapd-modules/nops/nops.c
new file mode 100644
index 0000000..6dffb6b
--- /dev/null
+++ b/contrib/slapd-modules/nops/nops.c
@@ -0,0 +1,178 @@
+/* nops.c - Overlay to filter idempotent operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Copyright 2008 Emmanuel Dreyfus.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the Emmanuel Dreyfus for
+ * inclusion in OpenLDAP Software.
+ */
+#include "portable.h"
+
+#ifdef SLAPD_OVER_NOPS
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+static ConfigDriver nops_cf_gen;
+
+static int nops_cf_gen( ConfigArgs *c ) { return 0; }
+
+static void
+nops_rm_mod( Modifications **mods, Modifications *mod ) {
+ Modifications *next, *m;
+
+ next = mod->sml_next;
+ if (*mods == mod) {
+ *mods = next;
+ } else {
+ Modifications *m;
+
+ for (m = *mods; m; m = m->sml_next) {
+ if (m->sml_next == mod) {
+ m->sml_next = next;
+ break;
+ }
+ }
+ }
+
+ mod->sml_next = NULL;
+ slap_mods_free(mod, 1);
+
+ return;
+}
+
+static int
+nops_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ Backend *be = op->o_bd;
+ Entry *target_entry = NULL;
+ Modifications *m;
+ int rc;
+
+ if ((m = op->orm_modlist) == NULL) {
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "nops() got null orm_modlist");
+ return(rs->sr_err);
+ }
+
+ op->o_bd = on->on_info->oi_origdb;
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &target_entry);
+ op->o_bd = be;
+
+ if (rc != 0 || target_entry == NULL)
+ return 0;
+
+ /*
+ * For each attribute modification, check if the
+ * modification and the old entry are the same.
+ */
+ while (m) {
+ int i, j;
+ int found;
+ Attribute *a;
+ BerVarray bm;
+ BerVarray bt;
+ Modifications *mc;
+
+ mc = m;
+ m = m->sml_next;
+
+ /* Check only replace sub-operations */
+ if ((mc->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE)
+ continue;
+
+ /* If there is no values, skip */
+ if (((bm = mc->sml_values ) == NULL ) || (bm[0].bv_val == NULL))
+ continue;
+
+ /* If the attribute does not exist in old entry, skip */
+ if ((a = attr_find(target_entry->e_attrs, mc->sml_desc)) == NULL)
+ continue;
+ if ((bt = a->a_vals) == NULL)
+ continue;
+
+ /* For each value replaced, do we find it in old entry? */
+ found = 0;
+ for (i = 0; bm[i].bv_val; i++) {
+ for (j = 0; bt[j].bv_val; j++) {
+ if (bm[i].bv_len != bt[j].bv_len)
+ continue;
+ if (memcmp(bm[i].bv_val, bt[j].bv_val, bt[j].bv_len) != 0)
+ continue;
+
+ found++;
+ break;
+ }
+ }
+
+ /* Did we find as many values as we had in old entry? */
+ if (i != a->a_numvals || found != a->a_numvals)
+ continue;
+
+ /* This is a nop, remove it */
+ Debug(LDAP_DEBUG_TRACE, "removing nop on %s",
+ a->a_desc->ad_cname.bv_val );
+
+ nops_rm_mod(&op->orm_modlist, mc);
+ }
+ if (target_entry) {
+ op->o_bd = on->on_info->oi_origdb;
+ be_entry_release_r(op, target_entry);
+ op->o_bd = be;
+ }
+
+ if ((m = op->orm_modlist) == NULL) {
+ slap_callback *cb = op->o_callback;
+
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ op->o_callback = NULL;
+ send_ldap_error(op, rs, LDAP_SUCCESS, "");
+ op->o_callback = cb;
+
+ return (rs->sr_err);
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst nops_ovl;
+
+#if SLAPD_OVER_NOPS == SLAPD_MOD_DYNAMIC
+static
+#endif
+int
+nops_initialize( void ) {
+ nops_ovl.on_bi.bi_type = "nops";
+ nops_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ nops_ovl.on_bi.bi_op_modify = nops_modify;
+ return overlay_register( &nops_ovl );
+}
+
+#if SLAPD_OVER_NOPS == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return nops_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_NOPS) */
+
diff --git a/contrib/slapd-modules/nops/slapo-nops.5 b/contrib/slapd-modules/nops/slapo-nops.5
new file mode 100644
index 0000000..c27915e
--- /dev/null
+++ b/contrib/slapd-modules/nops/slapo-nops.5
@@ -0,0 +1,32 @@
+.TH SLAPO-NOPS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2008 Emmanuel Dreyfus
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-nops \- Remove Null Operations Overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+Some broken client tend to implement modifications as replace operations
+where all attributes are replaced, most of the time by the same values
+they had before. This can cause undesirable load on logs, ACL evaluation,
+or replication traffic.
+
+This overlay detects idempotent replace operations and filter them out.
+.SH CONFIGURATION
+This overlay had no specific configuration.
+.SH EXAMPLES
+.LP
+.RS
+.nf
+overlay nops
+.RE
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
+.SH ACKNOWLEDGEMENTS
+This module was written in 2008 by Emmanuel Dreyfus.
+.so ../Project
diff --git a/contrib/slapd-modules/nssov/Makefile b/contrib/slapd-modules/nssov/Makefile
new file mode 100644
index 0000000..13987c2
--- /dev/null
+++ b/contrib/slapd-modules/nssov/Makefile
@@ -0,0 +1,86 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2008-2022 The OpenLDAP Foundation.
+# Portions Copyright 2008 Howard Chu, Symas Corp. 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>.
+
+# Path to the OpenLDAP source tree
+LDAP_SRC=../../..
+
+# Path to the OpenLDAP object tree - same as above unless
+# you're doing out-of-tree builds.
+LDAP_BUILD=$(LDAP_SRC)
+
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+NLDAPD_INC=-Inss-pam-ldapd
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+OPT = -g -O2
+CC = gcc
+DEFS =
+INCS = $(LDAP_INC) $(NLDAPD_INC)
+LIBS = $(LDAP_LIB)
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+sysconfdir = $(prefix)/etc$(ldap_subdir)
+schemadir = $(sysconfdir)/schema
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: nssov.la
+
+XOBJS = tio.lo
+
+OBJS = alias.lo ether.lo group.lo host.lo netgroup.lo network.lo \
+ nssov.lo passwd.lo protocol.lo rpc.lo service.lo shadow.lo pam.lo
+
+MANPAGES = slapo-nssov.5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+tio.lo: nss-pam-ldapd/tio.c
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $?
+
+$(OBJS): nssov.h
+
+nssov.la: $(OBJS) $(XOBJS)
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info 0:0:0 \
+ -rpath $(moduledir) -module -o $@ $(OBJS) $(XOBJS) $(LIBS)
+
+install: install-lib install-man FORCE
+
+install-lib: nssov.la
+ mkdir -p $(DESTDIR)$(moduledir)
+ $(LIBTOOL) --mode=install cp nssov.la $(DESTDIR)$(moduledir)
+ cp ldapns.schema $(DESTDIR)$(schemadir)
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
+clean:
+ rm -f *.*o *.la .libs/*
+ rm -rf .libs
diff --git a/contrib/slapd-modules/nssov/README b/contrib/slapd-modules/nssov/README
new file mode 100644
index 0000000..af8631e
--- /dev/null
+++ b/contrib/slapd-modules/nssov/README
@@ -0,0 +1,134 @@
+This directory contains a slapd overlay, nssov, that handles
+NSS lookup requests through a local Unix Domain socket. It uses the
+same IPC protocol as Arthur de Jong's nss-ldapd, and a complete
+copy of the nss-ldapd source is included here. It also handles
+PAM requests.
+
+To use this code, you will need the client-side stuf library from
+nss-pam-ldapd. You can get it from:
+http://arthurdejong.org/nss-pam-ldapd
+You will not need the nslcd daemon; this overlay replaces that part.
+To disable building of the nslcd daemon in nss-pam-ldapd, add the
+--disable-nslcd option to the nss-pam-ldapd configure script. You
+should already be familiar with the RFC2307 and RFC2307bis schema
+to use this overlay. See the nss-pam-ldapd README for more information
+on the schema and which features are supported.
+
+To use the overlay, add:
+
+ include <path to>nis.schema
+
+ moduleload <path to>nssov.so
+ ...
+
+ database mdb
+ ...
+ overlay nssov
+
+to your slapd configuration file. (The nis.schema file contains
+the original RFC2307 schema. Some modifications will be needed to
+use RFC2307bis.)
+
+The overlay may be configured with Service Search Descriptors (SSDs)
+for each NSS service that will be used. SSDs are configured using
+
+ nssov-ssd <service> <url>
+
+where the <service> may be one of
+ aliases
+ ethers
+ group
+ hosts
+ netgroup
+ networks
+ passwd
+ protocols
+ rpc
+ services
+ shadow
+
+and the <url> must be of the form
+ ldap:///[<basedn>][??[<scope>][?<filter>]]
+
+The <basedn> will default to the first suffix of the current database.
+The <scope> defaults to "subtree". The default <filter> depends on which
+service is being used.
+
+If the local database is actually a proxy to a foreign LDAP server, some
+mapping of schema may be needed. Some simple attribute substitutions may
+be performed using
+
+ nssov-map <service> <orig> <new>
+
+See the nss-ldapd/README for the original attribute names used in this code.
+
+The overlay also supports dynamic configuration in cn=config. The layout
+of the config entry is
+
+ dn: olcOverlay={0}nssov,olcDatabase={1}mdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcNssOvConfig
+ olcOverlay: {0}nssov
+ olcNssSsd: passwd ldap:///ou=users,dc=example,dc=com??one
+ olcNssMap: passwd uid accountName
+
+which enables the passwd service, and uses the accountName attribute to
+fetch what is usually retrieved from the uid attribute.
+
+PAM authentication, account management, session management, and password
+management are supported.
+
+Authentication is performed using Simple Binds. Since all operations occur
+inside the slapd overlay, "fake" connections are used and they are
+inherently secure. Two methods of mapping the PAM username to an LDAP DN
+are provided:
+ the mapping can be accomplished using slapd's authz-regexp facility. In
+this case, a DN of the form
+ cn=<service>+uid=<user>,cn=<hostname>,cn=pam,cn=auth
+is fed into the regexp matcher. If a match is produced, the resulting DN
+is used.
+ otherwise, the NSS passwd map is invoked (which means it must already
+be configured).
+
+If no DN is found, the overlay returns PAM_USER_UNKNOWN. If the DN is
+found, and Password Policy is supported, then the Bind will use the
+Password Policy control and return expiration information to PAM.
+
+Account management also uses two methods. These methods depend on the
+ldapns.schema included with the nssov source.
+ The first is identical to the method used in PADL's pam_ldap module:
+host and authorizedService attributes may be looked up in the user's entry,
+and checked to determine access. Also a check may be performed to see if
+the user is a member of a particular group. This method is pretty
+inflexible and doesn't scale well to large networks of users, hosts,
+and services.
+ The second uses slapd's ACL engine to check if the user has "compare"
+privilege on an ipHost object whose name matches the current hostname, and
+whose authorizedService attribute matches the current service name. This
+method is preferred, since it allows authorization to be centralized in
+the ipHost entries instead of scattered across the entire user population.
+The ipHost entries must have an authorizedService attribute (e.g. by way
+of the authorizedServiceObject auxiliary class) to use this method.
+
+Session management: the overlay may optionally add a "logged in" attribute
+to a user's entry for successful logins, and delete the corresponding
+value upon logout. The attribute value is of the form
+ <generalizedTime> <host> <service> <tty> (<ruser@rhost>)
+
+Password management: the overlay will perform a PasswordModify exop
+in the server for the given user.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright 2008-2009 Howard Chu, Symas Corp. 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>.
+
diff --git a/contrib/slapd-modules/nssov/alias.c b/contrib/slapd-modules/nssov/alias.c
new file mode 100644
index 0000000..ae131db
--- /dev/null
+++ b/contrib/slapd-modules/nssov/alias.c
@@ -0,0 +1,116 @@
+/* alias.c - mail alias lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* Vendor-specific attributes and object classes.
+ * (Mainly from Sun.)
+ * ( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' SUP top STRUCTURAL
+ * DESC 'NIS mail alias'
+ * MUST cn
+ * MAY rfc822MailMember )
+ */
+
+/* the basic search filter for searches */
+static struct berval alias_filter = BER_BVC("(objectClass=nisMailAlias)");
+
+/* the attributes to request with searches */
+static struct berval alias_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("rfc822MailMember"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(alias)
+
+NSSOV_CBPRIV(alias,
+ struct berval name;
+ char buf[256];);
+
+static int write_alias(nssov_alias_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32,tmp2int32,tmp3int32;
+ struct berval tmparr[2], empty;
+ struct berval *names, *members;
+ Attribute *a;
+ int i;
+
+ /* get the name of the alias */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[0].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"alias entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the members of the alias */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[1].an_desc);
+ if ( !a ) {
+ BER_BVZERO( &empty );
+ members = &empty;
+ } else {
+ members = a->a_vals;
+ }
+ /* for each name, write an entry */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BVARRAY(cbp->fp,members);
+ }
+ return 0;
+}
+
+NSSOV_CB(alias)
+
+NSSOV_HANDLE(
+ alias,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_alias_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_ALIAS_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ alias,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG,"nssov_alias_all()\n");,
+ NSLCD_ACTION_ALIAS_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/ether.c b/contrib/slapd-modules/nssov/ether.c
new file mode 100644
index 0000000..cb18f1b
--- /dev/null
+++ b/contrib/slapd-modules/nssov/ether.c
@@ -0,0 +1,167 @@
+/* ether.c - ethernet address lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+struct ether_addr {
+ uint8_t ether_addr_octet[6];
+};
+
+/* ( nisSchema.2.11 NAME 'ieee802Device' SUP top AUXILIARY
+ * DESC 'A device with a MAC address; device SHOULD be
+ * used as a structural class'
+ * MAY macAddress )
+ */
+
+/* the basic search filter for searches */
+static struct berval ether_filter = BER_BVC("(objectClass=ieee802Device)");
+
+/* the attributes to request with searches */
+static struct berval ether_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("macAddress"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(ether)
+
+NSSOV_CBPRIV(ether,
+ char buf[256];
+ struct berval name;
+ struct berval addr;);
+
+#define WRITE_ETHER(fp,addr) \
+ {int ao[6]; \
+ sscanf(addr.bv_val,"%02x:%02x:%02x:%02x:%02x:%02x", \
+ &ao[0], &ao[1], &ao[2], &ao[3], &ao[4], &ao[5] );\
+ tmpaddr.ether_addr_octet[0] = ao[0]; \
+ tmpaddr.ether_addr_octet[1] = ao[1]; \
+ tmpaddr.ether_addr_octet[2] = ao[2]; \
+ tmpaddr.ether_addr_octet[3] = ao[3]; \
+ tmpaddr.ether_addr_octet[4] = ao[4]; \
+ tmpaddr.ether_addr_octet[5] = ao[5]; } \
+ WRITE(fp,&tmpaddr,sizeof(uint8_t[6]));
+
+static int write_ether(nssov_ether_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ struct ether_addr tmpaddr;
+ struct berval tmparr[2];
+ struct berval *names,*ethers;
+ Attribute *a;
+ int i,j;
+
+ /* get the name of the ether entry */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[0].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the addresses */
+ if (BER_BVISNULL(&cbp->addr))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[1].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ ethers = a->a_vals;
+ /* TODO: move parsing of addresses up here */
+ }
+ else
+ {
+ ethers=tmparr;
+ ethers[0]=cbp->addr;
+ BER_BVZERO(&ethers[1]);
+ }
+ /* write entries for all names and addresses */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ for (j=0;!BER_BVISNULL(&ethers[j]);j++)
+ {
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_ETHER(cbp->fp,ethers[j]);
+ }
+ return 0;
+}
+
+NSSOV_CB(ether)
+
+NSSOV_HANDLE(
+ ether,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.addr);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_ether_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_ETHER_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ ether,byether,
+ struct ether_addr addr;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.name);
+ READ(fp,&addr,sizeof(uint8_t[6]));
+ cbp.addr.bv_len = snprintf(cbp.buf,sizeof(cbp.buf), "%x:%x:%x:%x:%x:%x",
+ addr.ether_addr_octet[0],
+ addr.ether_addr_octet[1],
+ addr.ether_addr_octet[2],
+ addr.ether_addr_octet[3],
+ addr.ether_addr_octet[4],
+ addr.ether_addr_octet[5]);
+ cbp.addr.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_ether_byether(%s)\n",cbp.addr.bv_val);,
+ NSLCD_ACTION_ETHER_BYETHER,
+ nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
+)
+
+NSSOV_HANDLE(
+ ether,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.addr);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_ether_all()\n");,
+ NSLCD_ACTION_ETHER_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/group.c b/contrib/slapd-modules/nssov/group.c
new file mode 100644
index 0000000..1d93451
--- /dev/null
+++ b/contrib/slapd-modules/nssov/group.c
@@ -0,0 +1,346 @@
+/* group.c - group lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008-2009 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* for gid_t */
+#include <grp.h>
+
+/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL
+ * DESC 'Abstraction of a group of accounts'
+ * MUST ( cn $ gidNumber )
+ * MAY ( userPassword $ memberUid $ description ) )
+ *
+ * apart from that the above the uniqueMember attributes may be
+ * supported in a coming release (they map to DNs, which is an extra
+ * lookup step)
+ *
+ * using nested groups (groups that are member of a group) is currently
+ * not supported, this may be added in a later release
+ */
+
+/* the basic search filter for searches */
+static struct berval group_filter = BER_BVC("(objectClass=posixGroup)");
+
+/* the attributes to request with searches */
+static struct berval group_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("userPassword"),
+ BER_BVC("gidNumber"),
+ BER_BVC("memberUid"),
+ BER_BVC("uniqueMember"),
+ BER_BVNULL
+};
+
+#define CN_KEY 0
+#define PWD_KEY 1
+#define GID_KEY 2
+#define UID_KEY 3
+#define MEM_KEY 4
+
+/* default values for attributes */
+static struct berval default_group_userPassword = BER_BVC("*"); /* unmatchable */
+
+NSSOV_CBPRIV(group,
+ nssov_info *ni;
+ char buf[256];
+ struct berval name;
+ struct berval gidnum;
+ struct berval user;
+ int wantmembers;);
+
+/* create a search filter for searching a group entry
+ by member uid, return -1 on errors */
+static int mkfilter_group_bymember(nssov_group_cbp *cbp,struct berval *buf)
+{
+ struct berval dn;
+ /* try to translate uid to DN */
+ nssov_uid2dn(cbp->op,cbp->ni,&cbp->user,&dn);
+ if (BER_BVISNULL(&dn)) {
+ if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ cbp->mi->mi_filter.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,
+ cbp->user.bv_val );
+ } else { /* also lookup using user DN */
+ if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len +
+ dn.bv_len + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_len + 12 > buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(|(%s=%s)(%s=%s)))",
+ cbp->mi->mi_filter.bv_val,
+ cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, cbp->user.bv_val,
+ cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_val, dn.bv_val );
+ }
+ return 0;
+}
+
+NSSOV_INIT(group)
+
+/*
+ Checks to see if the specified name is a valid group name.
+
+ This test is based on the definition from POSIX (IEEE Std 1003.1, 2004,
+ 3.189 Group Name and 3.276 Portable Filename Character Set):
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+ The standard defines group names valid if they only contain characters from
+ the set [A-Za-z0-9._-] where the hyphen should not be used as first
+ character.
+*/
+static int isvalidgroupname(struct berval *name)
+{
+ int i;
+
+ if ( !name->bv_val || !name->bv_len )
+ return 0;
+ /* check first character */
+ if ( ! ( (name->bv_val[0]>='A' && name->bv_val[0] <= 'Z') ||
+ (name->bv_val[0]>='a' && name->bv_val[0] <= 'z') ||
+ (name->bv_val[0]>='0' && name->bv_val[0] <= '9') ||
+ name->bv_val[0]=='.' || name->bv_val[0]=='_' ) )
+ return 0;
+ /* check other characters */
+ for (i=1;i<name->bv_len;i++)
+ {
+#ifndef STRICT_GROUPS
+ /* allow spaces too */
+ if (name->bv_val[i] == ' ') continue;
+#endif
+ if ( ! ( (name->bv_val[i]>='A' && name->bv_val[i] <= 'Z') ||
+ (name->bv_val[i]>='a' && name->bv_val[i] <= 'z') ||
+ (name->bv_val[i]>='0' && name->bv_val[i] <= '9') ||
+ name->bv_val[i]=='.' || name->bv_val[i]=='_' || name->bv_val[i]=='-') )
+ return 0;
+ }
+ /* no test failed so it must be good */
+ return -1;
+}
+
+static int write_group(nssov_group_cbp *cbp,Entry *entry)
+{
+ struct berval tmparr[2], tmpgid[2];
+ struct berval *names,*gids,*members;
+ struct berval passwd = {0};
+ Attribute *a;
+ int i,j,nummembers,rc = 0;
+
+ /* get group name (cn) */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the group id(s) */
+ if (BER_BVISNULL(&cbp->gidnum))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GID_KEY].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ gids = a->a_vals;
+ }
+ else
+ {
+ gids=tmpgid;
+ gids[0]=cbp->gidnum;
+ BER_BVZERO(&gids[1]);
+ }
+ /* get group passwd (userPassword) (use only first entry) */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
+ if (a)
+ get_userpassword(&a->a_vals[0], &passwd);
+ if (BER_BVISNULL(&passwd))
+ passwd=default_group_userPassword;
+ /* get group members (memberUid&uniqueMember) */
+ if (cbp->wantmembers) {
+ Attribute *b;
+ i = 0; j = 0;
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
+ b = attr_find(entry->e_attrs, cbp->mi->mi_attrs[MEM_KEY].an_desc);
+ if ( a )
+ i += a->a_numvals;
+ if ( b )
+ i += b->a_numvals;
+ if ( i ) {
+ members = cbp->op->o_tmpalloc( (i+1) * sizeof(struct berval), cbp->op->o_tmpmemctx );
+
+ if ( a ) {
+ for (i=0; i<a->a_numvals; i++) {
+ if (isvalidusername(&a->a_vals[i])) {
+ ber_dupbv_x(&members[j],&a->a_vals[i],cbp->op->o_tmpmemctx);
+ j++;
+ }
+ }
+ }
+ a = b;
+ if ( a ) {
+ for (i=0; i<a->a_numvals; i++) {
+ if (nssov_dn2uid(cbp->op,cbp->ni,&a->a_nvals[i],&members[j]))
+ j++;
+ }
+ }
+ nummembers = j;
+ BER_BVZERO(&members[j]);
+ } else {
+ members=NULL;
+ nummembers = 0;
+ }
+
+ } else {
+ members=NULL;
+ nummembers = 0;
+ }
+ /* write entries for all names and gids */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ if (!isvalidgroupname(&names[i]))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"\n",
+ entry->e_name.bv_val,names[i].bv_val );
+ }
+ else
+ {
+ for (j=0;!BER_BVISNULL(&gids[j]);j++)
+ {
+ char *tmp;
+ int tmpint32;
+ gid_t gid;
+ gid = strtol(gids[j].bv_val, &tmp, 0);
+ if ( *tmp ) {
+ Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,
+ names[i].bv_val);
+ continue;
+ }
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BERVAL(cbp->fp,&passwd);
+ WRITE_INT32(cbp->fp,gid);
+ /* write a list of values */
+ WRITE_INT32(cbp->fp,nummembers);
+ if (nummembers)
+ {
+ int k;
+ for (k=0;k<nummembers;k++) {
+ WRITE_BERVAL(cbp->fp,&members[k]);
+ }
+ }
+ }
+ }
+ }
+ /* free and return */
+ if (members!=NULL)
+ ber_bvarray_free_x( members, cbp->op->o_tmpmemctx );
+ return rc;
+}
+
+NSSOV_CB(group)
+
+NSSOV_HANDLE(
+ group,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ if (!isvalidgroupname(&cbp.name)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name\n",cbp.name.bv_val);
+ return -1;
+ }
+ cbp.wantmembers = 1;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.gidnum);
+ BER_BVZERO(&cbp.user);,
+ Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_GROUP_BYNAME,
+ nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ group,bygid,
+ gid_t gid;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,gid);
+ cbp.gidnum.bv_val = cbp.buf;
+ cbp.gidnum.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",gid);
+ cbp.wantmembers = 1;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.user);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)\n",cbp.gidnum.bv_val);,
+ NSLCD_ACTION_GROUP_BYGID,
+ nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter)
+)
+
+NSSOV_HANDLE(
+ group,bymember,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.user.bv_len = tmpint32;
+ cbp.user.bv_val = cbp.buf;
+ if (!isvalidusername(&cbp.user)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name\n",cbp.user.bv_val);
+ return -1;
+ }
+ cbp.wantmembers = 0;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.gidnum);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)\n",cbp.user.bv_val);,
+ NSLCD_ACTION_GROUP_BYMEMBER,
+ mkfilter_group_bymember(&cbp,&filter)
+)
+
+NSSOV_HANDLE(
+ group,all,
+ struct berval filter;
+ /* no parameters to read */
+ cbp.wantmembers = 1;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.gidnum);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n");,
+ NSLCD_ACTION_GROUP_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/host.c b/contrib/slapd-modules/nssov/host.c
new file mode 100644
index 0000000..008b454
--- /dev/null
+++ b/contrib/slapd-modules/nssov/host.c
@@ -0,0 +1,161 @@
+/* host.c - host lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.6 NAME 'ipHost' SUP top AUXILIARY
+ * DESC 'Abstraction of a host, an IP device. The distinguished
+ * value of the cn attribute denotes the host's canonical
+ * name. Device SHOULD be used as a structural class'
+ * MUST ( cn $ ipHostNumber )
+ * MAY ( l $ description $ manager ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval host_filter = BER_BVC("(objectClass=ipHost)");
+
+/* the attributes to request with searches */
+static struct berval host_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipHostNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(host)
+
+NSSOV_CBPRIV(host,
+ char buf[1024];
+ struct berval name;
+ struct berval addr;);
+
+/* write a single host entry to the stream */
+static int write_host(nssov_host_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int numaddr,i,numname,dupname;
+ struct berval name,*names,*addrs;
+ Attribute *a;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the host */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the addresses */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ addrs = a->a_vals;
+ numaddr = a->a_numvals;
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,numaddr);
+ for (i=0;i<numaddr;i++)
+ {
+ WRITE_ADDRESS(cbp->fp,&addrs[i]);
+ }
+ return 0;
+}
+
+NSSOV_CB(host)
+
+NSSOV_HANDLE(
+ host,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.addr);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_host_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_HOST_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ host,byaddr,
+ int af;
+ char addr[64];
+ int len=sizeof(addr);
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.name);
+ READ_ADDRESS(fp,addr,len,af);
+ /* translate the address to a string */
+ if (inet_ntop(af,addr,cbp.buf,sizeof(cbp.buf))==NULL)
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string\n");
+ return -1;
+ }
+ cbp.addr.bv_val = cbp.buf;
+ cbp.addr.bv_len = strlen(cbp.buf);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_host_byaddr(%s)\n",cbp.addr.bv_val);,
+ NSLCD_ACTION_HOST_BYADDR,
+ nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
+)
+
+NSSOV_HANDLE(
+ host,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.addr);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_host_all()\n");,
+ NSLCD_ACTION_HOST_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/ldapns.schema b/contrib/slapd-modules/nssov/ldapns.schema
new file mode 100644
index 0000000..f6f7c9f
--- /dev/null
+++ b/contrib/slapd-modules/nssov/ldapns.schema
@@ -0,0 +1,38 @@
+# $OpenLDAP$
+# $Id: ldapns.schema,v 1.3 2009-10-01 19:17:20 tedcheng Exp $
+# LDAP Name Service Additional Schema
+# http://www.iana.org/assignments/gssapi-service-names
+
+#
+# Not part of the distribution: this is a workaround!
+#
+
+attributetype ( 1.3.6.1.4.1.5322.17.2.1 NAME 'authorizedService'
+ DESC 'IANA GSS-API authorized service name'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+attributetype ( 1.3.6.1.4.1.5322.17.2.2 NAME 'loginStatus'
+ DESC 'Currently logged in sessions for a user'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ ORDERING caseIgnoreOrderingMatch
+ SYNTAX OMsDirectoryString )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.1 NAME 'authorizedServiceObject'
+ DESC 'Auxiliary object class for adding authorizedService attribute'
+ SUP top
+ AUXILIARY
+ MAY authorizedService )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.2 NAME 'hostObject'
+ DESC 'Auxiliary object class for adding host attribute'
+ SUP top
+ AUXILIARY
+ MAY host )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.3 NAME 'loginStatusObject'
+ DESC 'Auxiliary object class for login status attribute'
+ SUP top
+ AUXILIARY
+ MAY loginStatus )
diff --git a/contrib/slapd-modules/nssov/netgroup.c b/contrib/slapd-modules/nssov/netgroup.c
new file mode 100644
index 0000000..7211a9a
--- /dev/null
+++ b/contrib/slapd-modules/nssov/netgroup.c
@@ -0,0 +1,199 @@
+/* netgroup.c - netgroup lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+#include <ac/ctype.h>
+
+/* ( nisSchema.2.8 NAME 'nisNetgroup' SUP top STRUCTURAL
+ * DESC 'Abstraction of a netgroup. May refer to other netgroups'
+ * MUST cn
+ * MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval netgroup_filter = BER_BVC("(objectClass=nisNetgroup)");
+
+/* the attributes to request with searches */
+static struct berval netgroup_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("nisNetgroupTriple"),
+ BER_BVC("memberNisNetgroup"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(netgroup)
+
+NSSOV_CBPRIV(netgroup,
+ char buf[256];
+ struct berval name;);
+
+static int write_string_stripspace_len(TFILE *fp,const char *str,int len)
+{
+ int32_t tmpint32;
+ int i,j;
+ DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"",str);
+ if (str==NULL)
+ {
+ WRITE_INT32(fp,0);
+ }
+ else
+ {
+ /* skip leading spaces */
+ for (i=0;(str[i]!='\0')&&(isspace(str[i]));i++)
+ /* nothing else to do */ ;
+ /* skip trailing spaces */
+ for (j=len;(j>i)&&(isspace(str[j-1]));j--)
+ /* nothing else to do */ ;
+ /* write length of string */
+ WRITE_INT32(fp,j-i);
+ /* write string itself */
+ if (j>i)
+ {
+ WRITE(fp,str+i,j-i);
+ }
+ }
+ /* we're done */
+ return 0;
+}
+
+#define WRITE_STRING_STRIPSPACE_LEN(fp,str,len) \
+ if (write_string_stripspace_len(fp,str,len)) \
+ return -1;
+
+#define WRITE_STRING_STRIPSPACE(fp,str) \
+ WRITE_STRING_STRIPSPACE_LEN(fp,str,strlen(str))
+
+static int write_netgroup_triple(TFILE *fp,const char *triple)
+{
+ int32_t tmpint32;
+ int i;
+ int hostb,hoste,userb,usere,domainb,domaine;
+ /* skip leading spaces */
+ for (i=0;(triple[i]!='\0')&&(isspace(triple[i]));i++)
+ /* nothing else to do */ ;
+ /* we should have a bracket now */
+ if (triple[i]!='(')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): entry does not begin with '(' (entry skipped)\n" );
+ return 0;
+ }
+ i++;
+ hostb=i;
+ /* find comma (end of host string) */
+ for (;(triple[i]!='\0')&&(triple[i]!=',');i++)
+ /* nothing else to do */ ;
+ if (triple[i]!=',')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n" );
+ return 0;
+ }
+ hoste=i;
+ i++;
+ userb=i;
+ /* find comma (end of user string) */
+ for (;(triple[i]!='\0')&&(triple[i]!=',');i++)
+ /* nothing else to do */ ;
+ if (triple[i]!=',')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n" );
+ return 0;
+ }
+ usere=i;
+ i++;
+ domainb=i;
+ /* find closing bracket (end of domain string) */
+ for (;(triple[i]!='\0')&&(triple[i]!=')');i++)
+ /* nothing else to do */ ;
+ if (triple[i]!=')')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ')' (entry skipped)\n" );
+ return 0;
+ }
+ domaine=i;
+ i++;
+ /* skip trailing spaces */
+ for (;(triple[i]!='\0')&&(isspace(triple[i]));i++)
+ /* nothing else to do */ ;
+ /* if anything is left in the string we have a problem */
+ if (triple[i]!='\0')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): string contains trailing data (entry skipped)\n" );
+ return 0;
+ }
+ /* write strings */
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,NSLCD_NETGROUP_TYPE_TRIPLE);
+ WRITE_STRING_STRIPSPACE_LEN(fp,triple+hostb,hoste-hostb)
+ WRITE_STRING_STRIPSPACE_LEN(fp,triple+userb,usere-userb)
+ WRITE_STRING_STRIPSPACE_LEN(fp,triple+domainb,domaine-domainb)
+ /* we're done */
+ return 0;
+}
+
+static int write_netgroup(nssov_netgroup_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int i;
+ Attribute *a;
+
+ /* get the netgroup triples and member */
+ a = attr_find(entry->e_attrs,cbp->mi->mi_attrs[1].an_desc);
+ if ( a ) {
+ /* write the netgroup triples */
+ for (i=0;i<a->a_numvals;i++)
+ {
+ if (write_netgroup_triple(cbp->fp, a->a_vals[i].bv_val))
+ return -1;
+ }
+ }
+ a = attr_find(entry->e_attrs,cbp->mi->mi_attrs[2].an_desc);
+ if ( a ) {
+ /* write netgroup members */
+ for (i=0;i<a->a_numvals;i++)
+ {
+ /* write the result code */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ /* write triple indicator */
+ WRITE_INT32(cbp->fp,NSLCD_NETGROUP_TYPE_NETGROUP);
+ /* write netgroup name */
+ if (write_string_stripspace_len(cbp->fp,a->a_vals[i].bv_val,a->a_vals[i].bv_len))
+ return -1;
+ }
+ }
+ /* we're done */
+ return 0;
+}
+
+NSSOV_CB(netgroup)
+
+NSSOV_HANDLE(
+ netgroup,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);,
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ Debug(LDAP_DEBUG_TRACE,"nssov_netgroup_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_NETGROUP_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
diff --git a/contrib/slapd-modules/nssov/network.c b/contrib/slapd-modules/nssov/network.c
new file mode 100644
index 0000000..0f67fa8
--- /dev/null
+++ b/contrib/slapd-modules/nssov/network.c
@@ -0,0 +1,161 @@
+/* network.c - network address lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+#include <ac/socket.h>
+
+/* ( nisSchema.2.7 NAME 'ipNetwork' SUP top STRUCTURAL
+ * DESC 'Abstraction of a network. The distinguished value of
+ * MUST ( cn $ ipNetworkNumber )
+ * MAY ( ipNetmaskNumber $ l $ description $ manager ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval network_filter = BER_BVC("(objectClass=ipNetwork)");
+
+/* the attributes used in searches */
+static struct berval network_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipNetworkNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(network)
+
+NSSOV_CBPRIV(network,
+ char buf[1024];
+ struct berval name;
+ struct berval addr;);
+
+/* write a single network entry to the stream */
+static int write_network(nssov_network_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int numaddr,i,numname,dupname;
+ struct berval name, *names, *addrs;
+ Attribute *a;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name);
+ /* get the other names for the network */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the addresses */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ addrs = a->a_vals;
+ numaddr = a->a_numvals;
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,numaddr);
+ for (i=0;i<numaddr;i++)
+ {
+ WRITE_ADDRESS(cbp->fp,&addrs[i]);
+ }
+ return 0;
+}
+
+NSSOV_CB(network)
+
+NSSOV_HANDLE(
+ network,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.addr);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_network_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_NETWORK_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ network,byaddr,
+ int af;
+ char addr[64];
+ int len=sizeof(addr);
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.name);
+ READ_ADDRESS(fp,addr,len,af);
+ /* translate the address to a string */
+ if (inet_ntop(af,addr,cbp.buf,sizeof(cbp.buf))==NULL)
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string\n");
+ return -1;
+ }
+ cbp.addr.bv_val = cbp.buf;
+ cbp.addr.bv_len = strlen(cbp.buf);,
+ Debug(LDAP_DEBUG_TRACE,"nslcd_network_byaddr(%s)\n",cbp.addr.bv_val);,
+ NSLCD_ACTION_NETWORK_BYADDR,
+ nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
+)
+
+NSSOV_HANDLE(
+ network,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.addr);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_network_all()\n");,
+ NSLCD_ACTION_NETWORK_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/README b/contrib/slapd-modules/nssov/nss-pam-ldapd/README
new file mode 100644
index 0000000..4176ad7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/README
@@ -0,0 +1,15 @@
+These files were pulled from the nss-pam-ldapd project version 0.9.4.
+Copyright notices are in the individual files.
+
+This is not the full distribution of nss-pam-ldapd, and does not
+include the client-side stub libraries. Get the latest release of
+nss-pam-ldapd from http://arthurdejong.org/nss-pam-ldapd/ to use
+this overlay.
+
+If your system already has the nss-pam-ldapd stub libraries
+installed, make sure the versions match the version number
+shown above. Otherwise, there may be incompatible differences in
+the protocols being used. Currently nssov requires at least
+version 0.9.0. If your system's version is older, you will need
+to install the client-side stubs from source.
+
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h
new file mode 100644
index 0000000..2efedc6
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h
@@ -0,0 +1,91 @@
+/*
+ attrs.h - wrapper macros for the gcc __attribute__(()) directive
+
+ Copyright (C) 2007, 2008, 2012 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef COMPAT__ATTRS_H
+#define COMPAT__ATTRS_H 1
+
+/* macro for testing the version of GCC */
+#define GCC_VERSION(major, minor) \
+ ((__GNUC__ > (major)) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
+
+/* These are macros to use some gcc-specific flags in case they're available
+ and otherwise define them to empty strings. This allows us to give
+ the compiler some extra information.
+ See http://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
+ for a list of attributes supported by gcc */
+
+/* this is used to flag function parameters that are not used in the function
+ body. */
+#if GCC_VERSION(3, 0)
+#define UNUSED(x) x __attribute__((__unused__))
+#else
+#define UNUSED(x) x
+#endif
+
+/* this is used to add extra format checking to the function calls as if this
+ was a printf()-like function */
+#if GCC_VERSION(3, 0)
+#define LIKE_PRINTF(format_idx, arg_idx) \
+ __attribute__((__format__(__printf__, format_idx, arg_idx)))
+#else
+#define LIKE_PRINTF(format_idx, arg_idx) /* no attribute */
+#endif
+
+/* indicates that the function is "pure": its result is purely based on
+ the parameters and has no side effects or used static data */
+#if GCC_VERSION(3, 0)
+#define PURE __attribute__((__pure__))
+#else
+#define PURE /* no attribute */
+#endif
+
+/* the function returns a new data structure that has been freshly
+ allocated */
+#if GCC_VERSION(3, 0)
+#define LIKE_MALLOC __attribute__((__malloc__))
+#else
+#define LIKE_MALLOC /* no attribute */
+#endif
+
+/* the function's return value should be used by the caller */
+#if GCC_VERSION(3, 4)
+#define MUST_USE __attribute__((__warn_unused_result__))
+#else
+#define MUST_USE /* no attribute */
+#endif
+
+/* the function's return value should be used by the caller */
+#if GCC_VERSION(2, 5)
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN /* no attribute */
+#endif
+
+/* define __STRING if it's not yet defined */
+#ifndef __STRING
+#ifdef __STDC__
+#define __STRING(x) #x
+#else /* __STDC__ */
+#define __STRING(x) "x"
+#endif /* not __STDC__ */
+#endif /* not __STRING */
+
+#endif /* not COMPAT__ATTRS_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h
new file mode 100644
index 0000000..21ec7c2
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h
@@ -0,0 +1,391 @@
+/*
+ nslcd-prot.h - helper macros for reading and writing in protocol streams
+
+ Copyright (C) 2006 West Consulting
+ Copyright (C) 2006-2014 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef COMMON__NSLCD_PROT_H
+#define COMMON__NSLCD_PROT_H 1
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "tio.h"
+
+/* If you use these macros you should define the following macros to
+ handle error conditions (these marcos should clean up and return from the
+ function):
+ ERROR_OUT_WRITEERROR(fp)
+ ERROR_OUT_READERROR(fp)
+ ERROR_OUT_BUFERROR(fp)
+ ERROR_OUT_NOSUCCESS(fp) */
+
+
+/* Debugging marcos that can be used to enable detailed protocol logging,
+ pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
+ to dump the actual bytestream. */
+
+#ifdef DEBUG_PROT
+/* define a debugging macro to output logging */
+#include <string.h>
+#include <errno.h>
+#define DEBUG_PRINT(fmt, arg) \
+ fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__, \
+ __PRETTY_FUNCTION__, arg);
+#else /* DEBUG_PROT */
+/* define an empty debug macro to disable logging */
+#define DEBUG_PRINT(fmt, arg)
+#endif /* not DEBUG_PROT */
+
+#ifdef DEBUG_PROT_DUMP
+/* define a debugging macro to output detailed logging */
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+static void debug_dump(const void *ptr, size_t size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
+ fprintf(stderr, "\n");
+}
+#define DEBUG_DUMP(ptr, size) \
+ fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ debug_dump(ptr, size);
+#else /* DEBUG_PROT_DUMP */
+/* define an empty debug macro to disable logging */
+#define DEBUG_DUMP(ptr, size)
+#endif /* not DEBUG_PROT_DUMP */
+
+
+/* WRITE marcos, used for writing data, on write error they will
+ call the ERROR_OUT_WRITEERROR macro
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ */
+
+#define WRITE(fp, ptr, size) \
+ DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d", (int)size); \
+ DEBUG_DUMP(ptr, size); \
+ if (tio_write(fp, ptr, (size_t)size)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_WRITEERROR(fp); \
+ }
+
+#define WRITE_INT32(fp, i) \
+ DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \
+ tmpint32 = htonl((int32_t)(i)); \
+ WRITE(fp, &tmpint32, sizeof(int32_t))
+
+#define WRITE_STRING(fp, str) \
+ DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \
+ if ((str) == NULL) \
+ { \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ WRITE_INT32(fp, strlen(str)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 > 0) \
+ { \
+ WRITE(fp, (str), tmpint32); \
+ } \
+ }
+
+#define WRITE_STRINGLIST(fp, arr) \
+ if ((arr) == NULL) \
+ { \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ /* first determine length of array */ \
+ for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \
+ /* noting */ ; \
+ /* write number of strings */ \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ WRITE_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ }
+
+#define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \
+ /* first determine length of array */ \
+ tmp3int32 = 0; \
+ for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
+ if (strcmp((arr)[tmp2int32], (not)) != 0) \
+ tmp3int32++; \
+ /* write number of strings (mius one because we intend to skip one) */ \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
+ { \
+ if (strcmp((arr)[tmp2int32], (not)) != 0) \
+ { \
+ WRITE_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ }
+
+/* READ macros, used for reading data, on read error they will
+ call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ */
+
+#define READ(fp, ptr, size) \
+ if (tio_read(fp, ptr, (size_t)size)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_READERROR(fp); \
+ } \
+ DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \
+ DEBUG_DUMP(ptr, size);
+
+#define READ_INT32(fp, i) \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ (i) = (int32_t)ntohl(tmpint32); \
+ DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
+
+/* read a string in a fixed-size "normal" buffer */
+#define READ_STRING(fp, buffer) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \
+ /* check if read would fit */ \
+ if (((size_t)tmpint32) >= sizeof(buffer)) \
+ { \
+ /* will not fit */ \
+ tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \
+ DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
+ ERROR_OUT_BUFERROR(fp); \
+ } \
+ /* read string from the stream */ \
+ if (tmpint32 > 0) \
+ { \
+ READ(fp, buffer, (size_t)tmpint32); \
+ } \
+ /* null-terminate string in buffer */ \
+ buffer[tmpint32] = '\0'; \
+ DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
+
+
+/* READ BUF macros that read data into a pre-allocated buffer.
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ char *buffer; - pointer to a buffer for reading strings
+ size_t buflen; - the size of the buffer
+ size_t bufptr; - the current position in the buffer
+ */
+
+/* current position in the buffer */
+#define BUF_CUR \
+ (buffer + bufptr)
+
+/* check that the buffer has sz bytes left in it */
+#define BUF_CHECK(fp, sz) \
+ if ((bufptr + (size_t)(sz)) > buflen) \
+ { \
+ /* will not fit */ \
+ tmpint32 = bufptr + (sz) - (buflen); \
+ DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
+ ERROR_OUT_BUFERROR(fp); \
+ }
+
+/* move the buffer pointer */
+#define BUF_SKIP(sz) \
+ bufptr += (size_t)(sz);
+
+/* move BUF_CUR forward so that it is aligned to the specified
+ type width */
+#define BUF_ALIGN(fp, type) \
+ /* figure out number of bytes to skip forward */ \
+ tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \
+ % sizeof(type); \
+ /* check and skip */ \
+ BUF_CHECK(fp, tmp2int32); \
+ BUF_SKIP(tmp2int32);
+
+/* allocate a piece of the buffer to store an array in */
+#define BUF_ALLOC(fp, ptr, type, num) \
+ /* align to the specified type width */ \
+ BUF_ALIGN(fp, type); \
+ /* check that we have enough room */ \
+ BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \
+ /* store the pointer */ \
+ (ptr) = (type *)BUF_CUR; \
+ /* reserve the space */ \
+ BUF_SKIP((size_t)(num) * sizeof(type));
+
+/* read a binary blob into the buffer */
+#define READ_BUF(fp, ptr, sz) \
+ /* check that there is enough room and read */ \
+ BUF_CHECK(fp, sz); \
+ READ(fp, BUF_CUR, (size_t)sz); \
+ /* store pointer and skip */ \
+ (ptr) = BUF_CUR; \
+ BUF_SKIP(sz);
+
+/* read string in the buffer (using buffer, buflen and bufptr)
+ and store the actual location of the string in field */
+#define READ_BUF_STRING(fp, field) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
+ /* check if read would fit */ \
+ BUF_CHECK(fp, tmpint32 + 1); \
+ /* read string from the stream */ \
+ if (tmpint32 > 0) \
+ { \
+ READ(fp, BUF_CUR, (size_t)tmpint32); \
+ } \
+ /* null-terminate string in buffer */ \
+ BUF_CUR[tmpint32] = '\0'; \
+ DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
+ /* prepare result */ \
+ (field) = BUF_CUR; \
+ BUF_SKIP(tmpint32 + 1);
+
+/* read an array from a stream and store it as a null-terminated
+ array list (size for the array is allocated) */
+#define READ_BUF_STRINGLIST(fp, arr) \
+ /* read the number of entries */ \
+ READ(fp, &tmp3int32, sizeof(int32_t)); \
+ tmp3int32 = ntohl(tmp3int32); \
+ DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ /* allocate room for *char[num + 1] */ \
+ BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \
+ /* read all entries */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ READ_BUF_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ /* set last entry to NULL */ \
+ (arr)[tmp2int32] = NULL;
+
+
+/* SKIP macros for skipping over certain parts of the protocol stream. */
+
+/* skip a number of bytes forward */
+#define SKIP(fp, sz) \
+ DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \
+ /* read (skip) the specified number of bytes */ \
+ if (tio_skip(fp, sz)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("READ : skip error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_READERROR(fp); \
+ }
+
+/* read a string from the stream but don't do anything with the result */
+#define SKIP_STRING(fp) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \
+ /* read (skip) the specified number of bytes */ \
+ SKIP(fp, tmpint32);
+
+/* skip a list of strings */
+#define SKIP_STRINGLIST(fp) \
+ /* read the number of entries */ \
+ READ(fp, &tmp3int32, sizeof(int32_t)); \
+ tmp3int32 = ntohl(tmp3int32); \
+ DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \
+ /* read all entries */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ SKIP_STRING(fp); \
+ }
+
+
+/* These are functions and macros for performing common operations in
+ the nslcd request/response protocol. */
+
+/* returns a socket to the server or NULL on error (see errno),
+ socket should be closed with tio_close() */
+TFILE *nslcd_client_open(void)
+ MUST_USE;
+
+/* generic request code */
+#define NSLCD_REQUEST(fp, action, writefn) \
+ /* open a client socket */ \
+ if ((fp = nslcd_client_open()) == NULL) \
+ { \
+ ERROR_OUT_OPENERROR; \
+ } \
+ /* write a request header with a request code */ \
+ WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \
+ WRITE_INT32(fp, (int32_t)action) \
+ /* write the request parameters (if any) */ \
+ writefn; \
+ /* flush the stream */ \
+ if (tio_flush(fp) < 0) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("WRITE_FLUSH : error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_WRITEERROR(fp); \
+ } \
+ /* read and check response version number */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)NSLCD_VERSION) \
+ { \
+ ERROR_OUT_READERROR(fp); \
+ } \
+ /* read and check response request number */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)(action)) \
+ { \
+ ERROR_OUT_READERROR(fp); \
+ }
+
+/* Read the response code (the result code of the query) from
+ the stream. */
+#define READ_RESPONSE_CODE(fp) \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \
+ { \
+ ERROR_OUT_NOSUCCESS(fp); \
+ }
+
+#endif /* not COMMON__NSLCD_PROT_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h
new file mode 100644
index 0000000..c7dc013
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h
@@ -0,0 +1,305 @@
+/*
+ nslcd.h - file describing client/server protocol
+
+ Copyright (C) 2006 West Consulting
+ Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef _NSLCD_H
+#define _NSLCD_H 1
+
+/*
+ The protocol used between the nslcd client and server is a simple binary
+ protocol. It is request/response based where the client initiates a
+ connection, does a single request and closes the connection again. Any
+ mangled or not understood messages will be silently ignored by the server.
+
+ A request looks like:
+ INT32 NSLCD_VERSION
+ INT32 NSLCD_ACTION_*
+ [request parameters if any]
+ A response looks like:
+ INT32 NSLCD_VERSION
+ INT32 NSLCD_ACTION_* (the original request type)
+ [result(s)]
+ INT32 NSLCD_RESULT_END
+ A single result entry looks like:
+ INT32 NSLCD_RESULT_BEGIN
+ [result value(s)]
+ If a response would return multiple values (e.g. for NSLCD_ACTION_*_ALL
+ functions) each return value will be preceded by a NSLCD_RESULT_BEGIN
+ value. After the last returned result the server sends
+ NSLCD_RESULT_END. If some error occurs (e.g. LDAP server unavailable,
+ error in the request, etc) the server terminates the connection to signal
+ an error condition (breaking the protocol).
+
+ These are the available basic data types:
+ INT32 - 32-bit integer value
+ TYPE - a typed field that is transferred using sizeof()
+ STRING - a string length (32bit) followed by the string value (not
+ null-terminated) the string itself is assumed to be UTF-8
+ STRINGLIST - a 32-bit number noting the number of strings followed by
+ the strings one at a time
+
+ Furthermore the ADDRESS compound data type is defined as:
+ INT32 type of address: e.g. AF_INET or AF_INET6
+ INT32 length of address
+ RAW the address itself
+ With the ADDRESSLIST using the same construct as with STRINGLIST.
+
+ The protocol uses network byte order for all types.
+*/
+
+/* The current version of the protocol. This protocol should only be
+ updated with major backwards-incompatible changes. */
+#define NSLCD_VERSION 0x00000002
+
+/* Get a NSLCD configuration option. There is one request parameter:
+ INT32 NSLCD_CONFIG_*
+ the result value is:
+ STRING value, interpretation depending on request */
+#define NSLCD_ACTION_CONFIG_GET 0x00010001
+
+/* return the message, if any, that is presented to the user when password
+ modification through PAM is prohibited */
+#define NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE 1
+
+/* Email alias (/etc/aliases) NSS requests. The result values for a
+ single entry are:
+ STRING alias name
+ STRINGLIST alias rcpts */
+#define NSLCD_ACTION_ALIAS_BYNAME 0x00020001
+#define NSLCD_ACTION_ALIAS_ALL 0x00020008
+
+/* Ethernet address/name mapping NSS requests. The result values for a
+ single entry are:
+ STRING ether name
+ TYPE(uint8_t[6]) ether address */
+#define NSLCD_ACTION_ETHER_BYNAME 0x00030001
+#define NSLCD_ACTION_ETHER_BYETHER 0x00030002
+#define NSLCD_ACTION_ETHER_ALL 0x00030008
+
+/* Group and group membership related NSS requests. The result values
+ for a single entry are:
+ STRING group name
+ STRING group password
+ INT32 group id
+ STRINGLIST members (usernames) of the group
+ (not that the BYMEMER call returns an empty members list) */
+#define NSLCD_ACTION_GROUP_BYNAME 0x00040001
+#define NSLCD_ACTION_GROUP_BYGID 0x00040002
+#define NSLCD_ACTION_GROUP_BYMEMBER 0x00040006
+#define NSLCD_ACTION_GROUP_ALL 0x00040008
+
+/* Hostname (/etc/hosts) lookup NSS requests. The result values
+ for an entry are:
+ STRING host name
+ STRINGLIST host aliases
+ ADDRESSLIST host addresses */
+#define NSLCD_ACTION_HOST_BYNAME 0x00050001
+#define NSLCD_ACTION_HOST_BYADDR 0x00050002
+#define NSLCD_ACTION_HOST_ALL 0x00050008
+
+/* Netgroup NSS result entries contain a number of parts. A result entry
+ starts with:
+ STRING netgroup name
+ followed by zero or more references to other netgroups or netgroup
+ triples. A reference to another netgroup looks like:
+ INT32 NSLCD_NETGROUP_TYPE_NETGROUP
+ STRING other netgroup name
+ A a netgroup triple looks like:
+ INT32 NSLCD_NETGROUP_TYPE_TRIPLE
+ STRING host
+ STRING user
+ STRING domain
+ A netgroup result entry is terminated by:
+ INT32 NSLCD_NETGROUP_TYPE_END
+ */
+#define NSLCD_ACTION_NETGROUP_BYNAME 0x00060001
+#define NSLCD_ACTION_NETGROUP_ALL 0x00060008
+#define NSLCD_NETGROUP_TYPE_NETGROUP 1
+#define NSLCD_NETGROUP_TYPE_TRIPLE 2
+#define NSLCD_NETGROUP_TYPE_END 3
+
+/* Network name (/etc/networks) NSS requests. Result values for a single
+ entry are:
+ STRING network name
+ STRINGLIST network aliases
+ ADDRESSLIST network addresses */
+#define NSLCD_ACTION_NETWORK_BYNAME 0x00070001
+#define NSLCD_ACTION_NETWORK_BYADDR 0x00070002
+#define NSLCD_ACTION_NETWORK_ALL 0x00070008
+
+/* User account (/etc/passwd) NSS requests. Result values are:
+ STRING user name
+ STRING user password
+ INT32 user id
+ INT32 group id
+ STRING gecos information
+ STRING home directory
+ STRING login shell */
+#define NSLCD_ACTION_PASSWD_BYNAME 0x00080001
+#define NSLCD_ACTION_PASSWD_BYUID 0x00080002
+#define NSLCD_ACTION_PASSWD_ALL 0x00080008
+
+/* Protocol information requests. Result values are:
+ STRING protocol name
+ STRINGLIST protocol aliases
+ INT32 protocol number */
+#define NSLCD_ACTION_PROTOCOL_BYNAME 0x00090001
+#define NSLCD_ACTION_PROTOCOL_BYNUMBER 0x00090002
+#define NSLCD_ACTION_PROTOCOL_ALL 0x00090008
+
+/* RPC information requests. Result values are:
+ STRING rpc name
+ STRINGLIST rpc aliases
+ INT32 rpc number */
+#define NSLCD_ACTION_RPC_BYNAME 0x000a0001
+#define NSLCD_ACTION_RPC_BYNUMBER 0x000a0002
+#define NSLCD_ACTION_RPC_ALL 0x000a0008
+
+/* Service (/etc/services) information requests. The BYNAME and BYNUMBER
+ requests contain an extra protocol string in the request which, if not
+ blank, will filter the services by this protocol. Result values are:
+ STRING service name
+ STRINGLIST service aliases
+ INT32 service (port) number
+ STRING service protocol */
+#define NSLCD_ACTION_SERVICE_BYNAME 0x000b0001
+#define NSLCD_ACTION_SERVICE_BYNUMBER 0x000b0002
+#define NSLCD_ACTION_SERVICE_ALL 0x000b0008
+
+/* Extended user account (/etc/shadow) information requests. Result
+ values for a single entry are:
+ STRING user name
+ STRING user password
+ INT32 last password change
+ INT32 mindays
+ INT32 maxdays
+ INT32 warn
+ INT32 inact
+ INT32 expire
+ INT32 flag */
+#define NSLCD_ACTION_SHADOW_BYNAME 0x000c0001
+#define NSLCD_ACTION_SHADOW_ALL 0x000c0008
+
+/* PAM-related requests. The request parameters for all these requests
+ begin with:
+ STRING user name
+ STRING service name
+ STRING ruser
+ STRING rhost
+ STRING tty
+ If the user is not known in LDAP no result may be returned (immediately
+ return NSLCD_RESULT_END instead of a PAM error code). */
+
+/* PAM authentication check request. The extra request values are:
+ STRING password
+ and the result value consists of:
+ INT32 authc NSLCD_PAM_* result code
+ STRING user name (the canonical user name)
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message
+ If the username is empty in this request an attempt is made to
+ authenticate as the administrator (set using rootpwmoddn).
+ Some authorisation checks are already done during authentication so the
+ response also includes authorisation information. */
+#define NSLCD_ACTION_PAM_AUTHC 0x000d0001
+
+/* PAM authorisation check request. The result value consists of:
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message
+ The authentication check may have already returned some authorisation
+ information. The authorisation error message, if supplied, will be used
+ by the PAM module instead of a message that is generated by the PAM
+ module itself. */
+#define NSLCD_ACTION_PAM_AUTHZ 0x000d0002
+
+/* PAM session open request. The result value consists of:
+ STRING session id
+ This session id may be used to close this session with. */
+#define NSLCD_ACTION_PAM_SESS_O 0x000d0003
+
+/* PAM session close request. This request has the following
+ extra request value:
+ STRING session id
+ and this calls only returns an empty response value. */
+#define NSLCD_ACTION_PAM_SESS_C 0x000d0004
+
+/* PAM password modification request. This requests has the following extra
+ request values:
+ INT32 asroot: 0=oldpasswd is user passwd, 1=oldpasswd is root passwd
+ STRING old password
+ STRING new password
+ and returns there extra result values:
+ INT32 NSLCD_PAM_* result code
+ STRING error message */
+#define NSLCD_ACTION_PAM_PWMOD 0x000d0005
+
+/* User information change request. This request allows one to change
+ their full name and other information. The request parameters for this
+ request are:
+ STRING user name
+ INT32 asroot: 0=passwd is user passwd, 1=passwd is root passwd
+ STRING password
+ followed by one or more of the below, terminated by NSLCD_USERMOD_END
+ INT32 NSLCD_USERMOD_*
+ STRING new value
+ the response consists of one or more of the entries below, terminated
+ by NSLCD_USERMOD_END:
+ INT32 NSLCD_USERMOD_*
+ STRING response
+ (if the response is blank, the change went OK, otherwise the string
+ contains an error message)
+ */
+#define NSLCD_ACTION_USERMOD 0x000e0001
+
+/* These are the possible values for the NSLCD_ACTION_USERMOD operation
+ above. */
+#define NSLCD_USERMOD_END 0 /* end of change values */
+#define NSLCD_USERMOD_RESULT 1 /* global result value */
+#define NSLCD_USERMOD_FULLNAME 2 /* full name */
+#define NSLCD_USERMOD_ROOMNUMBER 3 /* room number */
+#define NSLCD_USERMOD_WORKPHONE 4 /* office phone number */
+#define NSLCD_USERMOD_HOMEPHONE 5 /* home phone number */
+#define NSLCD_USERMOD_OTHER 6 /* other info */
+#define NSLCD_USERMOD_HOMEDIR 7 /* home directory */
+#define NSLCD_USERMOD_SHELL 8 /* login shell */
+
+/* Request result codes. */
+#define NSLCD_RESULT_BEGIN 1
+#define NSLCD_RESULT_END 2
+
+/* Partial list of PAM result codes. */
+#define NSLCD_PAM_SUCCESS 0 /* everything ok */
+#define NSLCD_PAM_PERM_DENIED 6 /* Permission denied */
+#define NSLCD_PAM_AUTH_ERR 7 /* Authc failure */
+#define NSLCD_PAM_CRED_INSUFFICIENT 8 /* Cannot access authc data */
+#define NSLCD_PAM_AUTHINFO_UNAVAIL 9 /* Cannot retrieve authc info */
+#define NSLCD_PAM_USER_UNKNOWN 10 /* User not known */
+#define NSLCD_PAM_MAXTRIES 11 /* Retry limit reached */
+#define NSLCD_PAM_NEW_AUTHTOK_REQD 12 /* Password expired */
+#define NSLCD_PAM_ACCT_EXPIRED 13 /* Account expired */
+#define NSLCD_PAM_SESSION_ERR 14 /* Cannot make/remove session record */
+#define NSLCD_PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */
+#define NSLCD_PAM_AUTHTOK_DISABLE_AGING 23 /* Password aging disabled */
+#define NSLCD_PAM_IGNORE 25 /* Ignore module */
+#define NSLCD_PAM_ABORT 26 /* Fatal error */
+#define NSLCD_PAM_AUTHTOK_EXPIRED 27 /* authentication token has expired */
+
+#endif /* not _NSLCD_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
new file mode 100644
index 0000000..012e680
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
@@ -0,0 +1,520 @@
+/*
+ tio.c - timed io functions
+ This file is part of the nss-pam-ldapd library.
+
+ Copyright (C) 2007-2014 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#include "portable.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <limits.h>
+#include <poll.h>
+#include <time.h>
+
+#include "tio.h"
+
+/* for platforms that don't have ETIME use ETIMEDOUT */
+#ifndef ETIME
+#define ETIME ETIMEDOUT
+#endif /* ETIME */
+
+/* structure that holds a buffer
+ the buffer contains the data that is between the application and the
+ file descriptor that is used for efficient transfer
+ the buffer is built up as follows:
+ |.....********......|
+ ^start ^size
+ ^--len--^ */
+struct tio_buffer {
+ uint8_t *buffer;
+ size_t size; /* the size of the buffer */
+ size_t maxsize; /* the maximum size of the buffer */
+ size_t start; /* the start of the data (before start is unused) */
+ size_t len; /* size of the data (from the start) */
+};
+
+/* structure that holds all the state for files */
+struct tio_fileinfo {
+ int fd;
+ struct tio_buffer readbuffer;
+ struct tio_buffer writebuffer;
+ int readtimeout;
+ int writetimeout;
+ int read_resettable; /* whether the tio_reset() function can be called */
+#ifdef DEBUG_TIO_STATS
+ /* this is used to collect statistics on the use of the streams
+ and can be used to tune the buffer sizes */
+ size_t byteswritten;
+ size_t bytesread;
+#endif /* DEBUG_TIO_STATS */
+};
+
+/* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
+ a CLOCK_HIGHRES that has the same properties we need */
+#ifndef CLOCK_MONOTONIC
+#ifdef CLOCK_HIGHRES
+#define CLOCK_MONOTONIC CLOCK_HIGHRES
+#endif /* CLOCK_HIGHRES */
+#endif /* not CLOCK_MONOTONIC */
+
+/* update the timeout to the value that is remaining before the deadline
+ returns the number of milliseconds before the deadline (or a negative
+ value of the deadline has expired) */
+static inline int tio_time_remaining(struct timespec *deadline, int timeout)
+{
+ struct timespec tv;
+ /* if this is the first call, set the deadline and return the full time */
+ if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
+ {
+ if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
+ {
+ deadline->tv_sec += timeout / 1000;
+ deadline->tv_nsec += (timeout % 1000) * 1000000;
+ }
+ return timeout;
+ }
+ /* get the current time (fall back to full time on error) */
+ if (clock_gettime(CLOCK_MONOTONIC, &tv))
+ return timeout;
+ /* calculate time remaining in milliseconds */
+ return (deadline->tv_sec - tv.tv_sec) * 1000 +
+ (deadline->tv_nsec - tv.tv_nsec) / 1000000;
+}
+
+/* open a new TFILE based on the file descriptor */
+TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
+ size_t initreadsize, size_t maxreadsize,
+ size_t initwritesize, size_t maxwritesize)
+{
+ struct tio_fileinfo *fp;
+ fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
+ if (fp == NULL)
+ return NULL;
+ fp->fd = fd;
+ /* initialize read buffer */
+ fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
+ if (fp->readbuffer.buffer == NULL)
+ {
+ free(fp);
+ return NULL;
+ }
+ fp->readbuffer.size = initreadsize;
+ fp->readbuffer.maxsize = maxreadsize;
+ fp->readbuffer.start = 0;
+ fp->readbuffer.len = 0;
+ /* initialize write buffer */
+ fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
+ if (fp->writebuffer.buffer == NULL)
+ {
+ free(fp->readbuffer.buffer);
+ free(fp);
+ return NULL;
+ }
+ fp->writebuffer.size = initwritesize;
+ fp->writebuffer.maxsize = maxwritesize;
+ fp->writebuffer.start = 0;
+ fp->writebuffer.len = 0;
+ /* initialize other attributes */
+ fp->readtimeout = readtimeout;
+ fp->writetimeout = writetimeout;
+ fp->read_resettable = 0;
+#ifdef DEBUG_TIO_STATS
+ fp->byteswritten = 0;
+ fp->bytesread = 0;
+#endif /* DEBUG_TIO_STATS */
+ return fp;
+}
+
+/* wait for any activity on the specified file descriptor using
+ the specified deadline */
+static int tio_wait(int fd, short events, int timeout,
+ struct timespec *deadline)
+{
+ int t;
+ struct pollfd fds[1];
+ int rv;
+ while (1)
+ {
+ fds[0].fd = fd;
+ fds[0].events = events;
+ /* figure out the time we need to wait */
+ if ((t = tio_time_remaining(deadline, timeout)) < 0)
+ {
+ errno = ETIME;
+ return -1;
+ }
+ /* sanity check for moving clock */
+ if (t > timeout)
+ t = timeout;
+ /* wait for activity */
+ rv = poll(fds, 1, t);
+ if (rv > 0)
+ return 0; /* we have activity */
+ else if (rv == 0)
+ {
+ /* no file descriptors were available within the specified time */
+ errno = ETIME;
+ return -1;
+ }
+ else if ((errno != EINTR) && (errno != EAGAIN))
+ /* some error occurred */
+ return -1;
+ /* we just try again on EINTR or EAGAIN */
+ }
+}
+
+/* do a read on the file descriptor, returning the data in the buffer
+ if no data was read in the specified time an error is returned */
+int tio_read(TFILE *fp, void *buf, size_t count)
+{
+ struct timespec deadline = {0, 0};
+ int rv;
+ uint8_t *tmp;
+ size_t newsz;
+ size_t len;
+ /* have a more convenient storage type for the buffer */
+ uint8_t *ptr = (uint8_t *)buf;
+ /* loop until we have returned all the needed data */
+ while (1)
+ {
+ /* check if we have enough data in the buffer */
+ if (fp->readbuffer.len >= count)
+ {
+ if (count > 0)
+ {
+ if (ptr != NULL)
+ memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
+ /* adjust buffer position */
+ fp->readbuffer.start += count;
+ fp->readbuffer.len -= count;
+ }
+ return 0;
+ }
+ /* empty what we have and continue from there */
+ if (fp->readbuffer.len > 0)
+ {
+ if (ptr != NULL)
+ {
+ memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
+ fp->readbuffer.len);
+ ptr += fp->readbuffer.len;
+ }
+ count -= fp->readbuffer.len;
+ fp->readbuffer.start += fp->readbuffer.len;
+ fp->readbuffer.len = 0;
+ }
+ /* after this point until the read fp->readbuffer.len is 0 */
+ if (!fp->read_resettable)
+ {
+ /* the stream is not resettable, re-use the buffer */
+ fp->readbuffer.start = 0;
+ }
+ else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
+ {
+ /* buffer is running empty, try to grow buffer */
+ if (fp->readbuffer.size < fp->readbuffer.maxsize)
+ {
+ newsz = fp->readbuffer.size * 2;
+ if (newsz > fp->readbuffer.maxsize)
+ newsz = fp->readbuffer.maxsize;
+ tmp = realloc(fp->readbuffer.buffer, newsz);
+ if (tmp != NULL)
+ {
+ fp->readbuffer.buffer = tmp;
+ fp->readbuffer.size = newsz;
+ }
+ }
+ /* if buffer still does not contain enough room, clear resettable */
+ if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
+ {
+ fp->readbuffer.start = 0;
+ fp->read_resettable = 0;
+ }
+ }
+ /* wait until we have input */
+ if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
+ return -1;
+ /* read the input in the buffer */
+ len = fp->readbuffer.size - fp->readbuffer.start;
+#ifdef SSIZE_MAX
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
+#endif /* SSIZE_MAX */
+ rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
+ /* check for errors */
+ if (rv == 0)
+ {
+ errno = ECONNRESET;
+ return -1;
+ }
+ else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
+ return -1; /* something went wrong with the read */
+ else if (rv > 0)
+ fp->readbuffer.len = rv; /* skip the read part in the buffer */
+#ifdef DEBUG_TIO_STATS
+ fp->bytesread += rv;
+#endif /* DEBUG_TIO_STATS */
+ }
+}
+
+/* Read and discard the specified number of bytes from the stream. */
+int tio_skip(TFILE *fp, size_t count)
+{
+ return tio_read(fp, NULL, count);
+}
+
+/* Read all available data from the stream and empty the read buffer. */
+int tio_skipall(TFILE *fp, int timeout)
+{
+ struct timespec deadline = {0, 0};
+ int rv;
+ size_t len;
+ /* clear the read buffer */
+ fp->readbuffer.start = 0;
+ fp->readbuffer.len = 0;
+ fp->read_resettable = 0;
+ /* read until we can't read no more */
+ len = fp->readbuffer.size;
+#ifdef SSIZE_MAX
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
+#endif /* SSIZE_MAX */
+ while (1)
+ {
+ /* wait until we have input */
+ if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
+ return -1;
+ /* read data from the stream */
+ rv = read(fp->fd, fp->readbuffer.buffer, len);
+ if (rv == 0)
+ return 0; /* end-of-file */
+ if ((rv < 0) && (errno == EWOULDBLOCK))
+ return 0; /* we've ready everything we can without blocking */
+ if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
+ return -1; /* something went wrong with the read */
+ }
+}
+
+/* the caller has assured us that we can write to the file descriptor
+ and we give it a shot */
+static int tio_writebuf(TFILE *fp)
+{
+ int rv;
+ /* write the buffer */
+#ifdef MSG_NOSIGNAL
+ rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len, MSG_NOSIGNAL);
+#else /* not MSG_NOSIGNAL */
+ /* on platforms that cannot use send() with masked signals, we change the
+ signal mask and change it back after the write (note that there is a
+ race condition here) */
+ struct sigaction act, oldact;
+ /* set up sigaction */
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_sigaction = NULL;
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ /* ignore SIGPIPE */
+ if (sigaction(SIGPIPE, &act, &oldact) != 0)
+ return -1; /* error setting signal handler */
+ /* write the buffer */
+ rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len);
+ /* restore the old handler for SIGPIPE */
+ if (sigaction(SIGPIPE, &oldact, NULL) != 0)
+ return -1; /* error restoring signal handler */
+#endif
+ /* check for errors */
+ if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
+ return -1; /* something went wrong with the write */
+ /* skip the written part in the buffer */
+ if (rv > 0)
+ {
+ fp->writebuffer.start += rv;
+ fp->writebuffer.len -= rv;
+#ifdef DEBUG_TIO_STATS
+ fp->byteswritten += rv;
+#endif /* DEBUG_TIO_STATS */
+ /* reset start if len is 0 */
+ if (fp->writebuffer.len == 0)
+ fp->writebuffer.start = 0;
+ /* move contents of the buffer to the front if it will save enough room */
+ if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
+ {
+ memmove(fp->writebuffer.buffer,
+ fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len);
+ fp->writebuffer.start = 0;
+ }
+ }
+ return 0;
+}
+
+/* write all the data in the buffer to the stream */
+int tio_flush(TFILE *fp)
+{
+ struct timespec deadline = {0, 0};
+ /* loop until we have written our buffer */
+ while (fp->writebuffer.len > 0)
+ {
+ /* wait until we can write */
+ if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
+ return -1;
+ /* write one block */
+ if (tio_writebuf(fp))
+ return -1;
+ }
+ return 0;
+}
+
+/* try a single write of data in the buffer if the file descriptor
+ will accept data */
+static int tio_flush_nonblock(TFILE *fp)
+{
+ struct pollfd fds[1];
+ int rv;
+ /* see if we can write without blocking */
+ fds[0].fd = fp->fd;
+ fds[0].events = POLLOUT;
+ rv = poll(fds, 1, 0);
+ /* check if any file descriptors were ready (timeout) or we were
+ interrupted */
+ if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
+ return 0;
+ /* any other errors? */
+ if (rv < 0)
+ return -1;
+ /* so file descriptor will accept writes */
+ return tio_writebuf(fp);
+}
+
+int tio_write(TFILE *fp, const void *buf, size_t count)
+{
+ size_t fr;
+ uint8_t *tmp;
+ size_t newsz;
+ const uint8_t *ptr = (const uint8_t *)buf;
+ /* keep filling the buffer until we have buffered everything */
+ while (count > 0)
+ {
+ /* figure out free size in buffer */
+ fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
+ if (count <= fr)
+ {
+ /* the data fits in the buffer */
+ memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
+ fp->writebuffer.len, ptr, count);
+ fp->writebuffer.len += count;
+ return 0;
+ }
+ else if (fr > 0)
+ {
+ /* fill the buffer with data that will fit */
+ memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
+ fp->writebuffer.len, ptr, fr);
+ fp->writebuffer.len += fr;
+ ptr += fr;
+ count -= fr;
+ }
+ /* try to flush some of the data that is in the buffer */
+ if (tio_flush_nonblock(fp))
+ return -1;
+ /* if we have room now, try again */
+ if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
+ continue;
+ /* try to grow the buffer */
+ if (fp->writebuffer.size < fp->writebuffer.maxsize)
+ {
+ newsz = fp->writebuffer.size * 2;
+ if (newsz > fp->writebuffer.maxsize)
+ newsz = fp->writebuffer.maxsize;
+ tmp = realloc(fp->writebuffer.buffer, newsz);
+ if (tmp != NULL)
+ {
+ fp->writebuffer.buffer = tmp;
+ fp->writebuffer.size = newsz;
+ continue; /* try again */
+ }
+ }
+ /* write the buffer to the stream */
+ if (tio_flush(fp))
+ return -1;
+ }
+ return 0;
+}
+
+int tio_close(TFILE *fp)
+{
+ int retv;
+ /* write any buffered data */
+ retv = tio_flush(fp);
+#ifdef DEBUG_TIO_STATS
+ /* dump statistics to stderr */
+ fprintf(stderr, "DEBUG_TIO_STATS READ=%d WRITTEN=%d\n", fp->bytesread,
+ fp->byteswritten);
+#endif /* DEBUG_TIO_STATS */
+ /* close file descriptor */
+ if (close(fp->fd))
+ retv = -1;
+ /* free any allocated buffers */
+ memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
+ memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
+ free(fp->readbuffer.buffer);
+ free(fp->writebuffer.buffer);
+ /* free the tio struct itself */
+ free(fp);
+ /* return the result of the earlier operations */
+ return retv;
+}
+
+void tio_mark(TFILE *fp)
+{
+ /* move any data in the buffer to the start of the buffer */
+ if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
+ {
+ memmove(fp->readbuffer.buffer,
+ fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
+ fp->readbuffer.start = 0;
+ }
+ /* mark the stream as resettable */
+ fp->read_resettable = 1;
+}
+
+int tio_reset(TFILE *fp)
+{
+ /* check if the stream is (still) resettable */
+ if (!fp->read_resettable)
+ return -1;
+ /* reset the buffer */
+ fp->readbuffer.len += fp->readbuffer.start;
+ fp->readbuffer.start = 0;
+ return 0;
+}
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h
new file mode 100644
index 0000000..95f9812
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h
@@ -0,0 +1,83 @@
+/*
+ tio.h - timed io functions
+ This file is part of the nss-pam-ldapd library.
+
+ Copyright (C) 2007, 2008, 2010, 2012, 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+/*
+
+ TODO: Add some documentation here.
+
+ the SIGPIPE signal should be ignored (is ignored in this code)
+
+ This library is not thread safe. You cannot share TFILE objects between
+ threads and expect to be able to read and write from them in different
+ threads. All the state is in the TFILE object so calls to this library on
+ different objects can be done in parallel.
+
+*/
+
+#ifndef COMMON__TIO_H
+#define COMMON__TIO_H
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "attrs.h"
+
+/* This is a generic file handle used for reading and writing
+ (something like FILE from stdio.h). */
+typedef struct tio_fileinfo TFILE;
+
+/* Open a new TFILE based on the file descriptor. The timeout is set for any
+ operation (value in milliseconds). */
+TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
+ size_t initreadsize, size_t maxreadsize,
+ size_t initwritesize, size_t maxwritesize)
+ LIKE_MALLOC MUST_USE;
+
+/* Read the specified number of bytes from the stream. */
+int tio_read(TFILE *fp, void *buf, size_t count);
+
+/* Read and discard the specified number of bytes from the stream. */
+int tio_skip(TFILE *fp, size_t count);
+
+/* Read all available data from the stream and empty the read buffer. */
+int tio_skipall(TFILE *fp, int timeout);
+
+/* Write the specified buffer to the stream. */
+int tio_write(TFILE *fp, const void *buf, size_t count);
+
+/* Write out all buffered data to the stream. */
+int tio_flush(TFILE *fp);
+
+/* Flush the streams and closes the underlying file descriptor. */
+int tio_close(TFILE *fp);
+
+/* Store the current position in the stream so that we can jump back to it
+ with the tio_reset() function. */
+void tio_mark(TFILE *fp);
+
+/* Rewinds the stream to the point set by tio_mark(). Note that this only
+ resets the read stream and not the write stream. This function returns
+ whether the reset was successful (this function may fail if the buffers
+ were full). */
+int tio_reset(TFILE *fp);
+
+#endif /* COMMON__TIO_H */
diff --git a/contrib/slapd-modules/nssov/nssov.c b/contrib/slapd-modules/nssov/nssov.c
new file mode 100644
index 0000000..c8e4187
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nssov.c
@@ -0,0 +1,1045 @@
+/* nssov.c - nss-ldap overlay for slapd */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * Portions Copyright 2013 by Ted C. Cheng, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+#ifndef SLAPD_OVER_NSSOV
+#define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC
+#endif
+
+#include "slap-config.h"
+
+#include "lutil.h"
+
+#include <ac/errno.h>
+#include <ac/unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+AttributeDescription *nssov_pam_host_ad;
+AttributeDescription *nssov_pam_svc_ad;
+
+/* buffer sizes for I/O */
+#define READBUFFER_MINSIZE 32
+#define READBUFFER_MAXSIZE 64
+#define WRITEBUFFER_MINSIZE 64
+#define WRITEBUFFER_MAXSIZE 64*1024
+
+/* Find the given attribute's value in the RDN of the DN */
+void nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value)
+{
+ struct berval rdn;
+ char *next;
+
+ BER_BVZERO(value);
+ dnRdn( dn, &rdn );
+ do {
+ next = ber_bvchr( &rdn, '+' );
+ if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' &&
+ !ber_bvcmp( &rdn, &ad->ad_cname )) {
+ if ( next )
+ rdn.bv_len = next - rdn.bv_val;
+ value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1;
+ value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
+ break;
+ }
+ if ( !next )
+ break;
+ next++;
+ rdn.bv_len -= next - rdn.bv_val;
+ rdn.bv_val = next;
+ } while (1);
+}
+
+/* create a search filter using a name that requires escaping */
+int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf)
+{
+ char buf2[1024];
+ struct berval bv2 = {sizeof(buf2),buf2};
+
+ /* escape attribute */
+ if (nssov_escape(name,&bv2))
+ return -1;
+ /* build filter */
+ if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
+ bv2.bv_val );
+ return 0;
+}
+
+/* create a search filter using a string converted from an int */
+int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf)
+{
+ /* build filter */
+ if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
+ id->bv_val );
+ return 0;
+}
+
+void get_userpassword(struct berval *attr,struct berval *pw)
+{
+ int i;
+ /* go over the entries and return the remainder of the value if it
+ starts with {crypt} or crypt$ */
+ for (i=0;!BER_BVISNULL(&attr[i]);i++)
+ {
+ if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) {
+ pw->bv_val = attr[i].bv_val + 7;
+ pw->bv_len = attr[i].bv_len - 7;
+ return;
+ }
+ if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) {
+ pw->bv_val = attr[i].bv_val + 6;
+ pw->bv_len = attr[i].bv_len - 6;
+ return;
+ }
+ }
+ /* just return the first value completely */
+ *pw = *attr;
+ /* TODO: support more password formats e.g. SMD5
+ (which is $1$ but in a different format)
+ (any code for this is more than welcome) */
+}
+
+/* this writes a single address to the stream */
+int write_address(TFILE *fp,struct berval *addr)
+{
+ int32_t tmpint32;
+ struct in_addr ipv4addr;
+ struct in6_addr ipv6addr;
+ /* try to parse the address as IPv4 first, fall back to IPv6 */
+ if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0)
+ {
+ /* write address type */
+ WRITE_INT32(fp,AF_INET);
+ /* write the address length */
+ WRITE_INT32(fp,sizeof(struct in_addr));
+ /* write the address itself (in network byte order) */
+ WRITE(fp,&ipv4addr,sizeof(struct in_addr));
+ }
+ else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0)
+ {
+ /* write address type */
+ WRITE_INT32(fp,AF_INET6);
+ /* write the address length */
+ WRITE_INT32(fp,sizeof(struct in6_addr));
+ /* write the address itself (in network byte order) */
+ WRITE(fp,&ipv6addr,sizeof(struct in6_addr));
+ }
+ else
+ {
+ /* failure, log but write simple invalid address
+ (otherwise the address list is messed up) */
+ /* TODO: have error message in correct format */
+ Debug(LDAP_DEBUG_ANY,"nssov: unparsable address: %s\n",addr->bv_val );
+ /* write an illegal address type */
+ WRITE_INT32(fp,-1);
+ /* write an empty address */
+ WRITE_INT32(fp,0);
+ }
+ /* we're done */
+ return 0;
+}
+
+int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
+{
+ int32_t tmpint32;
+ int len;
+ /* read address family */
+ READ_INT32(fp,*af);
+ if ((*af!=AF_INET)&&(*af!=AF_INET6))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d\n",*af );
+ return -1;
+ }
+ /* read address length */
+ READ_INT32(fp,len);
+ if ((len>*addrlen)||(len<=0))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d\n",len );
+ return -1;
+ }
+ *addrlen=len;
+ /* read address */
+ READ(fp,addr,len);
+ /* we're done */
+ return 0;
+}
+
+int nssov_escape(struct berval *src,struct berval *dst)
+{
+ size_t pos=0;
+ int i;
+ /* go over all characters in source string */
+ for (i=0;i<src->bv_len;i++)
+ {
+ /* check if char will fit */
+ if (pos>=(dst->bv_len-4))
+ return -1;
+ /* do escaping for some characters */
+ switch (src->bv_val[i])
+ {
+ case '*':
+ strcpy(dst->bv_val+pos,"\\2a");
+ pos+=3;
+ break;
+ case '(':
+ strcpy(dst->bv_val+pos,"\\28");
+ pos+=3;
+ break;
+ case ')':
+ strcpy(dst->bv_val+pos,"\\29");
+ pos+=3;
+ break;
+ case '\\':
+ strcpy(dst->bv_val+pos,"\\5c");
+ pos+=3;
+ break;
+ default:
+ /* just copy character */
+ dst->bv_val[pos++]=src->bv_val[i];
+ break;
+ }
+ }
+ /* terminate destination string */
+ dst->bv_val[pos]='\0';
+ dst->bv_len = pos;
+ return 0;
+}
+
+/* read the version information and action from the stream
+ this function returns the read action in location pointer to by action */
+static int read_header(TFILE *fp,int32_t *action)
+{
+ int32_t tmpint32;
+ /* read the protocol version */
+ READ_INT32(fp,tmpint32);
+ if (tmpint32 != (int32_t)NSLCD_VERSION)
+ {
+ Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32 );
+ return -1;
+ }
+ /* read the request type */
+ READ_INT32(fp,*action);
+ return 0;
+}
+
+int nssov_config(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ int opt;
+ int32_t tmpint32;
+
+ READ_INT32(fp,opt);
+
+ Debug(LDAP_DEBUG_TRACE, "nssov_config (%d)\n",opt );
+
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_CONFIG_GET);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+
+ switch (opt) {
+ case NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE:
+ /* request for pam password_prohibit_message */
+ /* nssov_pam prohibits password */
+ if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_config(): %s (%s)\n",
+ "password_prohibit_message",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ WRITE_STRING(fp,ni->ni_pam_password_prohibit_message.bv_val);
+ }
+ default:
+ /* all other config options are ignored */
+ break;
+ }
+
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+
+/* read a request message, returns <0 in case of errors,
+ this function closes the socket */
+static void handleconnection(nssov_info *ni,int sock,Operation *op)
+{
+ TFILE *fp;
+ int32_t action;
+ int readtimeout,writetimeout;
+ uid_t uid;
+ gid_t gid;
+ char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
+ char peerbuf[8];
+ struct berval peerbv = { sizeof(peerbuf), peerbuf };
+
+ /* log connection */
+ if (LUTIL_GETPEEREID(sock,&uid,&gid,&peerbv)) {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d\n",
+ (int)uid,(int)gid );
+ }
+
+ /* Should do authid mapping too */
+ op->o_dn.bv_len = sprintf(authid,"gidNumber=%d+uidNumber=%d,cn=peercred,cn=external,cn=auth",
+ (int)gid, (int)uid );
+ op->o_dn.bv_val = authid;
+ op->o_ndn = op->o_dn;
+
+ /* set the timeouts:
+ * read timeout is half a second because clients should send their request
+ * quickly, write timeout is 60 seconds because clients could be taking some
+ * time to process the results
+ */
+ readtimeout = 500;
+ writetimeout = 60000;
+ /* create a stream object */
+ if ((fp=tio_fdopen(sock,readtimeout,writetimeout,
+ READBUFFER_MINSIZE,READBUFFER_MAXSIZE,
+ WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ (void)close(sock);
+ return;
+ }
+ /* read request */
+ if (read_header(fp,&action))
+ {
+ (void)tio_close(fp);
+ return;
+ }
+ /* handle request */
+ switch (action)
+ {
+ case NSLCD_ACTION_ALIAS_BYNAME: (void)nssov_alias_byname(ni,fp,op); break;
+ case NSLCD_ACTION_ALIAS_ALL: (void)nssov_alias_all(ni,fp,op); break;
+ case NSLCD_ACTION_ETHER_BYNAME: (void)nssov_ether_byname(ni,fp,op); break;
+ case NSLCD_ACTION_ETHER_BYETHER: (void)nssov_ether_byether(ni,fp,op); break;
+ case NSLCD_ACTION_ETHER_ALL: (void)nssov_ether_all(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_BYNAME: (void)nssov_group_byname(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_BYGID: (void)nssov_group_bygid(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_BYMEMBER: (void)nssov_group_bymember(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_ALL: (void)nssov_group_all(ni,fp,op); break;
+ case NSLCD_ACTION_HOST_BYNAME: (void)nssov_host_byname(ni,fp,op); break;
+ case NSLCD_ACTION_HOST_BYADDR: (void)nssov_host_byaddr(ni,fp,op); break;
+ case NSLCD_ACTION_HOST_ALL: (void)nssov_host_all(ni,fp,op); break;
+ case NSLCD_ACTION_NETGROUP_BYNAME: (void)nssov_netgroup_byname(ni,fp,op); break;
+ case NSLCD_ACTION_NETWORK_BYNAME: (void)nssov_network_byname(ni,fp,op); break;
+ case NSLCD_ACTION_NETWORK_BYADDR: (void)nssov_network_byaddr(ni,fp,op); break;
+ case NSLCD_ACTION_NETWORK_ALL: (void)nssov_network_all(ni,fp,op); break;
+ case NSLCD_ACTION_PASSWD_BYNAME: (void)nssov_passwd_byname(ni,fp,op); break;
+ case NSLCD_ACTION_PASSWD_BYUID: (void)nssov_passwd_byuid(ni,fp,op); break;
+ case NSLCD_ACTION_PASSWD_ALL: (void)nssov_passwd_all(ni,fp,op); break;
+ case NSLCD_ACTION_PROTOCOL_BYNAME: (void)nssov_protocol_byname(ni,fp,op); break;
+ case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break;
+ case NSLCD_ACTION_PROTOCOL_ALL: (void)nssov_protocol_all(ni,fp,op); break;
+ case NSLCD_ACTION_RPC_BYNAME: (void)nssov_rpc_byname(ni,fp,op); break;
+ case NSLCD_ACTION_RPC_BYNUMBER: (void)nssov_rpc_bynumber(ni,fp,op); break;
+ case NSLCD_ACTION_RPC_ALL: (void)nssov_rpc_all(ni,fp,op); break;
+ case NSLCD_ACTION_SERVICE_BYNAME: (void)nssov_service_byname(ni,fp,op); break;
+ case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break;
+ case NSLCD_ACTION_SERVICE_ALL: (void)nssov_service_all(ni,fp,op); break;
+ case NSLCD_ACTION_SHADOW_BYNAME: if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
+ case NSLCD_ACTION_SHADOW_ALL: if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_AUTHC: (void)pam_authc(ni,fp,op,uid); break;
+ case NSLCD_ACTION_PAM_AUTHZ: (void)pam_authz(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_SESS_O: if (uid==0) (void)pam_sess_o(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_SESS_C: if (uid==0) (void)pam_sess_c(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_PWMOD: (void)pam_pwmod(ni,fp,op,uid); break;
+ case NSLCD_ACTION_CONFIG_GET: (void)nssov_config(ni,fp,op); break;
+ default:
+ Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action );
+ break;
+ }
+ /* we're done with the request */
+ (void)tio_close(fp);
+ return;
+}
+
+/* accept a connection on the socket */
+static void *acceptconn(void *ctx, void *arg)
+{
+ nssov_info *ni = arg;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ int csock;
+
+ if ( slapd_shutdown )
+ return NULL;
+
+ {
+ struct sockaddr_storage addr;
+ socklen_t alen;
+ int j;
+
+ /* accept a new connection */
+ alen=(socklen_t)sizeof(struct sockaddr_storage);
+ csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen);
+ connection_client_enable(ni->ni_conn);
+ if (csock<0)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
+ {
+ Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ return NULL;
+ }
+ Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ return NULL;
+ }
+ /* make sure O_NONBLOCK is not inherited */
+ if ((j=fcntl(csock,F_GETFL,0))<0)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(csock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return NULL;
+ }
+ if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(csock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return NULL;
+ }
+ }
+ connection_fake_init( &conn, &opbuf, ctx );
+ op=&opbuf.ob_op;
+ conn.c_ssf = conn.c_transport_ssf = local_ssf;
+ op->o_bd = ni->ni_db;
+ op->o_tag = LDAP_REQ_SEARCH;
+
+ /* handle the connection */
+ handleconnection(ni,csock,op);
+
+ return NULL;
+}
+
+static slap_verbmasks nss_svcs[] = {
+ { BER_BVC("aliases"), NM_alias },
+ { BER_BVC("ethers"), NM_ether },
+ { BER_BVC("group"), NM_group },
+ { BER_BVC("hosts"), NM_host },
+ { BER_BVC("netgroup"), NM_netgroup },
+ { BER_BVC("networks"), NM_network },
+ { BER_BVC("passwd"), NM_passwd },
+ { BER_BVC("protocols"), NM_protocol },
+ { BER_BVC("rpc"), NM_rpc },
+ { BER_BVC("services"), NM_service },
+ { BER_BVC("shadow"), NM_shadow },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks pam_opts[] = {
+ { BER_BVC("userhost"), NI_PAM_USERHOST },
+ { BER_BVC("userservice"), NI_PAM_USERSVC },
+ { BER_BVC("usergroup"), NI_PAM_USERGRP },
+ { BER_BVC("hostservice"), NI_PAM_HOSTSVC },
+ { BER_BVC("authz2dn"), NI_PAM_SASL2DN },
+ { BER_BVC("uid2dn"), NI_PAM_UID2DN },
+ { BER_BVNULL, 0 }
+};
+
+enum {
+ NSS_SSD=1,
+ NSS_MAP,
+ NSS_PAM,
+ NSS_PAMGROUP,
+ NSS_PAMSESS
+};
+
+static ConfigDriver nss_cf_gen;
+
+static ConfigTable nsscfg[] = {
+ { "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD,
+ nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' "
+ "DESC 'URL for searches in a given service' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP,
+ nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' "
+ "DESC 'Map <service> lookups of <orig> attr to <new> attr' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-pam", "options", 2, 0, 0, ARG_MAGIC|NSS_PAM,
+ nss_cf_gen, "(OLcfgCtAt:3.3 NAME 'olcNssPam' "
+ "DESC 'PAM authentication and authorization options' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-pam-defhost", "hostname", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_defhost),
+ "(OLcfgCtAt:3.4 NAME 'olcNssPamDefHost' "
+ "DESC 'Default hostname for service checks' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-group-dn", "DN", 2, 2, 0, ARG_MAGIC|ARG_DN|NSS_PAMGROUP,
+ nss_cf_gen, "(OLcfgCtAt:3.5 NAME 'olcNssPamGroupDN' "
+ "DESC 'DN of group in which membership is required' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-group-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
+ (void *)offsetof(struct nssov_info, ni_pam_group_ad),
+ "(OLcfgCtAt:3.6 NAME 'olcNssPamGroupAD' "
+ "DESC 'Member attribute to use for group check' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-min-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
+ (void *)offsetof(struct nssov_info, ni_pam_min_uid),
+ "(OLcfgCtAt:3.7 NAME 'olcNssPamMinUid' "
+ "DESC 'Minimum UID allowed to login' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-max-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
+ (void *)offsetof(struct nssov_info, ni_pam_max_uid),
+ "(OLcfgCtAt:3.8 NAME 'olcNssPamMaxUid' "
+ "DESC 'Maximum UID allowed to login' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-template-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
+ (void *)offsetof(struct nssov_info, ni_pam_template_ad),
+ "(OLcfgCtAt:3.9 NAME 'olcNssPamTemplateAD' "
+ "DESC 'Attribute to use for template login name' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-template", "name", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_template),
+ "(OLcfgCtAt:3.10 NAME 'olcNssPamTemplate' "
+ "DESC 'Default template login name' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-session", "service", 2, 2, 0, ARG_MAGIC|NSS_PAMSESS,
+ nss_cf_gen, "(OLcfgCtAt:3.11 NAME 'olcNssPamSession' "
+ "DESC 'Services for which sessions will be recorded' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-pam-password-prohibit-message",
+ "password_prohibit_message", 2, 2, 0,
+ ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_password_prohibit_message),
+ "(OLcfgCtAt:3.12 NAME 'olcNssPamPwdProhibitMsg' "
+ "DESC 'Prohibit password modification message' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-pwdmgr-dn",
+ "pwdmgr_dn", 2, 2, 0,
+ ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_pwdmgr_dn),
+ "(OLcfgCtAt:3.13 NAME 'olcPamPwdmgrDn' "
+ "DESC 'Password Manager DN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-pwdmgr-pwd",
+ "pwdmgr_pwd", 2, 2, 0,
+ ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_pwdmgr_pwd),
+ "(OLcfgCtAt:3.14 NAME 'olcPamPwdmgrPwd' "
+ "DESC 'Password Manager Pwd' "
+ "EQUALITY octetStringMatch "
+ "SYNTAX OMsOctetString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0,0,0, ARG_IGNORED }
+};
+
+static ConfigOCs nssocs[] = {
+ { "( OLcfgCtOc:3.1 "
+ "NAME 'olcNssOvConfig' "
+ "DESC 'NSS lookup configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ "
+ "olcNssPamGroupDN $ olcNssPamGroupAD $ "
+ "olcNssPamMinUid $ olcNssPamMaxUid $ olcNssPamSession $ "
+ "olcNssPamTemplateAD $ olcNssPamTemplate ) )",
+ Cft_Overlay, nsscfg },
+ { NULL, 0, NULL }
+};
+
+static int
+nss_cf_gen(ConfigArgs *c)
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ nssov_info *ni = on->on_bi.bi_private;
+ nssov_mapinfo *mi;
+ int i, j, rc = 0;
+ slap_mask_t m;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch(c->type) {
+ case NSS_SSD:
+ rc = 1;
+ for (i=NM_alias;i<NM_NONE;i++) {
+ struct berval scope;
+ struct berval ssd;
+ struct berval base;
+
+ mi = &ni->ni_maps[i];
+
+ /* ignore all-default services */
+ if ( mi->mi_scope == LDAP_SCOPE_DEFAULT &&
+ bvmatch( &mi->mi_filter, &mi->mi_filter0 ) &&
+ BER_BVISNULL( &mi->mi_base ))
+ continue;
+
+ if ( BER_BVISNULL( &mi->mi_base ))
+ base = ni->ni_db->be_nsuffix[0];
+ else
+ base = mi->mi_base;
+ ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ?
+ LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope);
+ ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len +
+ base.bv_len + scope.bv_len + mi->mi_filter.bv_len;
+ ssd.bv_val = ch_malloc( ssd.bv_len + 1 );
+ sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val,
+ base.bv_val, scope.bv_val, mi->mi_filter.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &ssd );
+ rc = 0;
+ }
+ break;
+ case NSS_MAP:
+ rc = 1;
+ for (i=NM_alias;i<NM_NONE;i++) {
+
+ mi = &ni->ni_maps[i];
+ for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) {
+ if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j],
+ &mi->mi_attrs[j].an_name)) {
+ struct berval map;
+
+ map.bv_len = nss_svcs[i].word.bv_len +
+ mi->mi_attrkeys[j].bv_len +
+ mi->mi_attrs[j].an_desc->ad_cname.bv_len + 2;
+ map.bv_val = ch_malloc(map.bv_len + 1);
+ sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val,
+ mi->mi_attrkeys[j].bv_val, mi->mi_attrs[j].an_desc->ad_cname.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &map );
+ rc = 0;
+ }
+ }
+ }
+ break;
+ case NSS_PAM:
+ rc = mask_to_verbs( pam_opts, ni->ni_pam_opts, &c->rvalue_vals );
+ break;
+ case NSS_PAMGROUP:
+ if (!BER_BVISEMPTY( &ni->ni_pam_group_dn )) {
+ value_add_one( &c->rvalue_vals, &ni->ni_pam_group_dn );
+ value_add_one( &c->rvalue_nvals, &ni->ni_pam_group_dn );
+ } else {
+ rc = 1;
+ }
+ break;
+ case NSS_PAMSESS:
+ if (ni->ni_pam_sessions) {
+ ber_bvarray_dup_x( &c->rvalue_vals, ni->ni_pam_sessions, NULL );
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* FIXME */
+ return 1;
+ }
+ switch( c->type ) {
+ case NSS_SSD: {
+ LDAPURLDesc *lud;
+
+ i = verb_to_mask(c->argv[1], nss_svcs);
+ if ( i == NM_NONE )
+ return 1;
+
+ mi = &ni->ni_maps[i];
+ rc = ldap_url_parse(c->argv[2], &lud);
+ if ( rc )
+ return 1;
+ do {
+ struct berval base;
+ /* Must be LDAP scheme */
+ if (strcasecmp(lud->lud_scheme,"ldap")) {
+ rc = 1;
+ break;
+ }
+ /* Host part, attrs, and extensions must be empty */
+ if (( lud->lud_host && *lud->lud_host ) ||
+ lud->lud_attrs || lud->lud_exts ) {
+ rc = 1;
+ break;
+ }
+ ber_str2bv( lud->lud_dn,0,0,&base);
+ rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL);
+ if ( rc )
+ break;
+ if ( lud->lud_filter ) {
+ /* steal this */
+ ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter);
+ lud->lud_filter = NULL;
+ }
+ mi->mi_scope = lud->lud_scope;
+ } while(0);
+ ldap_free_urldesc( lud );
+ }
+ break;
+ case NSS_MAP:
+ i = verb_to_mask(c->argv[1], nss_svcs);
+ if ( i == NM_NONE )
+ return 1;
+ rc = 1;
+ mi = &ni->ni_maps[i];
+ for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
+ if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ rc = slap_str2ad( c->argv[3], &ad, &text);
+ if ( rc == 0 ) {
+ mi->mi_attrs[j].an_desc = ad;
+ mi->mi_attrs[j].an_name = ad->ad_cname;
+ }
+ break;
+ }
+ }
+ break;
+ case NSS_PAM:
+ m = ni->ni_pam_opts;
+ i = verbs_to_mask(c->argc, c->argv, pam_opts, &m);
+ if (i == 0) {
+ ni->ni_pam_opts = m;
+ if ((m & NI_PAM_USERHOST) && !nssov_pam_host_ad) {
+ const char *text;
+ i = slap_str2ad("host", &nssov_pam_host_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ snprintf(c->cr_msg, sizeof(c->cr_msg),
+ "nssov: host attr unknown: %s", text);
+ Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg );
+ rc = 1;
+ break;
+ }
+ }
+ if ((m & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) && !nssov_pam_svc_ad) {
+ const char *text;
+ i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ snprintf(c->cr_msg, sizeof(c->cr_msg),
+ "nssov: authorizedService attr unknown: %s", text);
+ Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg );
+ rc = 1;
+ break;
+ }
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+ case NSS_PAMGROUP:
+ ni->ni_pam_group_dn = c->value_ndn;
+ ch_free( c->value_dn.bv_val );
+ break;
+ case NSS_PAMSESS:
+ ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
+ ber_bvarray_add( &ni->ni_pam_sessions, &c->value_bv );
+ break;
+ }
+ return rc;
+}
+
+static int
+nssov_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ nssov_info *ni;
+ int rc;
+
+ rc = nssov_pam_init();
+ if (rc) return rc;
+
+ ni = ch_calloc( 1, sizeof(nssov_info) );
+ on->on_bi.bi_private = ni;
+
+ /* set up map keys */
+ nssov_alias_init(ni);
+ nssov_ether_init(ni);
+ nssov_group_init(ni);
+ nssov_host_init(ni);
+ nssov_netgroup_init(ni);
+ nssov_network_init(ni);
+ nssov_passwd_init(ni);
+ nssov_protocol_init(ni);
+ nssov_rpc_init(ni);
+ nssov_service_init(ni);
+ nssov_shadow_init(ni);
+
+ ni->ni_db = be->bd_self;
+ ni->ni_pam_opts = NI_PAM_UID2DN;
+
+ return 0;
+}
+
+static int
+nssov_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ return 0;
+}
+
+static int
+nssov_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ nssov_info *ni = on->on_bi.bi_private;
+ nssov_mapinfo *mi;
+
+ int i, sock;
+ struct sockaddr_un addr;
+
+ /* Set default bases */
+ for (i=0; i<NM_NONE; i++) {
+ if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) {
+ ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] );
+ }
+ if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT )
+ ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE;
+ }
+ /* validate attribute maps */
+ mi = ni->ni_maps;
+ for ( i=0; i<NM_NONE; i++,mi++) {
+ const char *text;
+ int j;
+ for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
+ /* skip attrs we already validated */
+ if ( mi->mi_attrs[j].an_desc ) continue;
+ if ( slap_bv2ad( &mi->mi_attrs[j].an_name,
+ &mi->mi_attrs[j].an_desc, &text )) {
+ Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n",
+ mi->mi_attrs[j].an_name.bv_val, text );
+ return -1;
+ }
+ }
+ BER_BVZERO(&mi->mi_attrs[j].an_name);
+ mi->mi_attrs[j].an_desc = NULL;
+ }
+
+ /* Find host and authorizedService definitions */
+ if ((ni->ni_pam_opts & NI_PAM_USERHOST) && !nssov_pam_host_ad)
+ {
+ const char *text;
+ i = slap_str2ad("host", &nssov_pam_host_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,"nssov: host attr unknown: %s\n",
+ text );
+ return -1;
+ }
+ }
+ if ((ni->ni_pam_opts & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) &&
+ !nssov_pam_svc_ad)
+ {
+ const char *text;
+ i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,"nssov: authorizedService attr unknown: %s\n",
+ text );
+ return -1;
+ }
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ char ebuf[128];
+ /* make sure /var/run/nslcd exists */
+ if (mkdir(NSLCD_PATH, (mode_t) 0555)) {
+ int saved_errno = errno;
+ Debug(LDAP_DEBUG_TRACE,"nssov: mkdir(%s) failed (ignored): %s\n",
+ NSLCD_PATH, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ } else {
+ Debug(LDAP_DEBUG_TRACE,"nssov: created %s\n",NSLCD_PATH );
+ }
+
+ /* create a socket */
+ if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
+ {
+ int saved_errno = errno;
+ Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ return -1;
+ }
+ /* remove existing named socket */
+ if (unlink(NSLCD_SOCKET)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ /* create socket address structure */
+ memset(&addr,0,sizeof(struct sockaddr_un));
+ addr.sun_family=AF_UNIX;
+ strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path));
+ addr.sun_path[sizeof(addr.sun_path)-1]='\0';
+ /* bind to the named socket */
+ if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)))
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ /* close the file descriptor on exit */
+ if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ /* set permissions of socket so anybody can do requests */
+ /* Note: we use chmod() here instead of fchmod() because
+ fchmod does not work on sockets
+ http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
+ http://lkml.org/lkml/2005/5/16/11 */
+ if (chmod(NSLCD_SOCKET,(mode_t)0666))
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ /* start listening for connections */
+ if (listen(sock,SOMAXCONN)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ ni->ni_socket = sock;
+ ni->ni_conn = connection_client_setup( sock, acceptconn, ni );
+ }
+
+ return 0;
+}
+
+static int
+nssov_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ nssov_info *ni = on->on_bi.bi_private;
+
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ char ebuf[128];
+ /* close socket if it's still in use */
+ if (ni->ni_socket >= 0)
+ {
+ if (close(ni->ni_socket)) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ ni->ni_socket = -1;
+ }
+ /* remove existing named socket */
+ if (unlink(NSLCD_SOCKET)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ }
+ return 0;
+}
+
+static slap_overinst nssov;
+
+int
+nssov_initialize( void )
+{
+ int rc;
+
+ nssov.on_bi.bi_type = "nssov";
+ nssov.on_bi.bi_db_init = nssov_db_init;
+ nssov.on_bi.bi_db_destroy = nssov_db_destroy;
+ nssov.on_bi.bi_db_open = nssov_db_open;
+ nssov.on_bi.bi_db_close = nssov_db_close;
+
+ nssov.on_bi.bi_cf_ocs = nssocs;
+
+ rc = config_register_schema( nsscfg, nssocs );
+ if ( rc ) return rc;
+
+ return overlay_register(&nssov);
+}
+
+#if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return nssov_initialize();
+}
+#endif
diff --git a/contrib/slapd-modules/nssov/nssov.h b/contrib/slapd-modules/nssov/nssov.h
new file mode 100644
index 0000000..ce1ecd7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nssov.h
@@ -0,0 +1,348 @@
+/* nssov.h - NSS overlay header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Howard Chu.
+ * Portions Copyright 2013 Ted C. Cheng, Symas Corp.
+ * 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>.
+ */
+
+#ifndef NSSOV_H
+#define NSSOV_H
+
+#ifndef NSLCD_PATH
+#define NSLCD_PATH "/var/run/nslcd"
+#endif
+
+#ifndef NSLCD_SOCKET
+#define NSLCD_SOCKET NSLCD_PATH "/socket"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "nslcd.h"
+#include "nslcd-prot.h"
+#include "tio.h"
+#include "attrs.h"
+
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+
+#include "portable.h"
+#include "slap.h"
+#include <ac/string.h>
+
+/* selectors for different maps */
+enum nssov_map_selector
+{
+ NM_alias,
+ NM_ether,
+ NM_group,
+ NM_host,
+ NM_netgroup,
+ NM_network,
+ NM_passwd,
+ NM_protocol,
+ NM_rpc,
+ NM_service,
+ NM_shadow,
+ NM_NONE
+};
+
+typedef struct nssov_mapinfo {
+ struct berval mi_base;
+ int mi_scope;
+ struct berval mi_filter0;
+ struct berval mi_filter;
+ struct berval *mi_attrkeys;
+ AttributeName *mi_attrs;
+} nssov_mapinfo;
+
+typedef struct nssov_info
+{
+ /* search timelimit */
+ int ni_timelimit;
+ struct nssov_mapinfo ni_maps[NM_NONE];
+ int ni_socket;
+ Connection *ni_conn;
+ BackendDB *ni_db;
+
+ /* PAM authz support... */
+ slap_mask_t ni_pam_opts;
+ struct berval ni_pam_group_dn;
+ AttributeDescription *ni_pam_group_ad;
+ int ni_pam_min_uid;
+ int ni_pam_max_uid;
+ AttributeDescription *ni_pam_template_ad;
+ struct berval ni_pam_template;
+ struct berval ni_pam_defhost;
+ struct berval *ni_pam_sessions;
+ struct berval ni_pam_password_prohibit_message;
+ struct berval ni_pam_pwdmgr_dn;
+ struct berval ni_pam_pwdmgr_pwd;
+} nssov_info;
+
+#define NI_PAM_USERHOST 1 /* old style host checking */
+#define NI_PAM_USERSVC 2 /* old style service checking */
+#define NI_PAM_USERGRP 4 /* old style group checking */
+#define NI_PAM_HOSTSVC 8 /* new style authz checking */
+#define NI_PAM_SASL2DN 0x10 /* use sasl2dn */
+#define NI_PAM_UID2DN 0x20 /* use uid2dn */
+
+#define NI_PAM_OLD (NI_PAM_USERHOST|NI_PAM_USERSVC|NI_PAM_USERGRP)
+#define NI_PAM_NEW NI_PAM_HOSTSVC
+
+extern AttributeDescription *nssov_pam_host_ad;
+extern AttributeDescription *nssov_pam_svc_ad;
+
+/* Read the default configuration file. */
+void nssov_cfg_init(nssov_info *ni,const char *fname);
+
+/* macros for basic read and write operations, the following
+ ERROR_OUT* marcos define the action taken on errors
+ the stream is not closed because the caller closes the
+ stream */
+
+#define ERROR_OUT_WRITEERROR(fp) \
+ Debug(LDAP_DEBUG_ANY,"nssov: error writing to client\n"); \
+ return -1;
+
+#define ERROR_OUT_READERROR(fp) \
+ Debug(LDAP_DEBUG_ANY,"nssov: error reading from client\n"); \
+ return -1;
+
+#define ERROR_OUT_BUFERROR(fp) \
+ Debug(LDAP_DEBUG_ANY,"nssov: client supplied argument too large\n"); \
+ return -1;
+
+#define WRITE_BERVAL(fp, bv) \
+ DEBUG_PRINT("WRITE_BERVAL: var="__STRING(bv)" bv_val=\"%s\"", (bv)->bv_val); \
+ if ((bv) == NULL) \
+ { \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ WRITE_INT32(fp, (bv)->bv_len); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 > 0) \
+ { \
+ WRITE(fp, (bv)->bv_val, tmpint32); \
+ } \
+ } \
+
+#define WRITE_BVARRAY(fp, arr) \
+ if ((arr) == NULL) \
+ { \
+ DEBUG_PRINT("WRITE_BVARRAY: var="__STRING(arr)" num=%d", 0); \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ /* first determine length of array */ \
+ for (tmp3int32 = 0; (arr)[tmp3int32].bv_val != NULL; tmp3int32++) \
+ /* nothing */ ; \
+ /* write number of strings */ \
+ DEBUG_PRINT("WRITE_BVARRAY: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ WRITE_BERVAL(fp, &(arr)[tmp2int32]); \
+ } \
+ } \
+
+/* Find the given attribute's value in the RDN of the DN. */
+void nssov_find_rdnval(struct berval *dn,AttributeDescription *ad,struct berval *value);
+
+/* This tries to get the user password attribute from the entry.
+ It will try to return an encrypted password as it is used in /etc/passwd,
+ /etc/group or /etc/shadow depending upon what is in the directory.
+ This function will return NULL if no passwd is found and will return the
+ literal value in the directory if conversion is not possible. */
+void get_userpassword(struct berval *attr, struct berval *pw);
+
+/* write out an address, parsing the addr value */
+int write_address(TFILE *fp,struct berval *addr);
+
+/* a helper macro to write out addresses and bail out on errors */
+#define WRITE_ADDRESS(fp,addr) \
+ if (write_address(fp,addr)) \
+ return -1;
+
+/* read an address from the stream */
+int read_address(TFILE *fp,char *addr,int *addrlen,int *af);
+
+/* helper macro to read an address from the stream */
+#define READ_ADDRESS(fp,addr,len,af) \
+ len=(int)sizeof(addr); \
+ if (read_address(fp,addr,&(len),&(af))) \
+ return -1;
+
+/* checks to see if the specified string is a valid username */
+int isvalidusername(struct berval *name);
+
+/* transforms the DN into a uid doing an LDAP lookup if needed */
+int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid);
+
+/* transforms the uid into a DN by doing an LDAP lookup */
+int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn);
+int nssov_name2dn_cb(Operation *op, SlapReply *rs);
+
+/* Escapes characters in a string for use in a search filter. */
+int nssov_escape(struct berval *src,struct berval *dst);
+
+int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf);
+int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf);
+
+void nssov_alias_init(nssov_info *ni);
+void nssov_ether_init(nssov_info *ni);
+void nssov_group_init(nssov_info *ni);
+void nssov_host_init(nssov_info *ni);
+void nssov_netgroup_init(nssov_info *ni);
+void nssov_network_init(nssov_info *ni);
+void nssov_passwd_init(nssov_info *ni);
+void nssov_protocol_init(nssov_info *ni);
+void nssov_rpc_init(nssov_info *ni);
+void nssov_service_init(nssov_info *ni);
+void nssov_shadow_init(nssov_info *ni);
+
+int nssov_pam_init(void);
+
+/* these are the different functions that handle the database
+ specific actions, see nslcd.h for the action descriptions */
+int nssov_alias_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_alias_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_ether_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_ether_byether(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_ether_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_bygid(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_bymember(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_host_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_host_byaddr(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_host_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_netgroup_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_network_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_network_byaddr(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_network_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_passwd_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_passwd_byuid(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_passwd_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_protocol_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_protocol_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_protocol_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_rpc_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_rpc_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_rpc_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_service_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_service_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_service_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_shadow_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_shadow_all(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid);
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid);
+
+/* config initialization */
+#define NSSOV_INIT(db) \
+ void nssov_##db##_init(nssov_info *ni) \
+ { \
+ nssov_mapinfo *mi = &ni->ni_maps[NM_##db]; \
+ int i; \
+ for (i=0;!BER_BVISNULL(&db##_keys[i]);i++); \
+ i++; \
+ mi->mi_attrs = ch_malloc( i*sizeof(AttributeName)); \
+ for (i=0;!BER_BVISNULL(&db##_keys[i]);i++) { \
+ mi->mi_attrs[i].an_name = db##_keys[i]; \
+ mi->mi_attrs[i].an_desc = NULL; \
+ } \
+ mi->mi_scope = LDAP_SCOPE_DEFAULT; \
+ mi->mi_filter0 = db##_filter; \
+ ber_dupbv( &mi->mi_filter, &mi->mi_filter0 ); \
+ mi->mi_filter = db##_filter; \
+ mi->mi_attrkeys = db##_keys; \
+ BER_BVZERO(&mi->mi_base); \
+ }
+
+/* param structure for search callback */
+#define NSSOV_CBPRIV(db,parms) \
+ typedef struct nssov_##db##_cbp { \
+ nssov_mapinfo *mi; \
+ TFILE *fp; \
+ Operation *op; \
+ parms \
+ } nssov_##db##_cbp
+
+/* callback for writing search results */
+#define NSSOV_CB(db) \
+ static int nssov_##db##_cb(Operation *op, SlapReply *rs) \
+ { \
+ if ( rs->sr_type == REP_SEARCH ) { \
+ nssov_##db##_cbp *cbp = op->o_callback->sc_private; \
+ if (write_##db(cbp,rs->sr_entry)) return LDAP_OTHER; \
+ } \
+ return LDAP_SUCCESS; \
+ } \
+
+/* macro for generating service handling code */
+#define NSSOV_HANDLE(db,fn,readfn,logcall,action,mkfilter) \
+ int nssov_##db##_##fn(nssov_info *ni,TFILE *fp,Operation *op) \
+ { \
+ /* define common variables */ \
+ int32_t tmpint32; \
+ nssov_##db##_cbp cbp; \
+ slap_callback cb = {0}; \
+ SlapReply rs = {REP_RESULT}; \
+ cbp.mi = &ni->ni_maps[NM_##db]; \
+ cbp.fp = fp; \
+ cbp.op = op; \
+ /* read request parameters */ \
+ readfn; \
+ /* log call */ \
+ logcall; \
+ /* write the response header */ \
+ WRITE_INT32(fp,NSLCD_VERSION); \
+ WRITE_INT32(fp,action); \
+ /* prepare the search filter */ \
+ if (mkfilter) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"nssov_" __STRING(db) "_" __STRING(fn) "(): filter buffer too small"); \
+ return -1; \
+ } \
+ cb.sc_private = &cbp; \
+ op->o_callback = &cb; \
+ cb.sc_response = nssov_##db##_cb; \
+ slap_op_time( &op->o_time, &op->o_tincr ); \
+ op->o_req_dn = cbp.mi->mi_base; \
+ op->o_req_ndn = cbp.mi->mi_base; \
+ op->ors_scope = cbp.mi->mi_scope; \
+ op->ors_filterstr = filter; \
+ op->ors_filter = str2filter_x( op, filter.bv_val ); \
+ op->ors_attrs = cbp.mi->mi_attrs; \
+ op->ors_tlimit = SLAP_NO_LIMIT; \
+ op->ors_slimit = SLAP_NO_LIMIT; \
+ /* do the internal search */ \
+ op->o_bd->be_search( op, &rs ); \
+ filter_free_x( op, op->ors_filter, 1 ); \
+ WRITE_INT32(fp,NSLCD_RESULT_END); \
+ return 0; \
+ }
+
+#endif /* NSSOV_H */
diff --git a/contrib/slapd-modules/nssov/pam.c b/contrib/slapd-modules/nssov/pam.c
new file mode 100644
index 0000000..1d416c7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/pam.c
@@ -0,0 +1,862 @@
+/* pam.c - pam processing routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * Portions Copyright 2013 by Ted C. Cheng, Symas Corp.
+ * 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>.
+ */
+
+#include "nssov.h"
+#include "lutil.h"
+
+#undef ldap_debug /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h" /* for ldap_ld_free */
+
+static int ppolicy_cid;
+static AttributeDescription *ad_loginStatus;
+
+struct paminfo {
+ struct berval uid;
+ struct berval dn;
+ struct berval svc;
+ struct berval ruser;
+ struct berval rhost;
+ struct berval tty;
+ struct berval pwd;
+ int authz;
+ struct berval msg;
+ int ispwdmgr;
+};
+
+static int pam_bindcb(
+ Operation *op, SlapReply *rs)
+{
+ struct paminfo *pi = op->o_callback->sc_private;
+ LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
+ rs->sr_ctrls, NULL);
+ if (ctrl) {
+ LDAP *ld;
+ ber_int_t expire, grace;
+ LDAPPasswordPolicyError error;
+
+ ldap_create(&ld);
+ if (ld) {
+ int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
+ &expire,&grace,&error);
+ if (rc == LDAP_SUCCESS) {
+ if (expire >= 0) {
+ char *unit = "seconds";
+ if (expire > 60) {
+ expire /= 60;
+ unit = "minutes";
+ }
+ if (expire > 60) {
+ expire /= 60;
+ unit = "hours";
+ }
+ if (expire > 24) {
+ expire /= 24;
+ unit = "days";
+ }
+#if 0 /* Who warns about expiration so far in advance? */
+ if (expire > 7) {
+ expire /= 7;
+ unit = "weeks";
+ }
+ if (expire > 4) {
+ expire /= 4;
+ unit = "months";
+ }
+ if (expire > 12) {
+ expire /= 12;
+ unit = "years";
+ }
+#endif
+ pi->msg.bv_len = sprintf(pi->msg.bv_val,
+ "\nWARNING: Password expires in %d %s\n", expire, unit);
+ } else if (grace > 0) {
+ pi->msg.bv_len = sprintf(pi->msg.bv_val,
+ "Password expired; %d grace logins remaining",
+ grace);
+ pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+ } else if (error != PP_noError) {
+ ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
+ &pi->msg);
+ switch (error) {
+ case PP_passwordExpired:
+ /* report this during authz */
+ rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+ case PP_changeAfterReset:
+ pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+ }
+ }
+ }
+ ldap_ld_free(ld,0,NULL,NULL);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int pam_uid2dn(nssov_info *ni, Operation *op,
+ struct paminfo *pi)
+{
+ struct berval sdn;
+
+ BER_BVZERO(&pi->dn);
+
+ if (!isvalidusername(&pi->uid)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_pam_uid2dn(%s): invalid user name\n",
+ pi->uid.bv_val ? pi->uid.bv_val : "NULL" );
+ return NSLCD_PAM_USER_UNKNOWN;
+ }
+
+ if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
+ int hlen = global_host_bv.bv_len;
+
+ /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
+ sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
+ STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
+ sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
+ sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
+ pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
+ slap_sasl2dn(op, &sdn, &pi->dn, 0);
+ op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
+ }
+
+ /* If no luck, do a basic uid search */
+ if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
+ nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
+ if (!BER_BVISEMPTY(&pi->dn)) {
+ sdn = pi->dn;
+ dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
+ }
+ }
+ if (BER_BVISEMPTY(&pi->dn)) {
+ return NSLCD_PAM_USER_UNKNOWN;
+ }
+ return 0;
+}
+
+int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
+ struct paminfo *pi)
+{
+ int rc;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+
+ pi->msg.bv_val = pi->pwd.bv_val;
+ pi->msg.bv_len = 0;
+ pi->authz = NSLCD_PAM_SUCCESS;
+
+ if (!pi->ispwdmgr) {
+
+ rc = pam_uid2dn(ni, op, pi);
+ if (rc) goto finish;
+
+ if (BER_BVISEMPTY(&pi->pwd)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+
+ /* Should only need to do this once at open time, but there's always
+ * the possibility that ppolicy will get loaded later.
+ */
+ if (!ppolicy_cid) {
+ rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ &ppolicy_cid);
+ }
+ /* of course, 0 is a valid cid, but it won't be ppolicy... */
+ if (ppolicy_cid) {
+ op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
+ }
+ }
+
+ cb.sc_response = pam_bindcb;
+ cb.sc_private = pi;
+ op->o_callback = &cb;
+ op->o_dn.bv_val[0] = 0;
+ op->o_dn.bv_len = 0;
+ op->o_ndn.bv_val[0] = 0;
+ op->o_ndn.bv_len = 0;
+ op->o_tag = LDAP_REQ_BIND;
+ op->o_protocol = LDAP_VERSION3;
+ op->orb_method = LDAP_AUTH_SIMPLE;
+ op->orb_cred = pi->pwd;
+ op->o_req_dn = pi->dn;
+ op->o_req_ndn = pi->dn;
+ slap_op_time( &op->o_time, &op->o_tincr );
+ rc = op->o_bd->be_bind( op, &rs );
+ memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
+ /* quirk: on successful bind, caller has to send result. we need
+ * to make sure callbacks run.
+ */
+ if (rc == LDAP_SUCCESS)
+ send_ldap_result(op, &rs);
+ switch(rs.sr_err) {
+ case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
+ case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
+ default: rc = NSLCD_PAM_AUTH_ERR; break;
+ }
+finish:
+ Debug(LDAP_DEBUG_ANY,"pam_do_bind (%s): rc (%d)\n",
+ pi->dn.bv_val ? pi->dn.bv_val : "NULL", rc );
+ return rc;
+}
+
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid)
+{
+ int32_t tmpint32;
+ int rc;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ char pwdc[256];
+ struct paminfo pi;
+
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+ READ_STRING(fp,pwdc);
+ pi.pwd.bv_val = pwdc;
+ pi.pwd.bv_len = tmpint32;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",
+ pi.uid.bv_val ? pi.uid.bv_val : "NULL" );
+
+ BER_BVZERO(&pi.msg);
+ pi.ispwdmgr = 0;
+
+ /* if service is "passwd" and "nssov-pam-password-prohibit-message */
+ /* is set, deny the auth request */
+ if (!strcmp(svcc, "passwd") &&
+ !BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(): %s (%s)\n",
+ "password_prohibit_message for passwd",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+
+ /* if username is null, pwdmgr password preliminary check */
+ if (BER_BVISEMPTY(&pi.uid)) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) {
+ /* pwdmgr dn not configured */
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "pwdmgr dn not configured" );
+ ber_str2bv("pwdmgr dn not configured", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ } else if (calleruid != 0) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "caller is not root" );
+ ber_str2bv("only root may do that", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ } else {
+ /* use pwdmgr dn */
+ ber_str2bv(ni->ni_pam_pwdmgr_dn.bv_val, 0, 0, &pi.dn);
+ }
+
+ /* use pwdmgr pwd if configured */
+ if (BER_BVISEMPTY(&pi.pwd)) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_pwd)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "no pwdmgr pwd" );
+ ber_str2bv("pwdmgr pwd not configured", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ /* use configured pwdmgr pwd */
+ memset((void *) pwdc, 0, 256);
+ strncpy(pi.pwd.bv_val, ni->ni_pam_pwdmgr_pwd.bv_val,
+ ni->ni_pam_pwdmgr_pwd.bv_len);
+ pi.pwd.bv_len = ni->ni_pam_pwdmgr_pwd.bv_len;
+ }
+ pi.ispwdmgr = 1;
+ }
+
+
+ rc = pam_do_bind(ni, fp, op, &pi);
+
+finish:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s): rc (%d)\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL",rc );
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&pi.uid);
+ WRITE_INT32(fp,pi.authz); /* authz */
+ WRITE_BERVAL(fp,&pi.msg); /* authzmsg */
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+static struct berval grpmsg =
+ BER_BVC("Access denied by group check");
+static struct berval hostmsg =
+ BER_BVC("Access denied for this host");
+static struct berval svcmsg =
+ BER_BVC("Access denied for this service");
+static struct berval uidmsg =
+ BER_BVC("Access denied by UID check");
+
+static int pam_compare_cb(Operation *op, SlapReply *rs)
+{
+ if (rs->sr_err == LDAP_COMPARE_TRUE)
+ op->o_callback->sc_private = (void *)1;
+ return LDAP_SUCCESS;
+}
+
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ struct berval authzmsg = BER_BVNULL;
+ int32_t tmpint32;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ int rc;
+ struct paminfo pi;
+ Entry *e = NULL;
+ Attribute *a;
+ slap_callback cb = {0};
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto finish;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL" );
+
+ /* See if they have access to the host and service */
+ if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ struct berval hostdn = BER_BVNULL;
+ struct berval odn = op->o_ndn;
+ SlapReply rs = {REP_RESULT};
+ op->o_dn = pi.dn;
+ op->o_ndn = pi.dn;
+ {
+ nssov_mapinfo *mi = &ni->ni_maps[NM_host];
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf),fbuf};
+ SlapReply rs2 = {REP_RESULT};
+
+ /* Lookup the host entry */
+ nssov_filter_byname(mi,0,&global_host_bv,&filter);
+ cb.sc_private = &hostdn;
+ cb.sc_response = nssov_name2dn_cb;
+ op->o_callback = &cb;
+ op->o_req_dn = mi->mi_base;
+ op->o_req_ndn = mi->mi_base;
+ op->ors_scope = mi->mi_scope;
+ op->ors_filterstr = filter;
+ op->ors_filter = str2filter_x(op, filter.bv_val);
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = 2;
+ rc = op->o_bd->be_search(op, &rs2);
+ filter_free_x(op, op->ors_filter, 1);
+
+ if (BER_BVISEMPTY(&hostdn) &&
+ !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
+ filter.bv_len = sizeof(fbuf);
+ filter.bv_val = fbuf;
+ rs_reinit(&rs2, REP_RESULT);
+ nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
+ op->ors_filterstr = filter;
+ op->ors_filter = str2filter_x(op, filter.bv_val);
+ rc = op->o_bd->be_search(op, &rs2);
+ filter_free_x(op, op->ors_filter, 1);
+ }
+
+ /* no host entry, no default host -> deny */
+ if (BER_BVISEMPTY(&hostdn)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = hostmsg;
+ goto finish;
+ }
+ }
+
+ cb.sc_response = pam_compare_cb;
+ cb.sc_private = NULL;
+ op->o_tag = LDAP_REQ_COMPARE;
+ op->o_req_dn = hostdn;
+ op->o_req_ndn = hostdn;
+ ava.aa_desc = nssov_pam_svc_ad;
+ ava.aa_value = pi.svc;
+ op->orc_ava = &ava;
+ rc = op->o_bd->be_compare( op, &rs );
+ if ( cb.sc_private == NULL ) {
+ authzmsg = svcmsg;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ op->o_dn = odn;
+ op->o_ndn = odn;
+ }
+
+ /* See if they're a member of the group */
+ if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
+ !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
+ ni->ni_pam_group_ad) {
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ SlapReply rs = {REP_RESULT};
+ op->o_callback = &cb;
+ cb.sc_response = pam_compare_cb;
+ cb.sc_private = NULL;
+ op->o_tag = LDAP_REQ_COMPARE;
+ op->o_req_dn = ni->ni_pam_group_dn;
+ op->o_req_ndn = ni->ni_pam_group_dn;
+ ava.aa_desc = ni->ni_pam_group_ad;
+ ava.aa_value = pi.dn;
+ op->orc_ava = &ava;
+ rc = op->o_bd->be_compare( op, &rs );
+ if ( cb.sc_private == NULL ) {
+ authzmsg = grpmsg;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ }
+
+ /* We need to check the user's entry for these bits */
+ if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
+ ni->ni_pam_template_ad ||
+ ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
+ rc = be_entry_get_rw( op, &pi.dn, NULL, NULL, 0, &e );
+ if (rc != LDAP_SUCCESS) {
+ rc = NSLCD_PAM_USER_UNKNOWN;
+ goto finish;
+ }
+ }
+ if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
+ a = attr_find(e->e_attrs, nssov_pam_host_ad);
+ if (!a || attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &global_host_bv, NULL, op->o_tmpmemctx )) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = hostmsg;
+ goto finish;
+ }
+ }
+ if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
+ a = attr_find(e->e_attrs, nssov_pam_svc_ad);
+ if (!a || attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &pi.svc, NULL, op->o_tmpmemctx )) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = svcmsg;
+ goto finish;
+ }
+ }
+
+/* from passwd.c */
+#define UIDN_KEY 2
+
+ if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
+ int id;
+ char *tmp;
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
+ if (!a) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
+ (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ }
+
+ if (ni->ni_pam_template_ad) {
+ a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
+ if (a)
+ pi.uid = a->a_vals[0];
+ else if (!BER_BVISEMPTY(&ni->ni_pam_template))
+ pi.uid = ni->ni_pam_template;
+ }
+ rc = NSLCD_PAM_SUCCESS;
+
+finish:
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&authzmsg);
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ if (e) {
+ be_entry_release_r(op, e);
+ }
+ switch (rc) {
+ case NSLCD_PAM_SUCCESS:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): success\n" );
+ break;
+ case NSLCD_PAM_PERM_DENIED:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): %s\n",
+ authzmsg.bv_val ? authzmsg.bv_val : "NULL" );
+ break;
+ default:
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_authz(): permission denied, rc (%d)\n",
+ rc );
+ }
+ return 0;
+}
+
+static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
+{
+ int32_t tmpint32;
+ char svcc[256];
+ char uidc[32];
+ char ttyc[32];
+ char rhostc[256];
+ char ruserc[32];
+ char sessionID[64];
+ struct paminfo pi;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
+ struct berval timestamp, bv[2], *nbv;
+ time_t stamp;
+ Modifications mod;
+ int rc = 0;
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+
+ if (action==NSLCD_ACTION_PAM_SESS_O) {
+ slap_op_time( &op->o_time, &op->o_tincr );
+ timestamp.bv_len = sizeof(timebuf);
+ timestamp.bv_val = timebuf;
+ stamp = op->o_time;
+ slap_timestamp( &stamp, &timestamp );
+ } else {
+ READ_STRING(fp,sessionID);
+ timestamp.bv_val = sessionID;
+ timestamp.bv_len = tmpint32;
+ }
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto done;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', pi.dn.bv_val );
+
+ if (!ni->ni_pam_sessions) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(): %s\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c',
+ "pam session(s) not configured, ignored" );
+ rc = -1;
+ goto done;
+ }
+
+ {
+ int i, found=0;
+ for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
+ if (ni->ni_pam_sessions[i].bv_len != pi.svc.bv_len)
+ continue;
+ if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, pi.svc.bv_val)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): service(%s) not configured, ignored\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c',
+ pi.svc.bv_val );
+ rc = -1;
+ goto done;
+ }
+ }
+
+ bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + pi.svc.bv_len +
+ pi.tty.bv_len + pi.ruser.bv_len + pi.rhost.bv_len + STRLENOF(" (@)");
+ bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
+ sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
+ timestamp.bv_val, global_host_bv.bv_val, pi.svc.bv_val, pi.tty.bv_val,
+ pi.ruser.bv_val, pi.rhost.bv_val);
+
+ Debug(LDAP_DEBUG_TRACE, "nssov_pam_sess_%c(): loginStatus (%s) \n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', bv[0].bv_val );
+
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ BER_BVZERO(&bv[1]);
+ attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
+ mod.sml_nvalues = nbv;
+ mod.sml_desc = ad_loginStatus;
+ mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
+ LDAP_MOD_DELETE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_callback = &cb;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = pi.dn;
+ op->o_req_ndn = pi.dn;
+ if (op->o_bd->be_modify( op, &rs ) != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): modify op failed\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c' );
+ rc = -1;
+ }
+
+ if ( mod.sml_next ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+ ber_bvarray_free_x( nbv, op->o_tmpmemctx );
+
+done:;
+
+ if (rc == 0) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): success\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c' );
+ }
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,action);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ if (action==NSLCD_ACTION_PAM_SESS_O)
+ WRITE_STRING(fp,timestamp.bv_val);
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
+}
+
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
+}
+
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid)
+{
+ struct berval npw;
+ int32_t tmpint32;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ int asroot;
+ char opwc[256];
+ char npwc[256];
+ struct paminfo pi;
+ int rc;
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = svcc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = svcc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = svcc;
+ pi.tty.bv_len = tmpint32;
+ READ_INT32(fp, asroot);
+ READ_STRING(fp,opwc);
+ pi.pwd.bv_val = opwc;
+ pi.pwd.bv_len = tmpint32;
+ READ_STRING(fp,npwc);
+ npw.bv_val = npwc;
+ npw.bv_len = tmpint32;
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto done;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s %s\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL",
+ pi.uid.bv_val ? pi.uid.bv_val : "NULL",
+ asroot ? "as root" : "as user");
+
+ BER_BVZERO(&pi.msg);
+ pi.ispwdmgr = 0;
+
+ /* nssov_pam prohibits password mod */
+ if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s (%s)\n",
+ "password_prohibit_message",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+
+ if (asroot) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n",
+ "pwdmgr not configured" );
+ ber_str2bv("pwdmgr not configured", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+ if (calleruid != 0) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s\n",
+ "caller is not root" );
+ ber_str2bv("only root may do that", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+ /* root user requesting pwmod */
+ pi.ispwdmgr = 1;
+ }
+
+ if (!pi.ispwdmgr && BER_BVISEMPTY(&pi.pwd)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n",
+ "not pwdmgr and old pwd empty" );
+ ber_str2bv("must provide old password", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval bv;
+ SlapReply rs = {REP_RESULT};
+ slap_callback cb = {0};
+
+ ber_init_w_nullc(ber, LBER_USE_DER);
+ ber_printf(ber, "{");
+ if (!BER_BVISEMPTY(&pi.dn))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
+ &pi.dn);
+ /* supply old pwd whenever it's given */
+ if (!BER_BVISEMPTY(&pi.pwd))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
+ &pi.pwd);
+ if (!BER_BVISEMPTY(&npw))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
+ &npw);
+ ber_printf(ber, "N}");
+ ber_flatten2(ber, &bv, 0);
+ op->o_tag = LDAP_REQ_EXTENDED;
+ op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
+ op->ore_reqdata = &bv;
+
+ if (pi.ispwdmgr) {
+ /* root user changing end-user passwords */
+ op->o_dn = ni->ni_pam_pwdmgr_dn;
+ op->o_ndn = ni->ni_pam_pwdmgr_dn;
+ } else {
+ /* end-user self-pwd-mod */
+ op->o_dn = pi.dn;
+ op->o_ndn = pi.dn;
+ }
+ op->o_callback = &cb;
+ op->o_conn->c_authz_backend = op->o_bd;
+ cb.sc_response = slap_null_cb;
+ op->o_bd = frontendDB;
+ rc = op->o_bd->be_extended(op, &rs);
+ if (rs.sr_text)
+ ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
+ if (rc == LDAP_SUCCESS)
+ rc = NSLCD_PAM_SUCCESS;
+ else
+ rc = NSLCD_PAM_PERM_DENIED;
+
+done:;
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), rc (%d)\n", rc );
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&pi.msg);
+ return 0;
+}
+
+int nssov_pam_init()
+{
+ int code = 0;
+ const char *text;
+ if (!ad_loginStatus)
+ code = slap_str2ad("loginStatus", &ad_loginStatus, &text);
+
+ return code;
+}
diff --git a/contrib/slapd-modules/nssov/passwd.c b/contrib/slapd-modules/nssov/passwd.c
new file mode 100644
index 0000000..137106d
--- /dev/null
+++ b/contrib/slapd-modules/nssov/passwd.c
@@ -0,0 +1,435 @@
+/* passwd.c - password lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
+ * DESC 'Abstraction of an account with POSIX attributes'
+ * MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+ * MAY ( userPassword $ loginShell $ gecos $ description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
+
+/* the attributes used in searches */
+static struct berval passwd_keys[] = {
+ BER_BVC("uid"),
+ BER_BVC("userPassword"),
+ BER_BVC("uidNumber"),
+ BER_BVC("gidNumber"),
+ BER_BVC("gecos"),
+ BER_BVC("cn"),
+ BER_BVC("homeDirectory"),
+ BER_BVC("loginShell"),
+ BER_BVC("objectClass"),
+ BER_BVNULL
+};
+
+#define UID_KEY 0
+#define PWD_KEY 1
+#define UIDN_KEY 2
+#define GIDN_KEY 3
+#define GEC_KEY 4
+#define CN_KEY 5
+#define DIR_KEY 6
+#define SHL_KEY 7
+
+/* default values for attributes */
+static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */
+static struct berval default_passwd_homeDirectory = BER_BVC("");
+static struct berval default_passwd_loginShell = BER_BVC("");
+
+static struct berval shadow_passwd = BER_BVC("x");
+
+NSSOV_INIT(passwd)
+
+/*
+ Checks to see if the specified name is a valid user name.
+
+ This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
+ and 3.276 Portable Filename Character Set):
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+ The standard defines user names valid if they contain characters from
+ the set [A-Za-z0-9._-] where the hyphen should not be used as first
+ character. As an extension this test allows the dolar '$' sign as the last
+ character to support Samba special accounts.
+*/
+int isvalidusername(struct berval *bv)
+{
+ int i;
+ char *name = bv->bv_val;
+ if ((name==NULL)||(name[0]=='\0'))
+ return 0;
+ /* check first character */
+ if ( ! ( (name[0]>='A' && name[0] <= 'Z') ||
+ (name[0]>='a' && name[0] <= 'z') ||
+ (name[0]>='0' && name[0] <= '9') ||
+ name[0]=='.' || name[0]=='_' ) )
+ return 0;
+ /* check other characters */
+ for (i=1;i<bv->bv_len;i++)
+ {
+ if ( name[i]=='$' )
+ {
+ /* if the char is $ we require it to be the last char */
+ if (name[i+1]!='\0')
+ return 0;
+ }
+ else if ( ! ( (name[i]>='A' && name[i] <= 'Z') ||
+ (name[i]>='a' && name[i] <= 'z') ||
+ (name[i]>='0' && name[i] <= '9') ||
+ name[i]=='.' || name[i]=='_' || name[i]=='-') )
+ return 0;
+ }
+ /* no test failed so it must be good */
+ return -1;
+}
+
+/* return 1 on success */
+int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
+{
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
+ Entry *e;
+
+ /* check for empty string */
+ if (!dn->bv_len)
+ return 0;
+ /* try to look up uid within DN string */
+ if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) &&
+ dn->bv_val[ad->ad_cname.bv_len] == '=')
+ {
+ struct berval bv, rdn;
+ dnRdn(dn, &rdn);
+ /* check if it is valid */
+ bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1;
+ bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
+ if (!isvalidusername(&bv))
+ return 0;
+ ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
+ return 1;
+ }
+ /* look up the uid from the entry itself */
+ if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
+ {
+ Attribute *a = attr_find(e->e_attrs, ad);
+ if (a) {
+ ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
+ }
+ be_entry_release_r(op, e);
+ if (a)
+ return 1;
+ }
+ return 0;
+}
+
+int nssov_name2dn_cb(Operation *op,SlapReply *rs)
+{
+ if ( rs->sr_type == REP_SEARCH )
+ {
+ struct berval *bv = op->o_callback->sc_private;
+ if ( !BER_BVISNULL(bv)) {
+ op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
+ BER_BVZERO(bv);
+ return LDAP_ALREADY_EXISTS;
+ }
+ ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
+ }
+ return LDAP_SUCCESS;
+}
+
+int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
+{
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf),fbuf};
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ Operation op2;
+ int rc;
+
+ /* if it isn't a valid username, just bail out now */
+ if (!isvalidusername(uid))
+ return 0;
+ /* we have to look up the entry */
+ nssov_filter_byid(mi,UID_KEY,uid,&filter);
+ BER_BVZERO(dn);
+ cb.sc_private = dn;
+ cb.sc_response = nssov_name2dn_cb;
+ op2 = *op;
+ op2.o_callback = &cb;
+ op2.o_req_dn = mi->mi_base;
+ op2.o_req_ndn = mi->mi_base;
+ op2.ors_scope = mi->mi_scope;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( op, filter.bv_val );
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ rc = op2.o_bd->be_search( &op2, &rs );
+ filter_free_x( op, op2.ors_filter, 1 );
+ return rc == LDAP_SUCCESS && !BER_BVISNULL(dn);
+}
+
+/* the maximum number of uidNumber attributes per entry */
+#define MAXUIDS_PER_ENTRY 5
+
+NSSOV_CBPRIV(passwd,
+ char buf[256];
+ struct berval name;
+ struct berval id;);
+
+static struct berval shadowclass = BER_BVC("shadowAccount");
+
+static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ struct berval tmparr[2], tmpuid[2];
+ char *tmp;
+ struct berval *names;
+ struct berval *uids;
+ struct berval passwd = {0};
+ gid_t gid;
+ struct berval gecos;
+ struct berval homedir;
+ struct berval shell;
+ Attribute *a;
+ int i,j;
+ int use_shadow = 0;
+ /* get the usernames for this entry */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the password for this entry */
+ a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
+ if ( a ) {
+ for ( i=0; i<a->a_numvals; i++) {
+ if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
+ use_shadow = 1;
+ break;
+ }
+ }
+ }
+ if ( use_shadow )
+ {
+ /* if the entry has a shadowAccount entry, point to that instead */
+ passwd = shadow_passwd;
+ }
+ else
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
+ if (a)
+ get_userpassword(&a->a_vals[0], &passwd);
+ if (BER_BVISNULL(&passwd))
+ passwd=default_passwd_userPassword;
+ }
+ /* get the uids for this entry */
+ if (BER_BVISNULL(&cbp->id))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ uids = a->a_vals;
+ }
+ else
+ {
+ uids = tmpuid;
+ uids[0] = cbp->id;
+ BER_BVZERO(&uids[1]);
+ }
+ /* get the gid for this entry */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ else if (a->a_numvals != 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
+ }
+ gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* get the gecos for this entry (fall back to cn) */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc);
+ if (!a)
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
+ if (!a || !a->a_numvals)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n",
+ entry->e_name.bv_val,
+ cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
+ cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
+ return 0;
+ }
+ else if (a->a_numvals > 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n",
+ entry->e_name.bv_val,
+ cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
+ cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
+ }
+ gecos=a->a_vals[0];
+ /* get the home directory for this entry */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val );
+ homedir=default_passwd_homeDirectory;
+ }
+ else
+ {
+ if (a->a_numvals > 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val );
+ }
+ homedir=a->a_vals[0];
+ if (homedir.bv_val[0]=='\0')
+ homedir=default_passwd_homeDirectory;
+ }
+ /* get the shell for this entry */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
+ if (!a)
+ {
+ shell=default_passwd_loginShell;
+ }
+ else
+ {
+ if (a->a_numvals > 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val );
+ }
+ shell=a->a_vals[0];
+ if (shell.bv_val[0]=='\0')
+ shell=default_passwd_loginShell;
+ }
+ /* write the entries */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ if (!isvalidusername(&names[i]))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n",
+ entry->e_name.bv_val,names[i].bv_val );
+ }
+ else
+ {
+ for (j=0;!BER_BVISNULL(&uids[j]);j++)
+ {
+ char *tmp;
+ uid_t uid;
+ uid = strtol(uids[j].bv_val, &tmp, 0);
+ if ( *tmp ) {
+ Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
+ names[i].bv_val);
+ continue;
+ }
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BERVAL(cbp->fp,&passwd);
+ WRITE_INT32(cbp->fp,uid);
+ WRITE_INT32(cbp->fp,gid);
+ WRITE_BERVAL(cbp->fp,&gecos);
+ WRITE_BERVAL(cbp->fp,&homedir);
+ WRITE_BERVAL(cbp->fp,&shell);
+ }
+ }
+ }
+ return 0;
+}
+
+NSSOV_CB(passwd)
+
+NSSOV_HANDLE(
+ passwd,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ if (!isvalidusername(&cbp.name)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val);
+ return -1;
+ }
+ BER_BVZERO(&cbp.id); ,
+ Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_PASSWD_BYNAME,
+ nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ passwd,byuid,
+ uid_t uid;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,uid);
+ cbp.id.bv_val = cbp.buf;
+ cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val);,
+ NSLCD_ACTION_PASSWD_BYUID,
+ nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
+)
+
+NSSOV_HANDLE(
+ passwd,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.id);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n");,
+ NSLCD_ACTION_PASSWD_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/protocol.c b/contrib/slapd-modules/nssov/protocol.c
new file mode 100644
index 0000000..1d131ca
--- /dev/null
+++ b/contrib/slapd-modules/nssov/protocol.c
@@ -0,0 +1,156 @@
+/* protocol.c - network protocol lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.4 NAME 'ipProtocol' SUP top STRUCTURAL
+ * DESC 'Abstraction of an IP protocol. Maps a protocol number
+ * to one or more names. The distinguished value of the cn
+ * attribute denotes the protocol's canonical name'
+ * MUST ( cn $ ipProtocolNumber )
+ * MAY description )
+ */
+
+/* the basic search filter for searches */
+static struct berval protocol_filter = BER_BVC("(objectClass=ipProtocol)");
+
+/* the attributes used in searches */
+static struct berval protocol_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipProtocolNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(protocol)
+
+NSSOV_CBPRIV(protocol,
+ char buf[256];
+ struct berval name;
+ struct berval numb;);
+
+static int write_protocol(nssov_protocol_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int i,numname,dupname,proto;
+ struct berval name,*names;
+ Attribute *a;
+ char *tmp;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the protocol */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the protocol number */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ } else if ( a->a_numvals > 1 ) {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ }
+ proto=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (*tmp)
+ {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,proto);
+ return 0;
+}
+
+NSSOV_CB(protocol)
+
+NSSOV_HANDLE(
+ protocol,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.numb);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_protocol_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_PROTOCOL_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ protocol,bynumber,
+ int protocol;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,protocol);
+ cbp.numb.bv_val = cbp.buf;
+ cbp.numb.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",protocol);
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_protocol_bynumber(%s)\n",cbp.numb.bv_val);,
+ NSLCD_ACTION_PROTOCOL_BYNUMBER,
+ nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter)
+)
+
+NSSOV_HANDLE(
+ protocol,all,
+ struct berval filter;
+ /* no parameters to read */,
+ Debug(LDAP_DEBUG_TRACE,"nssov_protocol_all()\n");,
+ NSLCD_ACTION_PROTOCOL_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/rpc.c b/contrib/slapd-modules/nssov/rpc.c
new file mode 100644
index 0000000..7d2045f
--- /dev/null
+++ b/contrib/slapd-modules/nssov/rpc.c
@@ -0,0 +1,158 @@
+/* rpc.c - rpc lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.5 NAME 'oncRpc' SUP top STRUCTURAL
+ * DESC 'Abstraction of an Open Network Computing (ONC)
+ * [RFC1057] Remote Procedure Call (RPC) binding.
+ * This class maps an ONC RPC number to a name.
+ * The distinguished value of the cn attribute denotes
+ * the RPC service's canonical name'
+ * MUST ( cn $ oncRpcNumber )
+ * MAY description )
+ */
+
+/* the basic search filter for searches */
+static struct berval rpc_filter = BER_BVC("(objectClass=oncRpc)");
+
+/* the attributes to request with searches */
+static struct berval rpc_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("oncRpcNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(rpc)
+
+NSSOV_CBPRIV(rpc,
+ char buf[256];
+ struct berval name;
+ struct berval numb;);
+
+/* write a single rpc entry to the stream */
+static int write_rpc(nssov_rpc_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int i,numname,dupname,number;
+ struct berval name,*names;
+ Attribute *a;
+ char *tmp;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the rpc */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the rpc number */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ } else if ( a->a_numvals > 1 ) {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ }
+ number=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (*tmp)
+ {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,number);
+ return 0;
+}
+
+NSSOV_CB(rpc)
+
+NSSOV_HANDLE(
+ rpc,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.numb);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_rpc_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_RPC_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ rpc,bynumber,
+ int number;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,number);
+ cbp.numb.bv_val = cbp.buf;
+ cbp.numb.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",number);
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_rpc_bynumber(%s)\n",cbp.numb.bv_val);,
+ NSLCD_ACTION_RPC_BYNUMBER,
+ nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter)
+)
+
+NSSOV_HANDLE(
+ rpc,all,
+ struct berval filter;
+ /* no parameters to read */,
+ Debug(LDAP_DEBUG_TRACE,"nssov_rpc_all()\n");,
+ NSLCD_ACTION_RPC_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/service.c b/contrib/slapd-modules/nssov/service.c
new file mode 100644
index 0000000..d3704e4
--- /dev/null
+++ b/contrib/slapd-modules/nssov/service.c
@@ -0,0 +1,250 @@
+/* service.c - service lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.3 NAME 'ipService' SUP top STRUCTURAL
+ * DESC 'Abstraction an Internet Protocol service.
+ * Maps an IP port and protocol (such as tcp or udp)
+ * to one or more names; the distinguished value of
+ * the cn attribute denotes the service's canonical
+ * name'
+ * MUST ( cn $ ipServicePort $ ipServiceProtocol )
+ * MAY ( description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval service_filter = BER_BVC("(objectClass=ipService)");
+
+/* the attributes to request with searches */
+static struct berval service_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipServicePort"),
+ BER_BVC("ipServiceProtocol"),
+ BER_BVNULL
+};
+
+static int mkfilter_service_byname(nssov_mapinfo *mi,struct berval *name,
+ struct berval *protocol,struct berval *buf)
+{
+ char buf2[1024],buf3[1024];
+ struct berval bv2 = {sizeof(buf2),buf2};
+ struct berval bv3 = {sizeof(buf3),buf3};
+
+ /* escape attributes */
+ if (nssov_escape(name,&bv2))
+ return -1;
+ if (!BER_BVISNULL(protocol)) {
+ if (nssov_escape(protocol,&bv3))
+ return -1;
+ if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[0].an_desc->ad_cname.bv_len +
+ bv3.bv_len + mi->mi_attrs[2].an_desc->ad_cname.bv_len + 9 > buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s)(%s=%s))",
+ mi->mi_filter.bv_val,
+ mi->mi_attrs[0].an_desc->ad_cname.bv_val, bv2.bv_val,
+ mi->mi_attrs[2].an_desc->ad_cname.bv_val, bv3.bv_val );
+ } else {
+ if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[0].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[0].an_desc->ad_cname.bv_val,
+ bv2.bv_val );
+ }
+ return 0;
+}
+
+static int mkfilter_service_bynumber(nssov_mapinfo *mi,struct berval *numb,
+ struct berval *protocol,struct berval *buf)
+{
+ char buf2[1024];
+ struct berval bv2 = {sizeof(buf2),buf2};
+
+ /* escape attribute */
+ if (!BER_BVISNULL(protocol)) {
+ if (nssov_escape(protocol,&bv2))
+ return -1;
+ if (numb->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[1].an_desc->ad_cname.bv_len +
+ bv2.bv_len + mi->mi_attrs[2].an_desc->ad_cname.bv_len + 9 > buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s)(%s=%s))",
+ mi->mi_filter.bv_val,
+ mi->mi_attrs[1].an_desc->ad_cname.bv_val, numb->bv_val,
+ mi->mi_attrs[2].an_desc->ad_cname.bv_val, bv2.bv_val );
+ } else {
+ if (numb->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[1].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[1].an_desc->ad_cname.bv_val,
+ numb->bv_val );
+ }
+ return 0;
+}
+
+NSSOV_INIT(service)
+
+NSSOV_CBPRIV(service,
+ char nbuf[256];
+ char pbuf[256];
+ struct berval name;
+ struct berval prot;);
+
+static int write_service(nssov_service_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ struct berval name,*names,*protos;
+ struct berval tmparr[2];
+ Attribute *a;
+ char *tmp;
+ int port;
+ int i,numname,dupname,numprot;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the rpc */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the service number */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ } else if ( a->a_numvals > 1 ) {
+ Debug(LDAP_DEBUG_ANY,"service entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ }
+ port=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (*tmp)
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* get protocols */
+ if (BER_BVISNULL(&cbp->prot))
+ {
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[2].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[2].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ protos = a->a_vals;
+ numprot = a->a_numvals;
+ }
+ else
+ {
+ protos=tmparr;
+ protos[0]=cbp->prot;
+ BER_BVZERO(&protos[1]);
+ numprot = 1;
+ }
+ /* write the entries */
+ for (i=0;i<numprot;i++)
+ {
+ int j;
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (j=0;j<numname;j++) {
+ if (j == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[j]);
+ }
+ WRITE_INT32(cbp->fp,port);
+ WRITE_BERVAL(cbp->fp,&protos[i]);
+ }
+ return 0;
+}
+
+NSSOV_CB(service)
+
+NSSOV_HANDLE(
+ service,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.nbuf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.nbuf;
+ READ_STRING(fp,cbp.pbuf);
+ cbp.prot.bv_len = tmpint32;
+ cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_service_byname(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val ? cbp.prot.bv_val : "");,
+ NSLCD_ACTION_SERVICE_BYNAME,
+ mkfilter_service_byname(cbp.mi,&cbp.name,&cbp.prot,&filter)
+)
+
+NSSOV_HANDLE(
+ service,bynumber,
+ int number;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,number);
+ cbp.name.bv_val = cbp.nbuf;
+ cbp.name.bv_len = snprintf(cbp.nbuf,sizeof(cbp.nbuf),"%d",number);
+ READ_STRING(fp,cbp.pbuf);
+ cbp.prot.bv_len = tmpint32;
+ cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_service_bynumber(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val);,
+ NSLCD_ACTION_SERVICE_BYNUMBER,
+ mkfilter_service_bynumber(cbp.mi,&cbp.name,&cbp.prot,&filter)
+)
+
+NSSOV_HANDLE(
+ service,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.prot);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_service_all()\n");,
+ NSLCD_ACTION_SERVICE_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/shadow.c b/contrib/slapd-modules/nssov/shadow.c
new file mode 100644
index 0000000..477ce50
--- /dev/null
+++ b/contrib/slapd-modules/nssov/shadow.c
@@ -0,0 +1,257 @@
+/* shadow.c - shadow account lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.1 NAME 'shadowAccount' SUP top AUXILIARY
+ * DESC 'Additional attributes for shadow passwords'
+ * MUST uid
+ * MAY ( userPassword $ shadowLastChange $ shadowMin
+ * shadowMax $ shadowWarning $ shadowInactive $
+ * shadowExpire $ shadowFlag $ description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval shadow_filter = BER_BVC("(objectClass=shadowAccount)");
+
+/* the attributes to request with searches */
+static struct berval shadow_keys[] = {
+ BER_BVC("uid"),
+ BER_BVC("userPassword"),
+ BER_BVC("shadowLastChange"),
+ BER_BVC("shadowMin"),
+ BER_BVC("shadowMax"),
+ BER_BVC("shadowWarning"),
+ BER_BVC("shadowInactive"),
+ BER_BVC("shadowExpire"),
+ BER_BVC("shadowFlag"),
+ BER_BVNULL
+};
+
+#define UID_KEY 0
+#define PWD_KEY 1
+#define CHG_KEY 2
+#define MIN_KEY 3
+#define MAX_KEY 4
+#define WRN_KEY 5
+#define INA_KEY 6
+#define EXP_KEY 7
+#define FLG_KEY 8
+
+/* default values for attributes */
+static struct berval default_shadow_userPassword = BER_BVC("*"); /* unmatchable */
+static int default_nums[] = { 0,0,
+ -1, /* LastChange */
+ -1, /* Min */
+ -1, /* Max */
+ -1, /* Warning */
+ -1, /* Inactive */
+ -1, /* Expire */
+ 0 /* Flag */
+};
+
+NSSOV_INIT(shadow)
+
+static long to_date(struct berval *date,AttributeDescription *attr)
+{
+ long value;
+ char *tmp;
+ /* do some special handling for date values on AD */
+ if (strcasecmp(attr->ad_cname.bv_val,"pwdLastSet")==0)
+ {
+ char buffer[8];
+ size_t l;
+ /* we expect an AD 64-bit datetime value;
+ we should do date=date/864000000000-134774
+ but that causes problems on 32-bit platforms,
+ first we divide by 1000000000 by stripping the
+ last 9 digits from the string and going from there */
+ l=date->bv_len-9;
+ if (l<1 || l>(sizeof(buffer)-1))
+ return 0; /* error */
+ strncpy(buffer,date->bv_val,l);
+ buffer[l]='\0';
+ value=strtol(buffer,&tmp,0);
+ if ((buffer[0]=='\0')||(*tmp!='\0'))
+ {
+ Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n",
+ attr->ad_cname.bv_val );
+ return 0;
+ }
+ return value/864-134774;
+ /* note that AD does not have expiry dates but a lastchangeddate
+ and some value that needs to be added */
+ }
+ value=strtol(date->bv_val,&tmp,0);
+ if ((date->bv_val[0]=='\0')||(*tmp!='\0'))
+ {
+ Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n",
+ attr->ad_cname.bv_val );
+ return 0;
+ }
+ return value;
+}
+
+#ifndef UF_DONT_EXPIRE_PASSWD
+#define UF_DONT_EXPIRE_PASSWD 0x10000
+#endif
+
+#define GET_OPTIONAL_LONG(var,key) \
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[key].an_desc); \
+ if ( !a || BER_BVISNULL(&a->a_vals[0])) \
+ var = default_nums[key]; \
+ else \
+ { \
+ if (a->a_numvals > 1) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values\n", \
+ entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val); \
+ } \
+ var=strtol(a->a_vals[0].bv_val,&tmp,0); \
+ if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0')) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s contains non-numeric %s value\n", \
+ entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val); \
+ return 0; \
+ } \
+ }
+
+#define GET_OPTIONAL_DATE(var,key) \
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[key].an_desc); \
+ if ( !a || BER_BVISNULL(&a->a_vals[0])) \
+ var = default_nums[key]; \
+ else \
+ { \
+ if (a->a_numvals > 1) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values\n", \
+ entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val); \
+ } \
+ var=to_date(&a->a_vals[0],cbp->mi->mi_attrs[key].an_desc); \
+ }
+
+NSSOV_CBPRIV(shadow,
+ char buf[256];
+ struct berval name;);
+
+static int write_shadow(nssov_shadow_cbp *cbp,Entry *entry)
+{
+ struct berval tmparr[2];
+ struct berval *names;
+ Attribute *a;
+ char *tmp;
+ struct berval passwd = {0};
+ long lastchangedate;
+ long mindays;
+ long maxdays;
+ long warndays;
+ long inactdays;
+ long expiredate;
+ unsigned long flag;
+ int i;
+ int32_t tmpint32;
+ /* get username */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get password */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
+ if ( a )
+ get_userpassword(&a->a_vals[0], &passwd);
+ if (BER_BVISNULL(&passwd))
+ passwd=default_shadow_userPassword;
+ /* get lastchange date */
+ GET_OPTIONAL_DATE(lastchangedate,CHG_KEY);
+ /* get mindays */
+ GET_OPTIONAL_LONG(mindays,MIN_KEY);
+ /* get maxdays */
+ GET_OPTIONAL_LONG(maxdays,MAX_KEY);
+ /* get warndays */
+ GET_OPTIONAL_LONG(warndays,WRN_KEY);
+ /* get inactdays */
+ GET_OPTIONAL_LONG(inactdays,INA_KEY);
+ /* get expire date */
+ GET_OPTIONAL_LONG(expiredate,EXP_KEY);
+ /* get flag */
+ GET_OPTIONAL_LONG(flag,FLG_KEY);
+ /* if we're using AD handle the flag specially */
+ if (strcasecmp(cbp->mi->mi_attrs[CHG_KEY].an_desc->ad_cname.bv_val,"pwdLastSet")==0)
+ {
+ if (flag&UF_DONT_EXPIRE_PASSWD)
+ maxdays=99999;
+ flag=0;
+ }
+ /* write the entries */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BERVAL(cbp->fp,&passwd);
+ WRITE_INT32(cbp->fp,lastchangedate);
+ WRITE_INT32(cbp->fp,mindays);
+ WRITE_INT32(cbp->fp,maxdays);
+ WRITE_INT32(cbp->fp,warndays);
+ WRITE_INT32(cbp->fp,inactdays);
+ WRITE_INT32(cbp->fp,expiredate);
+ WRITE_INT32(cbp->fp,flag);
+ }
+ return 0;
+}
+
+NSSOV_CB(shadow)
+
+NSSOV_HANDLE(
+ shadow,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);,
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ Debug(LDAP_DEBUG_ANY,"nssov_shadow_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_SHADOW_BYNAME,
+ nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ shadow,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_ANY,"nssov_shadow_all()\n");,
+ NSLCD_ACTION_SHADOW_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/slapo-nssov.5 b/contrib/slapd-modules/nssov/slapo-nssov.5
new file mode 100644
index 0000000..6128de9
--- /dev/null
+++ b/contrib/slapd-modules/nssov/slapo-nssov.5
@@ -0,0 +1,316 @@
+.TH SLAPO-NSSOV 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-nssov \- NSS and PAM requests through a local Unix Domain socket
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B nssov
+overlay to
+.BR slapd (8)
+services NSS and PAM requests through a local Unix Domain socket.
+It uses the same IPC protocol as Arthur de Jong's nss-pam-ldapd.
+An extract of the nss-ldapd source is included along with the
+nssov source code to allow the overlay to communicate with the
+nss-pam-ldapd client stubs.
+.LP
+Using a separate IPC protocol for NSS and PAM requests eliminates the
+libldap dependencies/clashes that the current pam_ldap/nss_ldap solutions
+all suffer from. Both the original nss-ldapd and this nssov solution
+are free from these library issues.
+.LP
+Unlike nss-pam-ldapd, since this overlay executes inside slapd it allows for
+the possibility of sophisticated caching, without any of the weaknesses of
+nscd and other related caching solutions. E.g., a remote LDAP database can
+be accessed using back-ldap with proxy caching (see
+.BR slapd-ldap (5)
+and
+.BR slapo-pcache (5)
+) to leverage back-ldap's
+connection pooling as well as pcache's persistent caching, to provide
+high performance and a measure of support for disconnected operation.
+Alternatively, cache considerations can be completely eliminated by running
+a regular database with syncrepl to maintain synchronization with a remote
+LDAP database.
+.LP
+Another major benefit of nssov is that it allows all security policy to be
+administered centrally via LDAP, instead of having fragile rules scattered
+across multiple flat files. As such, there is no client-side configuration at
+all for the NSS/PAM stub libraries. (The stubs talk to the server via a Unix
+domain socket whose path is hardcoded to NSLCDPATH). As a side benefit,
+this can finally eliminate the perpetual confusion between OpenLDAP's
+ldap.conf file in ETCDIR/ldap.conf and the similarly named files typically
+used by pam_ldap and nss_ldap.
+.LP
+User authentication is performed by internal simple Binds. User authorization
+leverages the slapd ACL engine, which offers much more power and flexibility
+than the simple group/hostname checks in the old pam_ldap code.
+.LP
+To use this code, you will need the client-side stub library from
+nss-pam-ldapd. You can get it from:
+http://arthurdejong.org/nss-pam-ldapd
+You will not need the nslcd daemon; this overlay replaces that part.
+To disable building of the nslcd daemon in nss-pam-ldapd, add the
+--disable-nslcd option to the nss-pam-ldapd configure script. You
+should already be familiar with the RFC2307 and RFC2307bis schema
+to use this overlay. See the nss-pam-ldapd README for more information
+on the schema and which features are supported.
+.LP
+You will also need to include the nis.schema in your slapd configuration
+for RFC2307 support. If you wish to use RFC2307bis you will need a slightly
+different schema. You will also need the ldapns.schema for PAM authorization
+management.
+.LP
+You must select
+.B ldap
+in the appropriate services in
+.I /etc/nsswitch.conf
+in order for these NSS features to take effect. Likewise, you must
+enable
+.B pam_ldap
+for the authenticate, account, session, and password services in
+.I /etc/pam.conf
+or
+.I /etc/pam.d
+for these PAM features to take effect.
+
+.TP
+.B overlay nssov
+This directive adds the nssov overlay to the current backend.
+.TP
+.B nssov-ssd <service> <url>
+This directive configures a Service Search Descriptor (SSD) for each NSS
+service that will be used. The <service> may be one of
+.RS
+.nf
+ aliases
+ ethers
+ group
+ hosts
+ netgroup
+ networks
+ passwd
+ protocols
+ rpc
+ services
+ shadow
+.fi
+.RE
+and the <url> must be of the form
+.RS
+.TP
+.B ldap:///[<basedn>][??[<scope>][?<filter>]]
+.RE
+The
+.B <basedn>
+will default to the first suffix of the current database.
+The
+.B <scope>
+defaults to "subtree". The default
+.B <filter>
+depends on which service is being used.
+.TP
+.B nssov-map <service> <orig> <new>
+If the local database is actually a proxy to a foreign LDAP server, some
+mapping of schema may be needed. This directive allows some simple attribute
+substitutions to be performed. See the
+.B nss-ldapd/README
+for the original attribute names used in this code.
+.TP
+.B nssov-pam <option> [...]
+This directive determines a number of PAM behaviors. Multiple options may
+be used at once, and available levels are:
+.RS
+.RS
+.PD 0
+.TP
+.B userhost
+check host attribute in user entry for authorization
+.TP
+.B userservice
+check authorizedService attribute in user entry for authorization
+.TP
+.B usergroup
+check that user is a member of specific group for authorization
+.TP
+.B hostservice
+check authorizedService attribute in host entry for authorization
+.TP
+.B authz2dn
+use authz-regexp mapping to map uid to LDAP DN
+.TP
+.B uid2dn
+use NSS passwd SSD to map uid to LDAP DN
+.PD
+.RE
+
+Setting the
+.BR userhost ,
+.BR userservice ,
+and
+.B usergroup
+options duplicates the original pam_ldap authorization behavior.
+
+The recommended approach is to use
+.B hostservice
+instead. In this case, ipHost entries must be created for all hosts
+being managed, and they must also have the authorizedServiceObject
+class to allow authorizedService attributes to be used. Also the
+NSS host SSD must be configured so that ipHost entries can be found.
+Authorization is checked by performing an LDAP Compare operation
+looking for the PAM service name in the authorizedService attribute.
+.B slapd
+ACLs should be set to grant or deny
+.B Compare
+privilege to the appropriate users or groups as desired.
+
+If the
+.B authz2dn
+option is set then authz-regexp mappings will be used to map the
+PAM username to an LDAP DN. The authentication DN will be of the
+form
+.RS
+.B cn=<service>+uid=<user>,cn=<hostname>,cn=pam,cn=auth
+.RE
+
+If no mapping is found for this authentication DN, then this
+mapping will be ignored.
+
+If the
+.B uid2dn
+option is set then the NSS passwd SSD will be used to map the
+PAM username to an LDAP DN. The passwd SSD must have already been
+configured for this mapping to succeed.
+
+If neither the authz2dn nor the uid2dn mapping succeeds, the module
+will return a PAM_USER_UNKNOWN failure code. If both options are set,
+the authz mapping is attempted first; if it succeeds the uid2dn mapping
+will be skipped.
+
+By default only the
+.B uid2dn
+option is set.
+.RE
+.TP
+.B nssov-pam-defhost <hostname>
+Specify a default hostname to check if an ipHost entry for the current
+hostname cannot be found. This setting is only relevant if the
+.B hostservice
+option has been set.
+.TP
+.B nssov-pam-group-dn <DN>
+Specify the DN of an LDAP group to check for authorization. The LDAP user
+must be a member of this group for the login to be allowed. There is no
+default value. This setting is only relevant if the
+.B usergroup
+option has been set.
+.TP
+.B nssov-pam-group-ad <attribute>
+Specify the attribute to use for group membership checks.
+There is no default value. This setting is only relevant if the
+.B usergroup
+option has been set.
+.TP
+.B nssov-pam-min-uid <integer>
+Specify a minimum uid that is allowed to login. Users with a uidNumber
+lower than this value will be denied access. The default is zero, which
+disables this setting.
+.TP
+.B nssov-pam-max-uid <integer>
+Specify a maximum uid that is allowed to login. Users with a uidNumber
+higher than this value will be denied access. The default is zero, which
+disables this setting.
+.TP
+.B nssov-pam-template-ad <attribute>
+Specify an attribute to check in a user's entry for a template login name.
+The template login feature is used by FreeBSD's PAM framework. It can be
+viewed as a form of proxying, where a user can authenticate with one
+username/password pair, but is assigned the identity and credentials of
+the template user. This setting is disabled by default.
+.TP
+.B nssov-pam-template <name>
+Specify a default username to be used if no template attribute is found
+in the user's entry. The
+.B nssov-pam-template-ad
+directive must be configured for this setting to have any effect.
+.TP
+.B nssov-pam-session <service>
+Specify a PAM service name whose sessions will be recorded. For the
+configured services, logins will be recorded in the
+.TP
+.B nssov-pam-password-prohibit-message <message>
+Disable password change service and return the specified message to
+users.
+.TP
+.B nssov-pam-pwdmgr-dn <dn>
+Specify the dn of the password manager.
+.TP
+.B nssov-pam-pwdmgr-pwd <pwd>
+Specify the pwd of the password manager.
+.TP
+.B loginStatus
+operational attribute of the user's entry. The attribute's values are
+of the form
+.RS
+.RS
+.B <generalizedTime> <host> <service> <tty> (<ruser@rhost>)
+.RE
+.RE
+Upon logout the corresponding value will be deleted. This feature allows
+a single LDAP Search to be used to check which users are logged in across
+all the hosts of a network. The rootdn of the database is used to perform
+the updates of the loginStatus attribute, so a rootdn must already be
+configured for this feature to work. By default no services are configured.
+.LP
+The PAM functions support LDAP Password Policy as well. If the password
+policy overlay is in use (see
+.BR slapo-ppolicy (5)),
+policy
+information (e.g. password expiration, password quality, etc.)
+may be returned to the PAM client as a result of authentication,
+account management, and password modification requests.
+
+The overlay also supports dynamic configuration in cn=config. An example
+of the config entry is
+.LP
+.RS
+.nf
+ dn: olcOverlay={0}nssov,ocDatabase={1}mdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcNssOvConfig
+ olcOverlay: {0}nssov
+ olcNssSsd: passwd ldap:///ou=users,dc=example,dc=com??one
+ olcNssMap: passwd uid accountName
+ olcNssPam: hostservice uid2dn
+ olcNssPamDefHost: defaulthost
+ olcNssPamMinUid: 500
+ olcNssPamMaxUid: 32000
+ olcNssPamSession: login
+ olcNssPamSession: sshd
+.fi
+.RE
+.LP
+which enables the passwd service, and uses the accountName attribute to
+fetch what is usually retrieved from the uid attribute. It also enables
+some PAM authorization controls, and specifies that the PAM
+.B login
+and
+.B sshd
+services should have their logins recorded.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapo\-pcache (5),
+.BR slapo\-ppolicy (5),
+.BR slapd (8).
+.SH AUTHOR
+Howard Chu, inspired by nss-ldapd by Arthur de Jong and pam_ldap by Luke Howard
+Enhancements by Ted C. Cheng, Symas Corp.
diff --git a/contrib/slapd-modules/passwd/Makefile b/contrib/slapd-modules/passwd/Makefile
new file mode 100644
index 0000000..6346496
--- /dev/null
+++ b/contrib/slapd-modules/passwd/Makefile
@@ -0,0 +1,70 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-kerberos.la pw-netscape.la pw-radius.la pw-apr1.la
+MANPAGES = slapd-pw-radius.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-kerberos.la: kerberos.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? -lkrb5
+
+pw-netscape.la: netscape.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $?
+
+pw-radius.la: radius.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? -lradius
+
+pw-apr1.la: apr1.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $?
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/README b/contrib/slapd-modules/passwd/README
new file mode 100644
index 0000000..069555f
--- /dev/null
+++ b/contrib/slapd-modules/passwd/README
@@ -0,0 +1,69 @@
+This directory contains native slapd plugins for password mechanisms that
+are not actively supported by the project. Currently this includes the
+Kerberos, Netscape MTA-MD5 and RADIUS password mechanisms. The Apache
+APR1 MD5 and BSD/Paul Henning Kamp MD5 mechanisms are also included.
+
+To use the Kerberos plugin, add:
+
+moduleload pw-kerberos.so
+
+to your slapd configuration file.
+
+To use the Netscape plugin, add:
+
+moduleload pw-netscape.so
+
+to your slapd configuration file.
+
+To use the APR1/BSD/MD5 plugin, add:
+
+moduleload pw-apr1.so
+
+to your slapd configuration file.
+
+To use the RADIUS plugin, add:
+
+moduleload pw-radius.so
+
+to your slapd configuration file; optionally, the path to a configuration
+file can be appended in the form
+
+moduleload pw-radius.so config="/etc/radius.conf"
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -Wall -g -DHAVE_KRB5 -o pw-kerberos.so kerberos.c
+
+Replace HAVE_KRB5 with HAVE_KRB4 if you want to use Kerberos IV.
+If your Kerberos header files are not in the C compiler's
+default path, you will need to add a "-I" directive for that as well.
+
+The corresponding command for the Netscape plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-netscape.so netscape.c
+
+The corresponding command for the RADIUS plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-radius.so radius.c -lradius
+
+(Actually, you might want to statically link the RADIUS client library
+libradius.a into the module).
+
+The corresponding command for the APR1 plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-apr1.so apr1.c
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2004-2022 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>.
+
diff --git a/contrib/slapd-modules/passwd/apr1-atol.pl b/contrib/slapd-modules/passwd/apr1-atol.pl
new file mode 100644
index 0000000..d6eaee7
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1-atol.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+# Apache $apr1$ to OpenLDAP {APR1} hash converter
+# (C) 2011 Devin J. Pohly
+# You may use this code freely. It would be nice to be credited.
+
+use MIME::Base64;
+
+while (<>) {
+ ($user, $hash) = split(/:/, $_);
+ unless ($hash =~ /^\$apr1\$/) {
+ print STDERR "Not an Apache MD5 hash\n";
+ exit 1;
+ }
+
+ chomp $hash;
+ ($_,$_,$salt,$hash) = split(/\$/, $hash);
+
+ $hash =~ tr|./0-9A-Za-z|A-Za-z0-9+/|;
+ $hash .= "AA";
+ $hash =~ s/(.)(.)(.)(.)/$4$3$2$1/gs;
+ $hash = decode_base64($hash);
+ $hash =~ s/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)..(.)/$1$4$7$10$13$15$2$5$8$11$14$16$3$6$9$12/s;
+ $hash .= $salt;
+ $hash = encode_base64($hash);
+ chop $hash;
+
+ print "$user:{APR1}$hash\n";
+} \ No newline at end of file
diff --git a/contrib/slapd-modules/passwd/apr1-ltoa.pl b/contrib/slapd-modules/passwd/apr1-ltoa.pl
new file mode 100644
index 0000000..ee628ec
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1-ltoa.pl
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -w
+
+# OpenLDAP {APR1} to Apache $apr1$ hash converter
+# (C) 2011 Devin J. Pohly
+# You may use this code freely. It would be nice to be credited.
+
+use MIME::Base64;
+
+while (<>) {
+ ($user, $hash) = split(/:/, $_);
+ unless ($hash =~ /^{APR1}/) {
+ print STDERR "Not an Apache MD5 hash\n";
+ next;
+ }
+
+ chomp $hash;
+ $hash = decode_base64(substr($hash, 6));
+ ($hash, $salt) = (substr($hash, 0, 16), substr($hash, 16));
+ $hash = $hash;
+ $hash =~ s/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/$1$7$13$2$8$14$3$9$15$4$10$16$5$11$6\0\0$12/s;
+ $hash = encode_base64($hash);
+ chomp $hash;
+ $hash =~ s/(.)(.)(.)(.)/$4$3$2$1/gs;
+ unless ($hash =~ /AA$/) {
+ #print "Problem with hash\n";
+ next;
+ }
+ $hash =~ s/AA$//;
+ $hash =~ tr|A-Za-z0-9+/|./0-9A-Za-z|;
+ print "$user:\$apr1\$$salt\$$hash\n"
+} \ No newline at end of file
diff --git a/contrib/slapd-modules/passwd/apr1.c b/contrib/slapd-modules/passwd/apr1.c
new file mode 100644
index 0000000..36880f3
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1.c
@@ -0,0 +1,236 @@
+/* $OpenLDAP$ */
+/*
+ * This file is derived from OpenLDAP Software. All of the modifications to
+ * OpenLDAP Software represented in the following file were developed by
+ * Devin J. Pohly <djpohly@gmail.com>. I have not assigned rights and/or
+ * interest in this work to any party.
+ *
+ * The extensions to OpenLDAP Software herein are subject to the following
+ * notice:
+ *
+ * Copyright 2011 Devin J. Pohly
+ * Portions Copyright 2011 Howard Chu
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP Public
+ * License.
+ *
+ * A portion of this code is used in accordance with the Beer-ware License,
+ * revision 42, as noted.
+ *
+ */
+
+#include "portable.h"
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "lutil_md5.h"
+#include <ac/string.h>
+
+#include <assert.h>
+
+/* the only difference between this and straight PHK is the magic */
+static LUTIL_PASSWD_CHK_FUNC chk_apr1;
+static LUTIL_PASSWD_HASH_FUNC hash_apr1;
+static const struct berval scheme_apr1 = BER_BVC("{APR1}");
+static const struct berval magic_apr1 = BER_BVC("$apr1$");
+
+static LUTIL_PASSWD_CHK_FUNC chk_bsdmd5;
+static LUTIL_PASSWD_HASH_FUNC hash_bsdmd5;
+static const struct berval scheme_bsdmd5 = BER_BVC("{BSDMD5}");
+static const struct berval magic_bsdmd5 = BER_BVC("$1$");
+
+static const unsigned char apr64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+#define APR_SALT_SIZE 8
+
+/* The algorithm implemented in this function was created by Poul-Henning
+ * Kamp and released under the following license:
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+static void do_phk_hash(
+ const struct berval *passwd,
+ const struct berval *salt,
+ const struct berval *magic,
+ unsigned char *digest)
+{
+ lutil_MD5_CTX ctx, ctx1;
+ int n;
+
+ /* Start hashing */
+ lutil_MD5Init(&ctx);
+ lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Update(&ctx, (const unsigned char *) magic->bv_val, magic->bv_len);
+ lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len);
+ /* Inner hash */
+ lutil_MD5Init(&ctx1);
+ lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len);
+ lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Final(digest, &ctx1);
+ /* Nom start mixing things up */
+ for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES)
+ lutil_MD5Update(&ctx, digest,
+ (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n));
+ memset(digest, 0, LUTIL_MD5_BYTES);
+ /* Curiouser and curiouser... */
+ for (n = passwd->bv_len; n; n >>= 1)
+ if (n & 1)
+ lutil_MD5Update(&ctx, digest, 1);
+ else
+ lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);
+ lutil_MD5Final(digest, &ctx);
+ /*
+ * Repeatedly hash things into the final value. This was originally
+ * intended to slow the algorithm down.
+ */
+ for (n = 0; n < 1000; n++) {
+ lutil_MD5Init(&ctx1);
+ if (n & 1)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ else
+ lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
+
+ if (n % 3)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) salt->bv_val, salt->bv_len);
+ if (n % 7)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+
+ if (n & 1)
+ lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
+ else
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Final(digest, &ctx1);
+ }
+}
+
+static int chk_phk(
+ const struct berval *magic,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ unsigned char digest[LUTIL_MD5_BYTES];
+ unsigned char *orig_pass;
+ int rc;
+ struct berval salt;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(digest))
+ return LUTIL_PASSWD_ERR;
+
+ /* base64 un-encode password hash */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if (orig_pass == NULL)
+ return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if (rc <= (int) sizeof(digest)) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ salt.bv_val = (char *) &orig_pass[sizeof(digest)];
+ salt.bv_len = rc - sizeof(digest);
+
+ do_phk_hash(cred, &salt, magic, digest);
+
+ if (text)
+ *text = NULL;
+
+ /* compare */
+ rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_apr1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_phk(&magic_apr1, passwd, cred, text);
+}
+
+static int chk_bsdmd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_phk(&magic_bsdmd5, passwd, cred, text);
+}
+
+static int hash_phk(
+ const struct berval *scheme,
+ const struct berval *magic,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ unsigned char digest_buf[LUTIL_MD5_BYTES];
+ char salt_buf[APR_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+ int n;
+
+ digest.bv_val = (char *) digest_buf;
+ digest.bv_len = sizeof(digest_buf);
+ salt.bv_val = salt_buf;
+ salt.bv_len = APR_SALT_SIZE;
+
+ /* generate random salt */
+ if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0)
+ return LUTIL_PASSWD_ERR;
+ /* limit it to characters in the 64-char set */
+ for (n = 0; n < salt.bv_len; n++)
+ salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)];
+
+ do_phk_hash(passwd, &salt, magic, digest_buf);
+
+ if (text)
+ *text = NULL;
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_apr1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ return hash_phk(scheme, &magic_apr1, passwd, hash, text);
+}
+
+static int hash_bsdmd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ return hash_phk(scheme, &magic_bsdmd5, passwd, hash, text);
+}
+
+int init_module(int argc, char *argv[]) {
+ int rc;
+ rc = lutil_passwd_add((struct berval *) &scheme_apr1, chk_apr1, hash_apr1);
+ if ( !rc )
+ rc = lutil_passwd_add((struct berval *) &scheme_bsdmd5,
+ chk_bsdmd5, hash_bsdmd5);
+ return rc;
+}
diff --git a/contrib/slapd-modules/passwd/kerberos.c b/contrib/slapd-modules/passwd/kerberos.c
new file mode 100644
index 0000000..bebcbd0
--- /dev/null
+++ b/contrib/slapd-modules/passwd/kerberos.c
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 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>.
+ */
+
+#include "portable.h"
+
+#include <unistd.h>
+
+#include <lber.h>
+#include <lber_pvt.h> /* BER_BVC definition */
+#include "lutil.h"
+#include <ac/string.h>
+
+#ifdef HAVE_KRB5
+#include <krb5.h>
+#elif defined(HAVE_KRB4)
+#include <krb.h>
+#endif
+
+/* From <ldap_pvt.h> */
+LDAP_F( char *) ldap_pvt_get_fqdn LDAP_P(( char * ));
+
+static LUTIL_PASSWD_CHK_FUNC chk_kerberos;
+static const struct berval scheme = BER_BVC("{KERBEROS}");
+
+static int chk_kerberos(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ unsigned int i;
+ int rtn;
+
+ for( i=0; i<cred->bv_len; i++) {
+ if(cred->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( cred->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ rtn = LUTIL_PASSWD_ERR;
+
+#ifdef HAVE_KRB5 /* HAVE_HEIMDAL_KRB5 */
+ {
+/* Portions:
+ * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H\xf6gskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_creds creds;
+ krb5_get_init_creds_opt get_options;
+ krb5_verify_init_creds_opt verify_options;
+ krb5_principal client, server;
+#ifdef notdef
+ krb5_preauthtype pre_auth_types[] = {KRB5_PADATA_ENC_TIMESTAMP};
+#endif
+
+ ret = krb5_init_context( &context );
+ if (ret) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef notdef
+ krb5_get_init_creds_opt_set_preauth_list(&get_options,
+ pre_auth_types, 1);
+#endif
+
+ krb5_get_init_creds_opt_init( &get_options );
+
+ krb5_verify_init_creds_opt_init( &verify_options );
+
+ ret = krb5_parse_name( context, passwd->bv_val, &client );
+
+ if (ret) {
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_get_init_creds_password( context,
+ &creds, client, cred->bv_val, NULL,
+ NULL, 0, NULL, &get_options );
+
+ if (ret) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ {
+ char *host = ldap_pvt_get_fqdn( NULL );
+
+ if( host == NULL ) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_sname_to_principal( context,
+ host, "ldap", KRB5_NT_SRV_HST, &server );
+
+ ber_memfree( host );
+ }
+
+ if (ret) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_verify_init_creds( context,
+ &creds, server, NULL, NULL, &verify_options );
+
+ krb5_free_principal( context, client );
+ krb5_free_principal( context, server );
+ krb5_free_cred_contents( context, &creds );
+ krb5_free_context( context );
+
+ rtn = ret ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+#elif defined(HAVE_KRB4)
+ {
+ /* Borrowed from Heimdal kpopper */
+/* Portions:
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+ int status;
+ char lrealm[REALM_SZ];
+ char tkt[MAXHOSTNAMELEN];
+
+ status = krb_get_lrealm(lrealm,1);
+ if (status == KFAILURE) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ snprintf(tkt, sizeof(tkt), "%s_slapd.%u",
+ TKT_ROOT, (unsigned)getpid());
+ krb_set_tkt_string (tkt);
+
+ status = krb_verify_user( passwd->bv_val, "", lrealm,
+ cred->bv_val, 1, "ldap");
+
+ dest_tkt(); /* no point in keeping the tickets */
+
+ return status == KFAILURE ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+#endif
+
+ return rtn;
+}
+
+int init_module(int argc, char *argv[]) {
+ return lutil_passwd_add( (struct berval *)&scheme, chk_kerberos, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/netscape.c b/contrib/slapd-modules/passwd/netscape.c
new file mode 100644
index 0000000..8e2de7b
--- /dev/null
+++ b/contrib/slapd-modules/passwd/netscape.c
@@ -0,0 +1,83 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 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>.
+ */
+
+#include "portable.h"
+
+#include <unistd.h>
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "lutil_md5.h"
+#include <ac/string.h>
+
+static LUTIL_PASSWD_CHK_FUNC chk_ns_mta_md5;
+static const struct berval scheme = BER_BVC("{NS-MTA-MD5}");
+
+#define NS_MTA_MD5_PASSLEN 64
+static int chk_ns_mta_md5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES], c;
+ char buffer[LUTIL_MD5_BYTES*2];
+ int i;
+
+ if( passwd->bv_len != NS_MTA_MD5_PASSLEN ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_MD5Init(&MD5context);
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &passwd->bv_val[32],
+ 32 );
+
+ c = 0x59;
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &c,
+ 1 );
+
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) cred->bv_val,
+ cred->bv_len );
+
+ c = 0xF7;
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &c,
+ 1 );
+
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &passwd->bv_val[32],
+ 32 );
+
+ lutil_MD5Final(MD5digest, &MD5context);
+
+ for( i=0; i < sizeof( MD5digest ); i++ ) {
+ buffer[i+i] = "0123456789abcdef"[(MD5digest[i]>>4) & 0x0F];
+ buffer[i+i+1] = "0123456789abcdef"[ MD5digest[i] & 0x0F];
+ }
+
+ /* compare */
+ return memcmp((char *)passwd->bv_val,
+ (char *)buffer, sizeof(buffer)) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+int init_module(int argc, char *argv[]) {
+ return lutil_passwd_add( (struct berval *)&scheme, chk_ns_mta_md5, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/pbkdf2/Makefile b/contrib/slapd-modules/passwd/pbkdf2/Makefile
new file mode 100644
index 0000000..6279f50
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/Makefile
@@ -0,0 +1,62 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = ../../../..
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+#DEFS = -DSLAPD_PBKDF2_DEBUG
+
+SSL_INC =
+SSL_LIB = -lcrypto
+
+INCS = $(LDAP_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(SSL_LIB)
+
+PROGRAMS = pw-pbkdf2.la
+MANPAGES = slapd-pw-pbkdf2.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-pbkdf2.la: pw-pbkdf2.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/pbkdf2/README b/contrib/slapd-modules/passwd/pbkdf2/README
new file mode 100644
index 0000000..d4d99d2
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/README
@@ -0,0 +1,99 @@
+PBKDF2 for OpenLDAP
+=======================
+
+pw-pbkdf2.c provides PBKDF2 key derivation functions in OpenLDAP.
+
+Schemes:
+
+ * {PBKDF2} - alias to {PBKDF2-SHA1}
+ * {PBKDF2-SHA1}
+ * {PBKDF2-SHA256}
+ * {PBKDF2-SHA512}
+
+# Requirements
+
+ * OpenSSL 1.0.0 or later
+
+# Installations
+
+First, You need to configure and build OpenLDAP.
+
+ $ cd <OPENLDAP_BUILD_DIR>/contrib/slapd-modules/passwd/
+ $ git clone https://github.com/hamano/openldap-pbkdf2.git
+ $ cd openldap-pbkdf2/
+ $ make
+ # make install
+
+# Configuration
+
+In slapd.conf:
+
+ moduleload pw-pbkdf2.so
+
+You can also tell OpenLDAP to use the schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash
+option in slapd.conf. For example:
+
+ password-hash {PBKDF2}
+or
+ password-hash {PBKDF2-SHA256}
+or
+ password-hash {PBKDF2-SHA512}
+
+# Testing
+
+You can get hash to use slappasswd.
+
+ $ slappasswd -o module-load=pw-pbkdf2.la -h {PBKDF2} -s secret
+ {PBKDF2}60000$Y6ZHtTTbeUgpIbIW0QDmDA$j/aU7jFKUSbH4UobNQDm9OEIwuw
+
+A quick way to test whether it's working is to customize the rootdn and
+rootpw in slapd.conf, eg:
+
+ rootdn "cn=Manager,dc=example,dc=com"
+ rootpw {PBKDF2}60000$Y6ZHtTTbeUgpIbIW0QDmDA$j/aU7jFKUSbH4UobNQDm9OEIwuw
+
+Then to test, run something like:
+
+ $ ldapsearch -x -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" -w secret
+
+# Debugging
+You can specify -DSLAPD_PBKDF2_DEBUG flag for debugging.
+
+# Message Format
+
+ {PBKDF2}<Iteration>$<Adapted Base64 Salt>$<Adapted Base64 DK>
+
+# References
+
+* [RFC 2898 Password-Based Cryptography][^1]
+[^1]: http://tools.ietf.org/html/rfc2898
+
+* [PKCS #5 PBKDF2 Test Vectors][^2]
+[^2]: http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06
+
+* [RFC 2307 Using LDAP as a Network Information Service][^3]
+[^3]: http://tools.ietf.org/html/rfc2307
+
+* [Python Passlib][^4]
+[^4]: http://pythonhosted.org/passlib/
+
+* [Adapted Base64 Encoding][^5]
+[^5]: http://pythonhosted.org/passlib/lib/passlib.utils.html#passlib.utils.ab64_encode
+
+# License
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2009-2022 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>.
+
+# ACKNOWLEDGEMENT
+This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
diff --git a/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c
new file mode 100644
index 0000000..1cc2770
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c
@@ -0,0 +1,451 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 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>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ */
+
+#define _GNU_SOURCE
+
+#include "portable.h"
+#include <ac/string.h>
+#include "lber_pvt.h"
+#include "lutil.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#elif HAVE_GNUTLS
+#include <nettle/pbkdf2.h>
+#include <nettle/hmac.h>
+typedef void (*pbkdf2_hmac_update)(void *, unsigned, const uint8_t *);
+typedef void (*pbkdf2_hmac_digest)(void *, unsigned, uint8_t *);
+#else
+#error Unsupported crypto backend.
+#endif
+
+#define PBKDF2_ITERATION 10000
+#define PBKDF2_SALT_SIZE 16
+#define PBKDF2_SHA1_DK_SIZE 20
+#define PBKDF2_SHA256_DK_SIZE 32
+#define PBKDF2_SHA512_DK_SIZE 64
+#define PBKDF2_MAX_DK_SIZE 64
+
+const struct berval pbkdf2_scheme = BER_BVC("{PBKDF2}");
+const struct berval pbkdf2_sha1_scheme = BER_BVC("{PBKDF2-SHA1}");
+const struct berval pbkdf2_sha256_scheme = BER_BVC("{PBKDF2-SHA256}");
+const struct berval pbkdf2_sha512_scheme = BER_BVC("{PBKDF2-SHA512}");
+
+/*
+ * Converting base64 string to adapted base64 string.
+ * Adapted base64 encode is identical to general base64 encode except
+ * that it uses '.' instead of '+', and omits trailing padding '=' and
+ * whitespace.
+ * see http://pythonhosted.org/passlib/lib/passlib.utils.html
+ * This is destructive function.
+ */
+static int b64_to_ab64(char *str)
+{
+ char *p = str;
+ do {
+ if(*p == '+'){
+ *p = '.';
+ }
+ if(*p == '='){
+ *p = '\0';
+ }
+ } while(*p++);
+ return 0;
+}
+
+/*
+ * Converting adapted base64 string to base64 string.
+ * dstsize will require src length + 2, due to output string have
+ * potential to append "=" or "==".
+ * return -1 if few output buffer.
+ */
+static int ab64_to_b64(char *src, char *dst, size_t dstsize){
+ int i;
+ char *p = src;
+ for(i=0; p[i] && p[i] != '$'; i++){
+ if(i >= dstsize){
+ dst[0] = '\0';
+ return -1;
+ }
+ if(p[i] == '.'){
+ dst[i] = '+';
+ }else{
+ dst[i] = p[i];
+ }
+ }
+ for(;i%4;i++){
+ if(i >= dstsize){
+ dst[0] = '\0';
+ return -1;
+ }
+ dst[i] = '=';
+ }
+ dst[i] = '\0';
+ return 0;
+}
+
+static int pbkdf2_format(
+ const struct berval *sc,
+ int iteration,
+ const struct berval *salt,
+ const struct berval *dk,
+ struct berval *msg)
+{
+
+ int rc, msg_len;
+ char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
+ char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
+
+ rc = lutil_b64_ntop((unsigned char *)salt->bv_val, salt->bv_len,
+ salt_b64, sizeof(salt_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+ b64_to_ab64(salt_b64);
+ rc = lutil_b64_ntop((unsigned char *)dk->bv_val, dk->bv_len,
+ dk_b64, sizeof(dk_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+ b64_to_ab64(dk_b64);
+ msg_len = asprintf(&msg->bv_val, "%s%d$%s$%s",
+ sc->bv_val, iteration,
+ salt_b64, dk_b64);
+ if(msg_len < 0){
+ msg->bv_len = 0;
+ return LUTIL_PASSWD_ERR;
+ }
+
+ msg->bv_len = msg_len;
+ return LUTIL_PASSWD_OK;
+}
+
+static int pbkdf2_encrypt(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *msg,
+ const char **text)
+{
+ unsigned char salt_value[PBKDF2_SALT_SIZE];
+ struct berval salt;
+ unsigned char dk_value[PBKDF2_MAX_DK_SIZE];
+ struct berval dk;
+ int iteration = PBKDF2_ITERATION;
+ int rc;
+#ifdef HAVE_OPENSSL
+ const EVP_MD *md;
+#elif HAVE_GNUTLS
+ struct hmac_sha1_ctx sha1_ctx;
+ struct hmac_sha256_ctx sha256_ctx;
+ struct hmac_sha512_ctx sha512_ctx;
+ void * current_ctx = NULL;
+ pbkdf2_hmac_update current_hmac_update = NULL;
+ pbkdf2_hmac_digest current_hmac_digest = NULL;
+#endif
+
+ salt.bv_val = (char *)salt_value;
+ salt.bv_len = sizeof(salt_value);
+ dk.bv_val = (char *)dk_value;
+
+#ifdef HAVE_OPENSSL
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk.bv_len = PBKDF2_SHA256_DK_SIZE;
+ md = EVP_sha256();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk.bv_len = PBKDF2_SHA512_DK_SIZE;
+ md = EVP_sha512();
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk.bv_len = PBKDF2_SHA256_DK_SIZE;
+ current_ctx = &sha256_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
+ hmac_sha256_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk.bv_len = PBKDF2_SHA512_DK_SIZE;
+ current_ctx = &sha512_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
+ hmac_sha512_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+
+ if(lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef HAVE_OPENSSL
+ if(!PKCS5_PBKDF2_HMAC(passwd->bv_val, passwd->bv_len,
+ (unsigned char *)salt.bv_val, salt.bv_len,
+ iteration, md, dk.bv_len, dk_value)){
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
+ dk.bv_len, iteration,
+ salt.bv_len, (const uint8_t *) salt.bv_val,
+ dk.bv_len, dk_value);
+#endif
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf("Encrypt for %s\n", scheme->bv_val);
+ printf(" Password:\t%s\n", passwd->bv_val);
+
+ printf(" Salt:\t\t");
+ int i;
+ for(i=0; i<salt.bv_len; i++){
+ printf("%02x", salt_value[i]);
+ }
+ printf("\n");
+ printf(" Iteration:\t%d\n", iteration);
+
+ printf(" DK:\t\t");
+ for(i=0; i<dk.bv_len; i++){
+ printf("%02x", dk_value[i]);
+ }
+ printf("\n");
+#endif
+
+ rc = pbkdf2_format(scheme, iteration, &salt, &dk, msg);
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf(" Output:\t%s\n", msg->bv_val);
+#endif
+
+ return rc;
+}
+
+static int pbkdf2_check(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ int rc;
+ int iteration;
+
+ /* salt_value require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
+ unsigned char salt_value[PBKDF2_SALT_SIZE + 1];
+ char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
+ /* dk_value require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
+ unsigned char dk_value[PBKDF2_MAX_DK_SIZE + 1];
+ char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
+ unsigned char input_dk_value[PBKDF2_MAX_DK_SIZE];
+ size_t dk_len;
+#ifdef HAVE_OPENSSL
+ const EVP_MD *md;
+#elif HAVE_GNUTLS
+ struct hmac_sha1_ctx sha1_ctx;
+ struct hmac_sha256_ctx sha256_ctx;
+ struct hmac_sha512_ctx sha512_ctx;
+ void * current_ctx = NULL;
+ pbkdf2_hmac_update current_hmac_update = NULL;
+ pbkdf2_hmac_digest current_hmac_digest = NULL;
+#endif
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf("Checking for %s\n", scheme->bv_val);
+ printf(" Stored Value:\t%s\n", passwd->bv_val);
+ printf(" Input Cred:\t%s\n", cred->bv_val);
+#endif
+
+#ifdef HAVE_OPENSSL
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk_len = PBKDF2_SHA256_DK_SIZE;
+ md = EVP_sha256();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk_len = PBKDF2_SHA512_DK_SIZE;
+ md = EVP_sha512();
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk_len = PBKDF2_SHA256_DK_SIZE;
+ current_ctx = &sha256_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
+ hmac_sha256_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk_len = PBKDF2_SHA512_DK_SIZE;
+ current_ctx = &sha512_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
+ hmac_sha512_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+
+ iteration = atoi(passwd->bv_val);
+ if(iteration < 1){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ char *ptr;
+ ptr = strchr(passwd->bv_val, '$');
+ if(!ptr){
+ return LUTIL_PASSWD_ERR;
+ }
+ ptr++; /* skip '$' */
+ rc = ab64_to_b64(ptr, salt_b64, sizeof(salt_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ptr = strchr(ptr, '$');
+ if(!ptr){
+ return LUTIL_PASSWD_ERR;
+ }
+ ptr++; /* skip '$' */
+ rc = ab64_to_b64(ptr, dk_b64, sizeof(dk_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* The targetsize require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
+ rc = lutil_b64_pton(salt_b64, salt_value, PBKDF2_SALT_SIZE + 1);
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* consistency check */
+ if(rc != PBKDF2_SALT_SIZE){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* The targetsize require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
+ rc = lutil_b64_pton(dk_b64, dk_value, sizeof(dk_value));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* consistency check */
+ if(rc != dk_len){
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef HAVE_OPENSSL
+ if(!PKCS5_PBKDF2_HMAC(cred->bv_val, cred->bv_len,
+ salt_value, PBKDF2_SALT_SIZE,
+ iteration, md, dk_len, input_dk_value)){
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
+ dk_len, iteration,
+ PBKDF2_SALT_SIZE, salt_value,
+ dk_len, input_dk_value);
+#endif
+
+ rc = memcmp(dk_value, input_dk_value, dk_len);
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf(" Iteration:\t%d\n", iteration);
+ printf(" Base64 Salt:\t%s\n", salt_b64);
+ printf(" Base64 DK:\t%s\n", dk_b64);
+ int i;
+ printf(" Stored Salt:\t");
+ for(i=0; i<PBKDF2_SALT_SIZE; i++){
+ printf("%02x", salt_value[i]);
+ }
+ printf("\n");
+
+ printf(" Stored DK:\t");
+ for(i=0; i<dk_len; i++){
+ printf("%02x", dk_value[i]);
+ }
+ printf("\n");
+
+ printf(" Input DK:\t");
+ for(i=0; i<dk_len; i++){
+ printf("%02x", input_dk_value[i]);
+ }
+ printf("\n");
+ printf(" Result:\t%d\n", rc);
+#endif
+ return rc?LUTIL_PASSWD_ERR:LUTIL_PASSWD_OK;
+}
+
+int init_module(int argc, char *argv[]) {
+ int rc;
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha1_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha256_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha512_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5 b/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5
new file mode 100644
index 0000000..3bacf62
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5
@@ -0,0 +1,112 @@
+.TH SLAPD-PW-PBKDF2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-pbkdf2 \- PBKDF2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-pbkdf2
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-pbkdf2
+module to
+.BR slapd (8)
+provides support for the use of the key stretching function
+PBKDF2 (Password-Based Key Derivation Function 2) following RFC 2898
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the following additional password schemes for use in slapd:
+.RS
+.TP
+.B {PBKDF2}
+alias to {PBKDF2-SHA1}
+.TP
+.B {PBKDF2-SHA1}
+PBKDF2 using HMAC-SHA-1 as the underlying pseudorandom function
+.TP
+.B {PBKDF2-SHA256}
+PBKDF2 using HMAC-SHA-256 as the underlying pseudorandom function
+.TP
+.B {PBKDF2-SHA512}
+PBKDF2 using HMAC-SHA-512 as the underlying pseudorandom function
+.RE
+
+.SH CONFIGURATION
+The
+.B pw-pbkdf2
+module does not need any configuration.
+.LP
+After loading the module, the password schemes
+{PBKDF2}, {PBKDF2-SHA1}, {PBKDF2-SHA256}, and {PBKDF2-SHA512}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use these schemes when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5).
+
+.SH NOTES
+If you want to use the schemes described here with
+.BR slappasswd (8),
+remember to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = pw-pbkdf2
+.LP
+.RE
+Depending on
+.BR pw-pbkdf2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+All of the userPassword LDAP attributes below encode the password
+.RI ' secret '.
+.EX
+.LP
+userPassword: {PBKDF2-SHA512}10000$/oQ4xZi382mk7kvCd3ZdkA$2wqjpuyV2l0U/a1QwoQPOtlQL.UcJGNACj1O24balruqQb/NgPW6OCvvrrJP8.SzA3/5iYvLnwWPzeX8IK/bEQ
+.LP
+userPassword: {PBKDF2-SHA256}10000$jq40ImWtmpTE.aYDYV1GfQ$mpiL4ui02ACmYOAnCjp/MI1gQk50xLbZ54RZneU0fCg
+.LP
+userPassword: {PBKDF2-SHA1}10000$QJTEclnXgh9Cz3ChCWpdAg$9.s98jwFJM.NXJK9ca/oJ5AyoAQ
+.EE
+.LP
+To make {PBKDF2-SHA512} the password hash used in Password Modify extended operations,
+simply set this line in slapd.conf(5):
+.EX
+.LP
+password-hash {PBKDF2-SHA512}
+.EX
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by HAMANO Tsukasa <hamano@osstech.co.jp>
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/radius.c b/contrib/slapd-modules/passwd/radius.c
new file mode 100644
index 0000000..8474bf5
--- /dev/null
+++ b/contrib/slapd-modules/passwd/radius.c
@@ -0,0 +1,149 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 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>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <lber.h>
+#include <lber_pvt.h> /* BER_BVC definition */
+#include "lutil.h"
+#include <ldap_pvt_thread.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <radlib.h>
+
+extern char *global_host; /* from slapd */
+static LUTIL_PASSWD_CHK_FUNC chk_radius;
+static const struct berval scheme = BER_BVC("{RADIUS}");
+static char *config_filename;
+static ldap_pvt_thread_mutex_t libradius_mutex;
+
+static int
+chk_radius(
+ const struct berval *sc,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ unsigned int i;
+ int rc = LUTIL_PASSWD_ERR;
+
+ struct rad_handle *h = NULL;
+
+ for ( i = 0; i < cred->bv_len; i++ ) {
+ if ( cred->bv_val[ i ] == '\0' ) {
+ return LUTIL_PASSWD_ERR; /* NUL character in cred */
+ }
+ }
+
+ if ( cred->bv_val[ i ] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for ( i = 0; i < passwd->bv_len; i++ ) {
+ if ( passwd->bv_val[ i ] == '\0' ) {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if ( passwd->bv_val[ i ] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ ldap_pvt_thread_mutex_lock( &libradius_mutex );
+
+ h = rad_auth_open();
+ if ( h == NULL ) {
+ ldap_pvt_thread_mutex_unlock( &libradius_mutex );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ if ( rad_config( h, config_filename ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_create_request( h, RAD_ACCESS_REQUEST ) ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_USER_NAME, passwd->bv_val ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_USER_PASSWORD, cred->bv_val ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_NAS_IDENTIFIER, global_host ) != 0 ) {
+ goto done;
+ }
+
+ switch ( rad_send_request( h ) ) {
+ case RAD_ACCESS_ACCEPT:
+ rc = LUTIL_PASSWD_OK;
+ break;
+
+ case RAD_ACCESS_REJECT:
+ rc = LUTIL_PASSWD_ERR;
+ break;
+
+ case RAD_ACCESS_CHALLENGE:
+ rc = LUTIL_PASSWD_ERR;
+ break;
+
+ case -1:
+ /* no valid response is received */
+ break;
+ }
+
+done:;
+ rad_close( h );
+
+ ldap_pvt_thread_mutex_unlock( &libradius_mutex );
+ return rc;
+}
+
+int
+term_module()
+{
+ return ldap_pvt_thread_mutex_destroy( &libradius_mutex );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ int i;
+
+ for ( i = 0; i < argc; i++ ) {
+ if ( strncasecmp( argv[ i ], "config=", STRLENOF( "config=" ) ) == 0 ) {
+ /* FIXME: what if multiple loads of same module?
+ * does it make sense (e.g. override an existing one)? */
+ if ( config_filename == NULL ) {
+ config_filename = ber_strdup( &argv[ i ][ STRLENOF( "config=" ) ] );
+ }
+
+ } else {
+ fprintf( stderr, "init_module(radius): unknown arg#%d=\"%s\".\n",
+ i, argv[ i ] );
+ return 1;
+ }
+ }
+
+ ldap_pvt_thread_mutex_init( &libradius_mutex );
+
+ return lutil_passwd_add( (struct berval *)&scheme, chk_radius, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/sha2/Makefile b/contrib/slapd-modules/passwd/sha2/Makefile
new file mode 100644
index 0000000..2d20756
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/Makefile
@@ -0,0 +1,59 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+#DEFS = -DSLAPD_SHA2_DEBUG
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-sha2.la
+MANPAGES = slapd-pw-sha2.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-sha2.la: slapd-sha2.lo sha2.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/sha2/README b/contrib/slapd-modules/passwd/sha2/README
new file mode 100644
index 0000000..bab1dcd
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/README
@@ -0,0 +1,144 @@
+SHA-2 OpenLDAP support
+----------------------
+
+slapd-sha2.c provides support for SSHA-512, SSHA-384, SSHA-256,
+SHA-512, SHA-384 and SHA-256 hashed passwords in OpenLDAP. For
+instance, one could have the LDAP attribute:
+
+userPassword: {SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==
+
+or:
+
+userPassword: {SHA384}WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+
+or:
+
+userPassword: {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+
+all of which encode the password 'secret'.
+
+
+Building
+--------
+
+1) Customize the OPENLDAP variable in Makefile to point to the OpenLDAP
+source root.
+
+For initial testing you might also want to edit DEFS to define
+SLAPD_SHA2_DEBUG, which enables logging to stderr (don't leave this on
+in production, as it prints passwords in cleartext).
+
+2) Run 'make' to produce slapd-sha2.so
+
+3) Copy slapd-sha2.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/slapd-sha2.so
+
+5) Restart slapd.
+
+
+Configuring
+-----------
+
+The {SSHA256}, {SSHA384}, {SSHA512}, {SSHA256}, {SHA384} and {SHA512}
+password schemes should now be recognised.
+
+You can also tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+password-hash {SSHA512}
+
+
+Testing
+-------
+
+A quick way to test whether it's working is to customize the rootdn and
+rootpw in slapd.conf, eg:
+
+rootdn "cn=admin,dc=example,dc=com"
+# This encrypts the string 'secret'
+
+rootpw {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+
+Then to test, run something like:
+
+ldapsearch -b "dc=example,dc=com" -D "cn=admin,dc=example,dc=com" -x -w secret
+
+
+-- Test hashes:
+
+Test hashes can be generated with openssl:
+
+$ echo -n "secret" | openssl dgst -sha256 -binary | openssl enc -base64
+K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+$ echo -n "secret" | openssl dgst -sha384 -binary | openssl enc -base64
+WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+$ echo -n "secret" | openssl dgst -sha512 -binary | openssl enc -base64
+vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cm
+W192CF5bDufKRpayrW/isg==
+
+(join those lines up to form the full hash)
+
+
+
+Alternatively we could modify an existing user's password with
+ldappasswd, and then test binding as that user:
+
+$ ldappasswd -D "cn=admin,dc=example,dc=com" -x -W -S uid=jturner,ou=People,dc=example,dc=com
+New password: secret
+Re-enter new password: secret
+Enter LDAP Password: <cn=admin's password>
+
+$ ldapsearch -b "dc=example,dc=com" -D "uid=jturner,ou=People,dc=example,dc=com" -x -w secret
+
+
+Debugging (SHA-512, SHA-384 and SHA-256 only)
+---------------------------------------------
+
+To see what's going on, recompile with SLAPD_SHA2_DEBUG (use the
+commented-out DEFS in Makefile), and then run slapd from the console
+to see stderr:
+
+$ sudo /etc/init.d/slapd stop
+Stopping OpenLDAP: slapd.
+$ sudo /usr/sbin/slapd -f /etc/ldap/slapd.conf -h ldap://localhost:389 -d stats
+@(#) $OpenLDAP$
+ buildd@palmer:/build/buildd/openldap2.3-2.4.9/debian/build/servers/slapd
+slapd starting
+...
+Validating password
+ Hash scheme: {SHA256}
+ Password to validate: secret
+ Password hash: K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+ Stored password hash: K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+ Result: match
+conn=0 op=0 BIND dn="cn=admin,dc=example,dc=com" mech=SIMPLE ssf=0
+conn=0 op=0 RESULT tag=97 err=0 text=
+conn=0 op=1 SRCH base="dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)"
+conn=0 fd=12 closed (connection lost)
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2009-2022 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>.
+
+---
+
+ACKNOWLEDGEMENT:
+This work was initially developed by Jeff Turner for inclusion in
+OpenLDAP Software, based upon the SHA-2 implementation independently
+developed by Aaron Gifford.
+
diff --git a/contrib/slapd-modules/passwd/sha2/sha2.c b/contrib/slapd-modules/passwd/sha2/sha2.c
new file mode 100644
index 0000000..047741a
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/sha2.c
@@ -0,0 +1,1070 @@
+/* $OpenLDAP$ */
+/*
+ * FILE: sha2.c
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
+#include <assert.h> /* assert() */
+#include "sha2.h"
+
+/*
+ * ASSERT NOTE:
+ * Some sanity checking code is included using assert(). On my FreeBSD
+ * system, this additional code can be removed by compiling with NDEBUG
+ * defined. Check your own systems manpage on assert() to see how to
+ * compile WITHOUT the sanity checking code on your system.
+ *
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file). Either define on the command line, for example:
+ *
+ * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ * #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER. If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivalent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ * #define LITTLE_ENDIAN 1234
+ * #define BIG_ENDIAN 4321
+ *
+ * And for little-endian machines, add:
+ *
+ * #define BYTE_ORDER LITTLE_ENDIAN
+ *
+ * Or for big-endian machines:
+ *
+ * #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
+#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
+#endif
+
+/*
+ * Define the followingsha2_* types to types of the correct length on
+ * the native architecture. Most BSD systems and Linux define u_intXX_t
+ * types. Machines with very recent ANSI C headers, can use the
+ * uintXX_t definitions from inttypes.h by defining SHA2_USE_INTTYPES_H
+ * during compile or in the sha.h header file.
+ *
+ * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
+ * will need to define these three typedefs below (and the appropriate
+ * ones in sha.h too) by hand according to their system architecture.
+ *
+ * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
+ * types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef uint8_t sha2_byte; /* Exactly 1 byte */
+typedef uint32_t sha2_word32; /* Exactly 4 bytes */
+typedef uint64_t sha2_word64; /* Exactly 8 bytes */
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef u_int8_t sha2_byte; /* Exactly 1 byte */
+typedef u_int32_t sha2_word32; /* Exactly 4 bytes */
+typedef u_int64_t sha2_word64; /* Exactly 8 bytes */
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
+#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
+#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define REVERSE32(w,x) { \
+ sha2_word32 tmp = (w); \
+ tmp = (tmp >> 16) | (tmp << 16); \
+ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x) { \
+ sha2_word64 tmp = (w); \
+ tmp = (tmp >> 32) | (tmp << 32); \
+ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+ ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n) { \
+ (w)[0] += (sha2_word64)(n); \
+ if ((w)[0] < (n)) { \
+ (w)[1]++; \
+ } \
+}
+
+/*
+ * Macros for copying blocks of memory and for zeroing out ranges
+ * of memory. Using these macros makes it easy to switch from
+ * using memset()/memcpy() and using bzero()/bcopy().
+ *
+ * Please define either SHA2_USE_MEMSET_MEMCPY or define
+ * SHA2_USE_BZERO_BCOPY depending on which function set you
+ * choose to use:
+ */
+#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
+/* Default to memset()/memcpy() if no option is specified */
+#define SHA2_USE_MEMSET_MEMCPY 1
+#endif
+#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
+/* Abort with an error if BOTH options are defined */
+#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
+#endif
+
+#ifdef SHA2_USE_MEMSET_MEMCPY
+#define MEMSET_BZERO(p,l) memset((p), 0, (l))
+#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
+#endif
+#ifdef SHA2_USE_BZERO_BCOPY
+#define MEMSET_BZERO(p,l) bzero((p), (l))
+#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
+#endif
+
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ * NOTE: The naming of R and S appears backwards here (R is a SHIFT and
+ * S is a ROTATION) because the SHA-256/384/512 description document
+ * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ * same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) ((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
+#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
+#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+static void SHA512_Last(SHA512_CTX*);
+static void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
+static void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
+
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+const static sha2_word32 K256[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-256: */
+const static sha2_word32 sha256_initial_hash_value[8] = {
+ 0x6a09e667UL,
+ 0xbb67ae85UL,
+ 0x3c6ef372UL,
+ 0xa54ff53aUL,
+ 0x510e527fUL,
+ 0x9b05688cUL,
+ 0x1f83d9abUL,
+ 0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+const static sha2_word64 K512[80] = {
+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+const static sha2_word64 sha384_initial_hash_value[8] = {
+ 0xcbbb9d5dc1059ed8ULL,
+ 0x629a292a367cd507ULL,
+ 0x9159015a3070dd17ULL,
+ 0x152fecd8f70e5939ULL,
+ 0x67332667ffc00b31ULL,
+ 0x8eb44a8768581511ULL,
+ 0xdb0c2e0d64f98fa7ULL,
+ 0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+const static sha2_word64 sha512_initial_hash_value[8] = {
+ 0x6a09e667f3bcc908ULL,
+ 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL,
+ 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL,
+ 0x5be0cd19137e2179ULL
+};
+
+/*
+ * Constant used by SHA256/384/512_End() functions for converting the
+ * digest to a readable hexadecimal character string:
+ */
+static const char *sha2_hex_digits = "0123456789abcdef";
+
+
+/*** SHA-256: *********************************************************/
+void SHA256_Init(SHA256_CTX* context) {
+ if (context == (SHA256_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);
+ context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE32(*data++, W256[j]); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + W256[j]; \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + (W256[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256(a,b,c,d,e,f,g,h) \
+ s0 = W256[(j+1)&0x0f]; \
+ s0 = sigma0_256(s0); \
+ s1 = W256[(j+14)&0x0f]; \
+ s1 = sigma1_256(s1); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ /* Rounds 0 to 15 (unrolled): */
+ ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds to 64: */
+ do {
+ ROUND256(a,b,c,d,e,f,g,h);
+ ROUND256(h,a,b,c,d,e,f,g);
+ ROUND256(g,h,a,b,c,d,e,f);
+ ROUND256(f,g,h,a,b,c,d,e);
+ ROUND256(e,f,g,h,a,b,c,d);
+ ROUND256(d,e,f,g,h,a,b,c);
+ ROUND256(c,d,e,f,g,h,a,b);
+ ROUND256(b,c,d,e,f,g,h,a);
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, T2, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Copy data while converting to host byte order */
+ REVERSE32(*data++,W256[j]);
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-256 compression function to update a..h with copy */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W256[(j+1)&0x0f];
+ s0 = sigma0_256(s0);
+ s1 = W256[(j+14)&0x0f];
+ s1 = sigma1_256(s1);
+
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA256_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ context->bitcount += freespace << 3;
+ len -= freespace;
+ data += freespace;
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ context->bitcount += len << 3;
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA256_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA256_Transform(context, (sha2_word32*)data);
+ context->bitcount += SHA256_BLOCK_LENGTH << 3;
+ len -= SHA256_BLOCK_LENGTH;
+ data += SHA256_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ context->bitcount += len << 3;
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
+ sha2_word32 *d = (sha2_word32*)digest;
+ sha2_word64 *p;
+ unsigned int usedspace;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount,context->bitcount);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA256_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+ }
+ } else {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Set the bit count: */
+ p = (sha2_word64 *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH];
+ *p = context->bitcount;
+
+ /* Final transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE32(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Clean up state data: */
+ MEMSET_BZERO(context, sizeof(*context));
+ usedspace = 0;
+}
+
+char *SHA256_End(SHA256_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA256_Final(digest, context);
+
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) {
+ SHA256_CTX context;
+
+ SHA256_Init(&context);
+ SHA256_Update(&context, data, len);
+ return SHA256_End(&context, digest);
+}
+
+
+/*** SHA-512: *********************************************************/
+void SHA512_Init(SHA512_CTX* context) {
+ if (context == (SHA512_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE64(*data++, W512[j]); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + W512[j]; \
+ (d) += T1, \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + (W512[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512(a,b,c,d,e,f,g,h) \
+ s0 = W512[(j+1)&0x0f]; \
+ s0 = sigma0_512(s0); \
+ s1 = W512[(j+14)&0x0f]; \
+ s1 = sigma1_512(s1); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds up to 79: */
+ do {
+ ROUND512(a,b,c,d,e,f,g,h);
+ ROUND512(h,a,b,c,d,e,f,g);
+ ROUND512(g,h,a,b,c,d,e,f);
+ ROUND512(f,g,h,a,b,c,d,e);
+ ROUND512(e,f,g,h,a,b,c,d);
+ ROUND512(d,e,f,g,h,a,b,c);
+ ROUND512(c,d,e,f,g,h,a,b);
+ ROUND512(b,c,d,e,f,g,h,a);
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert TO host byte order */
+ REVERSE64(*data++, W512[j]);
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-512 compression function to update a..h with copy */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W512[(j+1)&0x0f];
+ s0 = sigma0_512(s0);
+ s1 = W512[(j+14)&0x0f];
+ s1 = sigma1_512(s1);
+
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA512_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ ADDINC128(context->bitcount, freespace << 3);
+ len -= freespace;
+ data += freespace;
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ ADDINC128(context->bitcount, len << 3);
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA512_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA512_Transform(context, (sha2_word64*)data);
+ ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
+ len -= SHA512_BLOCK_LENGTH;
+ data += SHA512_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ ADDINC128(context->bitcount, len << 3);
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA512_Last(SHA512_CTX* context) {
+ sha2_word64 *p;
+ unsigned int usedspace;
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount[0],context->bitcount[0]);
+ REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA512_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);
+ }
+ } else {
+ /* Prepare for final transform: */
+ MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Store the length of input data (in bits): */
+ p = (sha2_word64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH];
+ p[0] = context->bitcount[1];
+ p[1] = context->bitcount[0];
+
+ /* Final transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+}
+
+void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last(context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA512_End(SHA512_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA512_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA512_Final(digest, context);
+
+ for (i = 0; i < SHA512_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) {
+ SHA512_CTX context;
+
+ SHA512_Init(&context);
+ SHA512_Update(&context, data, len);
+ return SHA512_End(&context, digest);
+}
+
+
+/*** SHA-384: *********************************************************/
+void SHA384_Init(SHA384_CTX* context) {
+ if (context == (SHA384_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
+ SHA512_Update((SHA512_CTX*)context, data, len);
+}
+
+void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last((SHA512_CTX*)context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 6; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA384_End(SHA384_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA384_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA384_Final(digest, context);
+
+ for (i = 0; i < SHA384_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) {
+ SHA384_CTX context;
+
+ SHA384_Init(&context);
+ SHA384_Update(&context, data, len);
+ return SHA384_End(&context, digest);
+}
+
diff --git a/contrib/slapd-modules/passwd/sha2/sha2.h b/contrib/slapd-modules/passwd/sha2/sha2.h
new file mode 100644
index 0000000..7fff142
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/sha2.h
@@ -0,0 +1,236 @@
+/* $OpenLDAP$ */
+/*
+ * FILE: sha2.h
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef __SHA2_H__
+#define __SHA2_H__
+
+#include "portable.h"
+
+#ifdef HAVE_INTTYPES_H
+# define SHA2_USE_INTTYPES_H 1
+#endif
+
+#ifndef LITTLE_ENDIAN
+# define LITTLE_ENDIAN 1234
+#endif
+#ifndef BIG_ENDIAN
+# define BIG_ENDIAN 4321
+#endif
+#ifndef BYTE_ORDER
+# ifdef WORDS_BIGENDIAN
+# define BYTE_ORDER BIG_ENDIAN
+# else
+# define BYTE_ORDER LITTLE_ENDIAN
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import u_intXX_t size_t type definitions from system headers. You
+ * may need to change this, or define these things yourself in this
+ * file.
+ */
+#include <sys/types.h>
+
+#ifdef SHA2_USE_INTTYPES_H
+
+#include <inttypes.h>
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define SHA256_BLOCK_LENGTH 64
+#define SHA256_DIGEST_LENGTH 32
+#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
+#define SHA384_BLOCK_LENGTH 128
+#define SHA384_DIGEST_LENGTH 48
+#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+/* NOTE: If your architecture does not define either u_intXX_t types or
+ * uintXX_t (from inttypes.h), you may need to define things by hand
+ * for your system:
+ */
+#if 0
+typedef unsigned char u_int8_t; /* 1-byte (8-bits) */
+typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */
+typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */
+#endif
+/*
+ * Most BSD systems already define u_intXX_t types, as does Linux.
+ * Some systems, however, like Compaq's Tru64 Unix instead can use
+ * uintXX_t types defined by very recent ANSI C standards and included
+ * in the file:
+ *
+ * #include <inttypes.h>
+ *
+ * If you choose to use <inttypes.h> then please define:
+ *
+ * #define SHA2_USE_INTTYPES_H
+ *
+ * Or on the command line during compile:
+ *
+ * cc -DSHA2_USE_INTTYPES_H ...
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef struct _SHA256_CTX {
+ uint32_t state[8];
+ uint64_t bitcount;
+ uint8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ uint64_t state[8];
+ uint64_t bitcount[2];
+ uint8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef struct _SHA256_CTX {
+ u_int32_t state[8];
+ u_int64_t bitcount;
+ u_int8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ u_int64_t state[8];
+ u_int64_t bitcount[2];
+ u_int8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+typedef SHA512_CTX SHA384_CTX;
+
+
+/*** SHA-256/384/512 Function Prototypes ******************************/
+/* avoid symbol clash with other crypto libs */
+#define SHA256_Init pw_SHA256_Init
+#define SHA256_Update pw_SHA256_Update
+#define SHA256_Final pw_SHA256_Final
+#define SHA256_End pw_SHA256_End
+#define SHA256_Data pw_SHA256_Data
+
+#define SHA384_Init pw_SHA384_Init
+#define SHA384_Update pw_SHA384_Update
+#define SHA384_Final pw_SHA384_Final
+#define SHA384_End pw_SHA384_End
+#define SHA384_Data pw_SHA384_Data
+
+#define SHA512_Init pw_SHA512_Init
+#define SHA512_Update pw_SHA512_Update
+#define SHA512_Final pw_SHA512_Final
+#define SHA512_End pw_SHA512_End
+#define SHA512_Data pw_SHA512_Data
+
+#ifndef NOPROTO
+#ifdef SHA2_USE_INTTYPES_H
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
+void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
+void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
+void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#else /* SHA2_USE_INTTYPES_H */
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t);
+void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t);
+void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t);
+void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+#else /* NOPROTO */
+
+void SHA256_Init();
+void SHA256_Update();
+void SHA256_Final();
+char* SHA256_End();
+char* SHA256_Data();
+
+void SHA384_Init();
+void SHA384_Update();
+void SHA384_Final();
+char* SHA384_End();
+char* SHA384_Data();
+
+void SHA512_Init();
+void SHA512_Update();
+void SHA512_Final();
+char* SHA512_End();
+char* SHA512_Data();
+
+#endif /* NOPROTO */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __SHA2_H__ */
+
diff --git a/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5 b/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5
new file mode 100644
index 0000000..f700b52
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5
@@ -0,0 +1,118 @@
+.TH SLAPD-PW-SHA2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-sha2 \- SHA-2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-sha2
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-sha2
+module to
+.BR slapd (8)
+provides support for the use of SSHA-512, SSHA-384, SSHA-256, SHA-512,
+SHA-384 and SHA-256 from the SHA-2 family (FIPS 180-2) of hash functions
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the following additional password schemes for use in slapd:
+.RS
+.TP
+.B {SSHA256}
+SHA-256 with salt, giving hash values of 256 bits length
+.TP
+.B {SHA256}
+plain SHA-256 giving hash values of 256 bits length
+.TP
+.B {SSHA384}
+SHA-384 with salt, giving hash values of 384 bits length
+.TP
+.B {SHA384}
+plain SHA-384 giving hash values of 384 bits length
+.TP
+.B {SSHA512}
+SHA-512 with salt, giving hash values of 512 bits length
+.TP
+.B {SHA512}
+plain SHA-512 giving hash values of 512 bits length
+.RE
+
+.SH CONFIGURATION
+The
+.B pw-sha2
+module does not need any configuration.
+.LP
+After loading the module, the password schemes
+{SSHA256}, {SSHA384}, {SSHA512}, {SSHA256}, {SHA384}, and {SHA512}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use these schemes when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5).
+
+.SH NOTES
+If you want to use the schemes described here with
+.BR slappasswd (8),
+don't forget to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = pw-sha2
+.LP
+.RE
+Depending on
+.BR pw-sha2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+All of the userPassword LDAP attributes below encode the password
+.RI ' secret '.
+.EX
+.LP
+userPassword: {SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==
+.LP
+userPassword: {SHA384}WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+.LP
+userPassword: {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+.EE
+.LP
+To make {SSHA512} the password hash used in Password Modify extended operations,
+simply set this line in slapd.conf(5):
+.EX
+.LP
+password-hash {SSHA512}
+.EX
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by Jeff Turner.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/sha2/slapd-sha2.c b/contrib/slapd-modules/passwd/sha2/slapd-sha2.c
new file mode 100644
index 0000000..d67afda
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/slapd-sha2.c
@@ -0,0 +1,508 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 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>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Jeff Turner for inclusion
+ * in OpenLDAP Software.
+ *
+ * Hash methods for passwords generation added by Cédric Delfosse.
+ *
+ * SSHA256 / SSHA384 / SSHA512 support added, and chk_sha*() replaced
+ * with libraries/liblutil/passwd.c:chk_sha1() implementation to
+ * fix a race by SATOH Fumiyasu @ OSS Technology, Inc.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+#include "lber_pvt.h"
+#include "lutil.h"
+#include "sha2.h"
+
+#ifdef SLAPD_SHA2_DEBUG
+#include <stdio.h>
+#endif
+
+#define SHA2_SALT_SIZE 8
+
+static int hash_ssha256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA256_CTX ct;
+ unsigned char hash256[SHA256_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash256;
+ digest.bv_len = sizeof(hash256);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA256_Init(&ct);
+ SHA256_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA256_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA256_Final(hash256, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA256_CTX ct;
+ unsigned char hash256[SHA256_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash256;
+ digest.bv_len = sizeof(hash256);
+
+ SHA256_Init(&ct);
+ SHA256_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA256_Final(hash256, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+static int hash_ssha384(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA384_CTX ct;
+ unsigned char hash384[SHA384_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash384;
+ digest.bv_len = sizeof(hash384);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA384_Init(&ct);
+ SHA384_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA384_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA384_Final(hash384, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha384(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA384_CTX ct;
+ unsigned char hash384[SHA384_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash384;
+ digest.bv_len = sizeof(hash384);
+
+ SHA384_Init(&ct);
+ SHA384_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA384_Final(hash384, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+static int hash_ssha512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA512_CTX ct;
+ unsigned char hash512[SHA512_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash512;
+ digest.bv_len = sizeof(hash512);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA512_Init(&ct);
+ SHA512_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA512_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA512_Final(hash512, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA512_CTX ct;
+ unsigned char hash512[SHA512_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash512;
+ digest.bv_len = sizeof(hash512);
+
+ SHA512_Init(&ct);
+ SHA512_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA512_Final(hash512, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+#ifdef SLAPD_SHA2_DEBUG
+static void chk_sha_debug(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char *cred_hash,
+ size_t cred_len,
+ int cmp_rc)
+{
+ int rc;
+ struct berval cred_b64;
+
+ cred_b64.bv_len = LUTIL_BASE64_ENCODE_LEN(cred_len) + 1;
+ cred_b64.bv_val = ber_memalloc(cred_b64.bv_len + 1);
+
+ if( cred_b64.bv_val == NULL ) {
+ return;
+ }
+
+ rc = lutil_b64_ntop(
+ (unsigned char *) cred_hash, cred_len,
+ cred_b64.bv_val, cred_b64.bv_len );
+
+ if( rc < 0 ) {
+ ber_memfree(cred_b64.bv_val);
+ return;
+ }
+
+ fprintf(stderr, "Validating password\n");
+ fprintf(stderr, " Hash scheme:\t\t%s\n", scheme->bv_val);
+ fprintf(stderr, " Password to validate: %s\n", cred->bv_val);
+ fprintf(stderr, " Password hash:\t%s\n", cred_b64.bv_val);
+ fprintf(stderr, " Stored password hash:\t%s\n", passwd->bv_val);
+ fprintf(stderr, " Result:\t\t%s\n", cmp_rc ? "do not match" : "match");
+
+ ber_memfree(cred_b64.bv_val);
+}
+#endif
+
+static int chk_ssha256(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA256_CTX SHAcontext;
+ unsigned char SHAdigest[SHA256_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA256_Init(&SHAcontext);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA256_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha256(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA256_CTX SHAcontext;
+ unsigned char SHAdigest[SHA256_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA256_Init(&SHAcontext);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA256_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_ssha384(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA384_CTX SHAcontext;
+ unsigned char SHAdigest[SHA384_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA384_Init(&SHAcontext);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA384_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha384(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA384_CTX SHAcontext;
+ unsigned char SHAdigest[SHA384_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA384_Init(&SHAcontext);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA384_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_ssha512(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA512_CTX SHAcontext;
+ unsigned char SHAdigest[SHA512_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA512_Init(&SHAcontext);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA512_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha512(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA512_CTX SHAcontext;
+ unsigned char SHAdigest[SHA512_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA512_Init(&SHAcontext);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA512_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+const struct berval ssha256scheme = BER_BVC("{SSHA256}");
+const struct berval sha256scheme = BER_BVC("{SHA256}");
+const struct berval ssha384scheme = BER_BVC("{SSHA384}");
+const struct berval sha384scheme = BER_BVC("{SHA384}");
+const struct berval ssha512scheme = BER_BVC("{SSHA512}");
+const struct berval sha512scheme = BER_BVC("{SHA512}");
+
+int init_module(int argc, char *argv[]) {
+ int result = 0;
+ result = lutil_passwd_add( (struct berval *)&ssha256scheme, chk_ssha256, hash_ssha256 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha256scheme, chk_sha256, hash_sha256 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&ssha384scheme, chk_ssha384, hash_ssha384 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha384scheme, chk_sha384, hash_sha384 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&ssha512scheme, chk_ssha512, hash_ssha512 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha512scheme, chk_sha512, hash_sha512 );
+ return result;
+}
diff --git a/contrib/slapd-modules/passwd/slapd-pw-radius.5 b/contrib/slapd-modules/passwd/slapd-pw-radius.5
new file mode 100644
index 0000000..9a74847
--- /dev/null
+++ b/contrib/slapd-modules/passwd/slapd-pw-radius.5
@@ -0,0 +1,110 @@
+.TH SLAPD-PW-RADIUS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-radius \- Radius backend password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-radius
+.I /path/to/radius.conf
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-radius
+module to
+.BR slapd (8)
+provides support for using a RADIUS infrastructure as backend to
+verify the password provided in Simple Bind operations to OpenLDAP.
+.LP
+It does so by providing an additional password scheme for use in slapd:
+.RS
+.TP
+.B {RADIUS}
+RADIUS password scheme
+.RE
+.LP
+Unlike in other password schemes, the value following the scheme is not
+a - potentially hashed - password, but the name of the corresponding
+RADIUS user in the RADIUS infrastructure.
+.LP
+This value, together with the password used in the Simple Bind operation,
+will be sent to the RADIUS server for authentication.
+.LP
+If the RADIUS server successfully authenticates the user,
+then the password verification succeeds, resulting in the LDAP Bind
+operation's success.
+.LP
+Conversely, failed RADIUS authentications leads to failing LDAP Binds.
+
+.SH CONFIGURATION
+The
+.B pw-radius
+module needs no configuration beyond the additional
+.I filename
+argument to
+.BR slapd.conf (5)'s
+.B moduleload
+directive.
+This filename is expected to point to a valid
+.BR radius.conf (5).
+file adhering to
+.BR libradius (3).
+.LP
+After loading the module, the password scheme
+.B {RADIUS}
+will be recognised in values of the
+.I userPassword
+attribute.
+
+.SH NOTES
+Owing to its construction, using the
+.B {RADIUS}
+scheme as argument to the
+.BR password-hash
+option in
+.BR slapd.conf (5)
+does not make much sense, because of the scheme's construction.
+.LP
+This also applies to the use of the
+.B {RADIUS}
+scheme in
+.B slappasswd
+or
+.BR ldappasswd .
+
+
+.SH EXAMPLES
+To indicate that Simple Bind operations shall use the RADIUS user
+.B johndoe
+when validating passwords against the RADIUS infrastructure,
+set a user's LDAP attribute userPassword to:
+.EX
+.LP
+userPassword: {RADIUS}johndoe
+.EE
+
+.SH LIMITATIONS
+Due to the way the configuration is loaded (additional argument
+to slapd.conf's moduleload directive), this module cannot be used
+with table-driven configuration.
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR libradius (3)
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/totp/Makefile b/contrib/slapd-modules/passwd/totp/Makefile
new file mode 100644
index 0000000..f7dff4b
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-totp.la
+MANPAGES = slapo-totp.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-totp.la: slapd-totp.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/totp/README b/contrib/slapd-modules/passwd/totp/README
new file mode 100644
index 0000000..e6867f2
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/README
@@ -0,0 +1,87 @@
+TOTP OpenLDAP support
+----------------------
+
+slapd-totp.c provides support for RFC 6238 TOTP Time-based One
+Time Passwords in OpenLDAP using SHA-1, SHA-256, and SHA-512.
+For instance, one could have the LDAP attribute:
+
+userPassword: {TOTP1}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
+
+which encodes the key '12345678901234567890'.
+
+It can also encode credentials consisting of a TOTP and a static
+password. The format for this is:
+
+userPassword: {TOTP1ANDPW}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ|<some_other_passwd>
+
+where <some_other_passwd> can be any scheme currently understood
+by OpenLDAP. For example, using '{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ='
+would encode the above TOTP with a static password of 'secret'. To
+authenticate using this scheme, enter the static password immediately
+followed by the TOTP, for example 'secret123456'.
+
+
+Building
+--------
+
+1) Customize the LDAP_SRC variable in Makefile to point to the OpenLDAP
+source root.
+
+2) Run 'make' to produce slapd-totp.so
+
+3) Copy slapd-totp.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/slapd-totp.so
+
+5) This module replaces the function of the slapo-lastbind overlay. You
+cannot use that overlay on the same database as this one.
+
+6) Restart slapd.
+
+
+Configuring
+-----------
+
+The {TOTP1}, {TOTP256}, {TOTP512}, {TOTP1ANDPW}, {TOTP256ANDPW},
+and {TOTP512ANDPW} password schemes should now be recognised.
+
+You can also tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+password-hash {TOTP1}
+
+TOTP password schemes will only work on databases that have a rootdn and the
+totp overlay configured:
+
+database mdb
+rootdn "..."
+...
+
+overlay totp
+
+
+
+Testing
+-------
+
+The TOTP1 algorithm is compatible with Google Authenticator.
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2015-2022 The OpenLDAP Foundation.
+Portions Copyright 2015 by Howard Chu, Symas Corp.
+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>.
+
diff --git a/contrib/slapd-modules/passwd/totp/slapd-totp.c b/contrib/slapd-modules/passwd/totp/slapd-totp.c
new file mode 100644
index 0000000..25081e1
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/slapd-totp.c
@@ -0,0 +1,1000 @@
+/* slapd-totp.c - Password module and overlay for TOTP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2015-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2015 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work includes code from the lastbind overlay.
+ */
+
+#include <portable.h>
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#if HAVE_OPENSSL
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_LENGTH
+#define TOTP_SHA1 EVP_sha1()
+#define TOTP_SHA256 EVP_sha256()
+#define TOTP_SHA512 EVP_sha512()
+#define TOTP_HMAC_CTX HMAC_CTX *
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static HMAC_CTX *HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ HMAC_CTX_init(ctx);
+ }
+ return ctx;
+}
+
+static void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ if (ctx != NULL) {
+ HMAC_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+#define HMAC_setup(ctx, key, len, hash) \
+ ctx = HMAC_CTX_new(); \
+ HMAC_Init_ex(ctx, key, len, hash, 0)
+#define HMAC_crunch(ctx, buf, len) HMAC_Update(ctx, buf, len)
+#define HMAC_finish(ctx, dig, dlen) \
+ HMAC_Final(ctx, dig, &dlen); \
+ HMAC_CTX_free(ctx)
+
+#elif HAVE_GNUTLS
+#include <nettle/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_SIZE
+#define TOTP_SHA1 &nettle_sha1
+#define TOTP_SHA256 &nettle_sha256
+#define TOTP_SHA512 &nettle_sha512
+#define TOTP_HMAC_CTX struct hmac_sha512_ctx
+
+#define HMAC_setup(ctx, key, len, hash) \
+ const struct nettle_hash *h=hash;\
+ hmac_set_key(&ctx.outer, &ctx.inner, &ctx.state, h, len, key)
+#define HMAC_crunch(ctx, buf, len) hmac_update(&ctx.state, h, len, buf)
+#define HMAC_finish(ctx, dig, dlen) \
+ hmac_digest(&ctx.outer, &ctx.inner, &ctx.state, h, h->digest_size, dig);\
+ dlen = h->digest_size
+
+#else
+# error Unsupported crypto backend.
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+
+static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512,
+ chk_totp1andpw, chk_totp256andpw, chk_totp512andpw;
+static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512,
+ hash_totp1andpw, hash_totp256andpw, hash_totp512andpw;
+static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
+static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
+static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
+static const struct berval scheme_totp1andpw = BER_BVC("{TOTP1ANDPW}");
+static const struct berval scheme_totp256andpw = BER_BVC("{TOTP256ANDPW}");
+static const struct berval scheme_totp512andpw = BER_BVC("{TOTP512ANDPW}");
+
+static AttributeDescription *ad_authTimestamp;
+
+/* This is the definition used by ISODE, as supplied to us in
+ * ITS#6238 Followup #9
+ */
+static struct schema_info {
+ char *def;
+ AttributeDescription **ad;
+} totp_OpSchema[] = {
+ { "( 1.3.6.1.4.1.453.16.2.188 "
+ "NAME 'authTimestamp' "
+ "DESC 'last successful authentication using any method/mech' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
+ &ad_authTimestamp},
+ { NULL, NULL }
+};
+
+/* RFC3548 base32 encoding/decoding */
+
+static const char Base32[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+static const char Pad32 = '=';
+
+static int
+totp_b32_ntop(
+ u_char const *src,
+ size_t srclength,
+ char *target,
+ size_t targsize)
+{
+ size_t datalength = 0;
+ u_char input0;
+ u_int input1; /* assumed to be at least 32 bits */
+ u_char output[8];
+ int i;
+
+ while (4 < srclength) {
+ if (datalength + 8 > targsize)
+ return (-1);
+ input0 = *src++;
+ input1 = *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ srclength -= 5;
+
+ for (i=7; i>1; i--) {
+ output[i] = input1 & 0x1f;
+ input1 >>= 5;
+ }
+ output[0] = input0 >> 3;
+ output[1] = (input0 & 0x07) << 2 | input1;
+
+ for (i=0; i<8; i++)
+ target[datalength++] = Base32[output[i]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ static const int outlen[] = { 2,4,5,7 };
+ int n;
+ if (datalength + 8 > targsize)
+ return (-1);
+
+ /* Get what's left. */
+ input1 = *src++;
+ for (i = 1; i < srclength; i++) {
+ input1 <<= 8;
+ input1 |= *src++;
+ }
+ input1 <<= 8 * (4-srclength);
+ n = outlen[srclength-1];
+ for (i=0; i<n; i++) {
+ target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
+ input1 <<= 5;
+ }
+ for (; i<8; i++)
+ target[datalength++] = Pad32;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* converts characters, eight at a time, starting at src
+ from base - 32 numbers into five 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+static int
+totp_b32_pton(
+ char const *src,
+ u_char *target,
+ size_t targsize)
+{
+ int tarindex, state, ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (ch == Pad32)
+ break;
+
+ pos = strchr(Base32, ch);
+ if (pos == 0) /* A non-base32 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base32) << 3;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 2;
+ target[tarindex+1] = ((pos - Base32) & 0x3)
+ << 6 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ target[tarindex] |= (pos - Base32) << 1;
+ }
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 4;
+ target[tarindex+1] = ((pos - Base32) & 0xf)
+ << 4 ;
+ }
+ tarindex++;
+ state = 4;
+ break;
+ case 4:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 1;
+ target[tarindex+1] = ((pos - Base32) & 0x1)
+ << 7 ;
+ }
+ tarindex++;
+ state = 5;
+ break;
+ case 5:
+ if (target) {
+ target[tarindex] |= (pos - Base32) << 2;
+ }
+ state = 6;
+ break;
+ case 6:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 3;
+ target[tarindex+1] = ((pos - Base32) & 0x7)
+ << 5 ;
+ }
+ tarindex++;
+ state = 7;
+ break;
+ case 7:
+ if (target) {
+ target[tarindex] |= (pos - Base32);
+ }
+ state = 0;
+ tarindex++;
+ break;
+
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-32 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad32) { /* We got a pad char. */
+ int i = 0;
+
+ /* count pad chars */
+ for (; ch; ch = *src++) {
+ if (ch != Pad32)
+ return (-1);
+ i++;
+ }
+ /* there are only 4 valid ending states with a
+ * pad character, make sure the number of pads is valid.
+ */
+ switch(state) {
+ case 2: if (i != 6) return -1;
+ break;
+ case 4: if (i != 4) return -1;
+ break;
+ case 5: if (i != 3) return -1;
+ break;
+ case 7: if (i != 1) return -1;
+ break;
+ default:
+ return -1;
+ }
+ /*
+ * Now make sure that the "extra" bits that slopped past
+ * the last full byte were zeros. If we don't check them,
+ * they become a subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
+
+/* RFC6238 TOTP */
+
+
+typedef struct myval {
+ ber_len_t mv_len;
+ void *mv_val;
+} myval;
+
+static void do_hmac(
+ const void *hash,
+ myval *key,
+ myval *data,
+ myval *out)
+{
+ TOTP_HMAC_CTX ctx;
+ unsigned int digestLen;
+
+ HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
+ HMAC_crunch(ctx, data->mv_val, data->mv_len);
+ HMAC_finish(ctx, out->mv_val, digestLen);
+ out->mv_len = digestLen;
+}
+
+static const int DIGITS_POWER[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+
+static void generate(
+ myval *key,
+ uint64_t tval,
+ int digits,
+ myval *out,
+ const void *mech)
+{
+ unsigned char digest[TOTP_SHA512_DIGEST_LENGTH];
+ myval digval;
+ myval data;
+ unsigned char msg[8];
+ int i, offset, res, otp;
+
+#if WORDS_BIGENDIAN
+ *(uint64_t *)msg = tval;
+#else
+ for (i=7; i>=0; i--) {
+ msg[i] = tval & 0xff;
+ tval >>= 8;
+ }
+#endif
+
+ data.mv_val = msg;
+ data.mv_len = sizeof(msg);
+
+ digval.mv_val = digest;
+ digval.mv_len = sizeof(digest);
+ do_hmac(mech, key, &data, &digval);
+
+ offset = digest[digval.mv_len-1] & 0xf;
+ res = ((digest[offset] & 0x7f) << 24) |
+ ((digest[offset+1] & 0xff) << 16) |
+ ((digest[offset+2] & 0xff) << 8) |
+ (digest[offset+3] & 0xff);
+
+ otp = res % DIGITS_POWER[digits];
+ out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
+}
+
+static int totp_op_cleanup( Operation *op, SlapReply *rs );
+static int totp_bind_response( Operation *op, SlapReply *rs );
+
+#define TIME_STEP 30
+#define DIGITS 6
+#define DELIM '|' /* a single character */
+#define TOTP_AND_PW_HASH_SCHEME "{SSHA}"
+
+static int chk_totp(
+ const struct berval *passwd,
+ const struct berval *cred,
+ const void *mech,
+ const char **text)
+{
+ void *ctx, *op_tmp;
+ Operation *op;
+ Entry *e;
+ Attribute *a;
+ long t, told = 0;
+ int rc;
+ myval out, key;
+ char outbuf[32];
+
+ /* Find our thread context, find our Operation */
+ ctx = ldap_pvt_thread_pool_context();
+ if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
+ !op_tmp)
+ return LUTIL_PASSWD_ERR;
+ op = op_tmp;
+
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
+
+ /* Make sure previous login is older than current time */
+ t = op->o_time / TIME_STEP;
+ a = attr_find(e->e_attrs, ad_authTimestamp);
+ if (a) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
+ lutil_tm2time(&tm, &tt) == 0) {
+ told = tt.tt_sec / TIME_STEP;
+ if (told >= t)
+ rc = LUTIL_PASSWD_ERR;
+ }
+ if (!rc) { /* seems OK, remember old stamp */
+ slap_callback *sc;
+ for (sc = op->o_callback; sc; sc = sc->sc_next) {
+ if (sc->sc_response == totp_bind_response) {
+ sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx);
+ break;
+ }
+ }
+ }
+ } /* else no previous login, 1st use is OK */
+
+ be_entry_release_r(op, e);
+ if (rc) return rc;
+
+ /* Key is stored in base32 */
+ key.mv_len = passwd->bv_len * 5 / 8;
+ key.mv_val = ber_memalloc(key.mv_len+1);
+
+ if (!key.mv_val)
+ return LUTIL_PASSWD_ERR;
+
+ rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
+ if (rc < 1) {
+ rc = LUTIL_PASSWD_ERR;
+ goto out;
+ }
+
+ out.mv_val = outbuf;
+ out.mv_len = sizeof(outbuf);
+ generate(&key, t, DIGITS, &out, mech);
+
+ /* compare */
+ if (out.mv_len != cred->bv_len) {
+ rc = LUTIL_PASSWD_ERR;
+ goto out;
+ }
+
+ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+
+ /* If current value doesn't match, try again with previous value
+ * but only if the most recent login is older than the previous
+ * time step but still set */
+ if (rc == LUTIL_PASSWD_ERR && told < t - 1 && told > 0) {
+ out.mv_val = outbuf;
+ out.mv_len = sizeof(outbuf);
+ generate(&key, t - 1, DIGITS, &out, mech);
+ /* compare */
+ if (out.mv_len != cred->bv_len)
+ goto out;
+ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+
+out:
+ memset(key.mv_val, 0, key.mv_len);
+ ber_memfree(key.mv_val);
+ return rc;
+}
+
+static int chk_totp_and_pw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text,
+ const void *mech)
+{
+ char *s;
+ int rc = LUTIL_PASSWD_ERR, rc_pass, rc_otp;
+ ber_len_t len;
+ struct berval cred_pass, cred_otp, passwd_pass, passwd_otp;
+
+ /* Check credential length, no point to continue if too short */
+ if (cred->bv_len <= DIGITS)
+ return rc;
+
+ /* The OTP seed of the stored password */
+ s = strchr(passwd->bv_val, DELIM);
+ if (s) {
+ len = s - passwd->bv_val;
+ } else {
+ return rc;
+ }
+ if (!ber_str2bv(passwd->bv_val, len, 1, &passwd_otp))
+ return rc;
+
+ /* The password part of the stored password */
+ s++;
+ ber_str2bv(s, 0, 0, &passwd_pass);
+
+ /* The OTP part of the entered credential */
+ ber_str2bv(&cred->bv_val[cred->bv_len - DIGITS], DIGITS, 0, &cred_otp);
+
+ /* The password part of the entered credential */
+ if (!ber_str2bv(cred->bv_val, cred->bv_len - DIGITS, 0, &cred_pass)) {
+ /* Cleanup */
+ memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
+ ber_memfree(passwd_otp.bv_val);
+ return rc;
+ }
+
+ rc_otp = chk_totp(&passwd_otp, &cred_otp, mech, text);
+ rc_pass = lutil_passwd(&passwd_pass, &cred_pass, NULL, text);
+ if (rc_otp == LUTIL_PASSWD_OK && rc_pass == LUTIL_PASSWD_OK)
+ rc = LUTIL_PASSWD_OK;
+
+ /* Cleanup and return */
+ memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
+ ber_memfree(passwd_otp.bv_val);
+
+ return rc;
+}
+
+static int chk_totp1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA1, text);
+}
+
+static int chk_totp256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA256, text);
+}
+
+static int chk_totp512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA512, text);
+}
+
+static int chk_totp1andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA1);
+}
+
+static int chk_totp256andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA256);
+}
+
+static int chk_totp512andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA512);
+}
+
+static int passwd_string32(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash)
+{
+ int b32len = (passwd->bv_len + 4)/5 * 8;
+ int rc;
+ hash->bv_len = scheme->bv_len + b32len;
+ hash->bv_val = ber_memalloc(hash->bv_len + 1);
+ AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
+ rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
+ hash->bv_val + scheme->bv_len, b32len+1);
+ if (rc < 0) {
+ ber_memfree(hash->bv_val);
+ hash->bv_val = NULL;
+ return LUTIL_PASSWD_ERR;
+ }
+ return LUTIL_PASSWD_OK;
+}
+
+static int hash_totp_and_pw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ struct berval otp, pass, hash_otp, hash_pass;
+ ber_len_t len;
+ char *s;
+ int rc = LUTIL_PASSWD_ERR;
+
+ /* The OTP seed part */
+ s = strchr(passwd->bv_val, DELIM);
+ if (s) {
+ len = s - passwd->bv_val;
+ } else {
+ return rc;
+ }
+ if (!ber_str2bv(passwd->bv_val, len, 0, &otp))
+ return rc;
+
+ /* The static password part */
+ s++;
+ ber_str2bv(s, 0, 0, &pass);
+
+ /* Hash the OTP seed */
+ rc = passwd_string32(scheme, &otp, &hash_otp);
+
+ /* If successful, hash the static password, else cleanup and return */
+ if (rc == LUTIL_PASSWD_OK) {
+ rc = lutil_passwd_hash(&pass, TOTP_AND_PW_HASH_SCHEME,
+ &hash_pass, text);
+ } else {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* If successful, allocate memory to combine them, else cleanup
+ * and return */
+ if (rc == LUTIL_PASSWD_OK) {
+ /* Add 1 character to bv_len to hold DELIM */
+ hash->bv_len = hash_pass.bv_len + hash_otp.bv_len + 1;
+ hash->bv_val = ber_memalloc(hash->bv_len + 1);
+ if (!hash->bv_val)
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ memset(hash_otp.bv_val, 0, hash_otp.bv_len);
+ ber_memfree(hash_otp.bv_val);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* If successful, combine the two hashes with the delimiter */
+ if (rc == LUTIL_PASSWD_OK) {
+ AC_MEMCPY(hash->bv_val, hash_otp.bv_val, hash_otp.bv_len);
+ hash->bv_val[hash_otp.bv_len] = DELIM;
+ AC_MEMCPY(hash->bv_val + hash_otp.bv_len + 1,
+ hash_pass.bv_val, hash_pass.bv_len);
+ hash->bv_val[hash->bv_len] = '\0';
+ }
+
+ /* Cleanup and return */
+ memset(hash_otp.bv_val, 0, hash_otp.bv_len);
+ memset(hash_pass.bv_val, 0, hash_pass.bv_len);
+ ber_memfree(hash_otp.bv_val);
+ ber_memfree(hash_pass.bv_val);
+
+ return rc;
+}
+
+static int hash_totp1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp1andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int hash_totp256andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int hash_totp512andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int totp_op_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *cb;
+
+ /* clear out the current key */
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
+ NULL, 0, NULL, NULL );
+
+ /* free the callback */
+ cb = op->o_callback;
+ op->o_callback = cb->sc_next;
+ if (cb->sc_private)
+ ber_bvfree_x(cb->sc_private, op->o_tmpmemctx);
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ return 0;
+}
+
+static int
+totp_bind_response( Operation *op, SlapReply *rs )
+{
+ Modifications *mod = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ Entry *e;
+ int rc;
+
+ /* we're only interested if the bind was successful */
+ if ( rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd->bd_info = bi;
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ {
+ time_t now;
+ Attribute *a;
+ Modifications *m;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+
+ /* get the current time */
+ now = op->o_time;
+
+ /* update the authTimestamp in the user's entry with the current time */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+ m->sml_next = mod;
+ mod = m;
+
+ /* get authTimestamp attribute, if it exists */
+ if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) {
+ struct berval *bv = op->o_callback->sc_private;
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], bv );
+ ber_dupbv( &m->sml_nvalues[0], bv );
+ m->sml_next = mod;
+ mod = m;
+ }
+ }
+
+ be_entry_release_r( op, e );
+
+ /* perform the update */
+ if ( mod ) {
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+
+ /* This is a DSA-specific opattr, it never gets replicated. */
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = mod;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.o_dont_replicate = 1;
+ rc = op->o_bd->be_modify( &op2, &r2 );
+ slap_mods_free( mod, 1 );
+ if (rc != LDAP_SUCCESS) {
+ /* slapd has logged this as a success already, but we
+ * need to fail it because the authTimestamp changed
+ * out from under us.
+ */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ connection2anonymous(op->o_conn);
+ op2 = *op;
+ op2.o_callback = NULL;
+ send_ldap_result(&op2, rs);
+ op->o_bd->bd_info = bi;
+ return rs->sr_err;
+ }
+ }
+
+ op->o_bd->bd_info = bi;
+ return SLAP_CB_CONTINUE;
+}
+
+static int totp_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* If this is a simple Bind, stash the Op pointer so our chk
+ * function can find it. Set a cleanup callback to clear it
+ * out when the Bind completes.
+ */
+ if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
+ slap_callback *cb;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ totp_op_cleanup, op, 0, NULL, NULL );
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_response = totp_bind_response;
+ cb->sc_cleanup = totp_op_cleanup;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int totp_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ int rc = 0;
+
+ if (!ad_authTimestamp) {
+ const char *text = NULL;
+ rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
+ if (rc) {
+ rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 );
+ if (rc) {
+ snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)",
+ text, rc);
+ Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg );
+ }
+ ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
+ }
+ }
+ return rc;
+}
+
+static slap_overinst totp;
+
+int
+totp_initialize(void)
+{
+ int rc;
+
+ totp.on_bi.bi_type = "totp";
+
+ totp.on_bi.bi_db_open = totp_db_open;
+ totp.on_bi.bi_op_bind = totp_op_bind;
+
+ rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp1andpw, chk_totp1andpw, hash_totp1andpw);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp256andpw, chk_totp256andpw, hash_totp256andpw);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp512andpw, chk_totp512andpw, hash_totp512andpw);
+ if (rc)
+ return rc;
+
+ return overlay_register(&totp);
+}
+
+int init_module(int argc, char *argv[]) {
+ return totp_initialize();
+}
diff --git a/contrib/slapd-modules/passwd/totp/slapo-totp.5 b/contrib/slapd-modules/passwd/totp/slapo-totp.5
new file mode 100644
index 0000000..7c99bf1
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/slapo-totp.5
@@ -0,0 +1,109 @@
+.TH PW-TOTP 5 "2015/7/2" "PW-TOTP"
+.\" Copyright 2015-2022 The OpenLDAP Foundation.
+.\" Portions Copyright 2015 by Howard Chu, Symas Corp. All rights reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+pw-totp \- TOTP Password handling module
+.SH SYNOPSIS
+.B moduleload
+.I pw-totp.la
+.SH DESCRIPTION
+The
+.B pw-totp
+module allows time-based one-time password, AKA "authenticator-style",
+authentication to be added to applications that use LDAP for
+authentication. In most cases no changes to the applications are needed to switch
+to this type of authentication.
+
+With this module, the password needed for a user to authenticate is calculated
+based on the current time and a key that is stored in the user's LDAP entry. Since
+the password is based on the time, it changes periodically. Once used, it cannot be
+used again so keyloggers and shoulder-surfers are thwarted. A mobile
+phone application, such as the Google Authenticator (a 'prover'), can be used
+to calculate the user's current password, which is expressed as a six-digit
+number.
+Alternatively, the value can be calculated by some other application with access
+to the user's key and delivered to the user through SMS or some other channel.
+When prompted to authenticate, the user merely enters the six-digit code provided by
+the prover.
+
+Additionally, the overlay can also authenticate TOTP passwords
+combined with a static password. To do this, utilize one of the
+{TOTP1ANDPW}, {TOTP256ANDPW}, or {TOTP512ANDPW} password schemes
+and append the static password scheme value to the end of the
+userPassword attribute, separated by a pipe (|) character.
+
+This implementation complies with
+.B RFC 6238 TOTP Time-based One Time Passwords
+and includes support for the SHA-1, SHA-256, and SHA-512 HMAC
+algorithms.
+
+The HMAC key used in the TOTP computation is stored in the userPassword attribute
+of the user's LDAP entry and the LDAP Password Modify Extended Operation is used to
+set and change the value. The
+value should correspond to that used by the the prover (authenticator).
+
+.SH CONFIGURATION
+Once the module is loaded with the moduleload command from the synopsis,
+the {TOTP1}, {TOTP256}, {TOTP512}
+{TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
+password schemes will be recognized.
+
+On the databases where your users reside you must configure the
+totp overlay:
+
+.nf
+ database mdb
+ \...
+ overlay totp
+ \...
+.fi
+
+You can tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+.nf
+ password-hash {TOTP256}
+.fi
+
+.SH NOTES
+This module includes functionality implemented by the slapo-lastbind overlay
+and cannot coexist with it in the same database. Also note
+that since the time that the last bind occurred
+is needed to properly implement TOTP, provisions need to be made to propagate
+the authTimestamp attribute to other servers that are providing authentication
+services.
+
+The hash functions for the {TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
+schemes expect the secret to be entered in the form:
+<OTP seed><DELIM><static password>, where DELIM is currently defined
+as the pipe character (|).
+
+.SH BUGS
+The time step is hard-coded to thirty seconds. This should be OK for many use cases,
+but it would be nice if the value
+could be changed with a configuration keyword or in an attribute value.
+However, after one successful initial authentication (to verify
+the clocks on the server and the user's prover are in sync) the TOTP
+value of the previous time window may also be used to successfully
+authenticate, provided no successful bind has been performed already
+in the current or previous time window. This eliminates false
+negatives caused by user or network delays
+entering or transmitting the TOTP value.
+
+The authenticator code that is generated is hard-coded to a length of six digits.
+While in most cases
+this is probably better than the alternative length of four digits, there may be
+cases where a four-digit value is preferred.
+
+In cases where password-hash lists multiple mechanisms, the TOTP key will also
+be changed at the same time. This is likely to be undesirable behavior.
+
+.SH "SEE ALSO"
+.BR slapd.conf (5) ldappasswd (1)
+.SH ACKNOWLEDGEMENT
+This work was developed by Howard Chu of Symas Corporation for inclusion in
+OpenLDAP Software.
+
+Password + TOTP support added by Greg Veldman on behalf of SCinet.
diff --git a/contrib/slapd-modules/ppm/CHANGELOG.md b/contrib/slapd-modules/ppm/CHANGELOG.md
new file mode 100644
index 0000000..d0e4ed7
--- /dev/null
+++ b/contrib/slapd-modules/ppm/CHANGELOG.md
@@ -0,0 +1,38 @@
+# CHANGELOG
+
+* 2021-02-23 David Coutadeur <david.coutadeur@gmail.com>
+ remove maxLength attribute (#21)
+ adapt the readme and documentation of ppm (#22)
+ prepare ppolicy10 in OpenLDAP 2.5 (#20, #23 and #24)
+ add pwdCheckModuleArg feature
+ Version 2.0
+* 2019-08-20 David Coutadeur <david.coutadeur@gmail.com>
+ adding debug symbols for ppm_test,
+ improve tests with the possibility to add username,
+ fix openldap crash when checkRDN=1 and username contains too short parts
+ Version 1.8
+* 2018-03-30 David Coutadeur <david.coutadeur@gmail.com>
+ various minor improvements provided by Tim Bishop (tdb) (compilation, test program,
+ imprvts in Makefile: new OLDAP_SOURCES variable pointing to OLDAP install. directory
+ Version 1.7
+* 2017-05-19 David Coutadeur <david.coutadeur@gmail.com>
+ Adds cracklib support
+ Readme adaptations and cleaning
+ Version 1.6
+* 2017-02-07 David Coutadeur <david.coutadeur@gmail.com>
+ Adds maxConsecutivePerClass (idea from Trevor Vaughan / tvaughan@onyxpoint.com)
+ Version 1.5
+* 2016-08-22 David Coutadeur <david.coutadeur@gmail.com>
+ Get config file from environment variable
+ Version 1.4
+* 2014-12-20 Daly Chikhaoui <dchikhaoui@janua.fr>
+ Adding checkRDN parameter
+ Version 1.3
+* 2014-10-28 David Coutadeur <david.coutadeur@gmail.com>
+ Adding maxLength parameter
+ Version 1.2
+* 2014-07-27 David Coutadeur <david.coutadeur@gmail.com>
+ Changing the configuration file and the configuration data structure
+ Version 1.1
+* 2014-04-04 David Coutadeur <david.coutadeur@gmail.com>
+ Version 1.0
diff --git a/contrib/slapd-modules/ppm/CONTRIBUTIONS.md b/contrib/slapd-modules/ppm/CONTRIBUTIONS.md
new file mode 100644
index 0000000..0d563d8
--- /dev/null
+++ b/contrib/slapd-modules/ppm/CONTRIBUTIONS.md
@@ -0,0 +1,5 @@
+# CONTRIBUTIONS
+
+* 2014 - 2021 - David Coutadeur <david.coutadeur@gmail.com> - maintainer
+* 2015 - Daly Chikhaoui - Janua <dchikhaoui@janua.fr> - contribution on RDN checks
+* 2017 - tdb - Tim Bishop - contribution on some compilation improvements
diff --git a/contrib/slapd-modules/ppm/INSTALL.md b/contrib/slapd-modules/ppm/INSTALL.md
new file mode 100644
index 0000000..6052dc6
--- /dev/null
+++ b/contrib/slapd-modules/ppm/INSTALL.md
@@ -0,0 +1,51 @@
+INSTALLATION
+============
+
+Dependencies
+------------------
+ppm is provided along with OpenLDAP sources. By default, it is available into contrib/slapd-modules.
+ - make sure both OpenLDAP sources and ppm are available for building.
+ - install cracklib development files if you want to test passwords against cracklib
+ - install pandoc if you want to build the man page
+
+
+Build
+-----
+Enter contrib/slapd-modules/ppm directory
+
+You can optionally customize some variables if you don't want the default ones:
+- prefix: prefix of the path where ppm is to be installed (defaults to /usr/local)
+- ldap_subdir: OpenLDAP specific subdirectory for modules and configurations (defaults to openldap )
+- moduledir: where the ppm module is to be deployed (defaults to $prefix/$libexecdir/$ldap_subdir)
+- etcdir: used to compose default sysconfdir location (defaults to $prefix/etc)
+- sysconfdir: where the ppm example policy is to be deployed (defaults to $prefix/$etcdir/$ldap_subdir)
+- LDAP_SRC: path to OpenLDAP source directory
+- Options in OPTS variable:
+ CONFIG_FILE: (DEPRECATED) path to a ppm configuration file (see PPM_READ_FILE in ppm.h)
+ note: ppm configuration now lies into pwdCheckModuleArg password policy attribute
+ provided example file is only helpful as an example or for testing
+ CRACKLIB: if defined, link against cracklib
+ DEBUG: If defined, ppm logs its actions with syslog
+
+
+To build ppm, simply run these commands:
+(based upon the default prefix /usr/local of OpenLDAP)
+
+```
+make clean
+make
+make test
+make doc
+make install
+```
+
+Here is an illustrative example showing how to overload some options:
+
+```
+make clean
+make LDAP_SRC=../../.. prefix=/usr/local libdir=/usr/local/lib
+make test LDAP_SRC=../../..
+make doc prefix=/usr/local
+make install prefix=/usr/local libdir=/usr/local/lib
+```
+
diff --git a/contrib/slapd-modules/ppm/LICENSE b/contrib/slapd-modules/ppm/LICENSE
new file mode 100644
index 0000000..03f692b
--- /dev/null
+++ b/contrib/slapd-modules/ppm/LICENSE
@@ -0,0 +1,50 @@
+OpenLDAP Public License
+
+The OpenLDAP Public License
+ Version 2.8.1, 25 November 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+ and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+ statements and notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number. You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission. Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA. All rights reserved. Permission to copy and
+distribute verbatim copies of this document is granted.
+
diff --git a/contrib/slapd-modules/ppm/Makefile b/contrib/slapd-modules/ppm/Makefile
new file mode 100644
index 0000000..7b6efad
--- /dev/null
+++ b/contrib/slapd-modules/ppm/Makefile
@@ -0,0 +1,97 @@
+# $OpenLDAP$
+# Copyright 2014 David Coutadeur, Paris.
+# 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/liblber/liblber.la $(LDAP_BUILD)/libraries/libldap/libldap.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2 -fpic
+
+# To skip linking against CRACKLIB make CRACK=no
+CRACK=yes
+CRACKDEF_yes= -DCRACKLIB
+CRACKDEF_no=
+
+CRACKLIB_yes= -lcrack
+CRACKLIB_no=
+
+CRACKDEF=$(CRACKDEF_$(CRACK))
+CRACKLIB=$(CRACKLIB_$(CRACK))
+
+DEFS = -DDEBUG $(CRACKDEF)
+# Define if using a config file:
+# -DCONFIG_FILE="\"$(sysconfdir)/$(EXAMPLE)\""
+
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS=ppm.so
+LTVER = 0:0:0
+
+LDAP_LIBS = -L$(LDAP_BUILD)/libraries/liblber/.libs -L$(LDAP_BUILD)/libraries/libldap/.libs -lldap -llber
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+etcdir = $(exec_prefix)/etc
+sysconfdir = $(etcdir)$(ldap_subdir)
+
+TEST=ppm_test
+EXAMPLE=ppm.example
+TESTS=./unit_tests.sh
+
+MANDOC=slapm-ppm.5
+MDDOC=ppm.md
+
+all: ppm $(TEST)
+
+$(TEST): ppm
+ $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(LDFLAGS) $(INCS) $(LDAP_LIBS) -Wl,-rpath=. -o $(TEST) ppm_test.c $(PROGRAMS) $(LDAP_LIBS) $(CRACKLIB)
+
+ppm.o:
+ $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) -c $(INCS) ppm.c
+
+ppm: ppm.o
+ $(CC) $(LDFLAGS) $(INCS) -shared -o $(PROGRAMS) ppm.o $(CRACKLIB)
+
+install: ppm
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS); do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)/$(moduledir) ; \
+ done
+ $(INSTALL) -m 644 $(EXAMPLE) $(DESTDIR)$(sysconfdir)/
+ $(INSTALL) -m 644 $(MANDOC) $(DESTDIR)$(man5dir)/
+# $(INSTALL) -m 755 $(TEST) $(libdir)
+
+.PHONY: clean
+
+clean:
+ $(RM) -f ppm.o $(PROGRAMS) ppm.lo $(TEST)
+ $(RM) -rf .libs
+
+test: ppm $(TEST)
+ LDAP_SRC=$(LDAP_SRC) $(TESTS)
+
+doc:
+ pandoc $(MDDOC) -s -t man -o $(MANDOC)
+ sed -i -e 's#ETCDIR#$(DESTDIR)$(sysconfdir)#g' $(MANDOC)
+
diff --git a/contrib/slapd-modules/ppm/README.md b/contrib/slapd-modules/ppm/README.md
new file mode 100644
index 0000000..129f788
--- /dev/null
+++ b/contrib/slapd-modules/ppm/README.md
@@ -0,0 +1 @@
+See ppm.md manual and INSTALL.md
diff --git a/contrib/slapd-modules/ppm/ppm.c b/contrib/slapd-modules/ppm/ppm.c
new file mode 100644
index 0000000..801ab6c
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.c
@@ -0,0 +1,684 @@
+/*
+ * ppm.c for OpenLDAP
+ *
+ * See LICENSE, README and INSTALL files
+ */
+
+
+/*
+ password policy module is called with:
+ int check_password (char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
+
+ *pPasswd: new password
+ **ppErrStr: pointer to the string containing the error message
+ *e: pointer to the current user entry
+ *pArg: pointer to a struct berval holding the value of pwdCheckModuleArg attr
+*/
+
+#include <stdlib.h> // for type conversion, such as atoi...
+#include <regex.h> // for matching allowedParameters / conf file
+#include <string.h>
+#include <ctype.h>
+#include <portable.h>
+#include <slap.h>
+#include <stdarg.h> // for variable nb of arguments functions
+#include "ppm.h"
+
+#ifdef CRACKLIB
+#include "crack.h" // use cracklib to check password
+#endif
+
+void
+ppm_log(int priority, const char *format, ...)
+{
+ // if DEBUG flag is set
+ // logs into syslog (for OpenLDAP) or to stdout (for tests)
+#if defined(DEBUG)
+ if(ppm_test != 1)
+ {
+ va_list syslog_args;
+ va_start(syslog_args, format);
+ vsyslog(priority, format, syslog_args);
+ va_end(syslog_args);
+ }
+ else
+ {
+ va_list stdout_args;
+ va_start(stdout_args, format);
+ vprintf(format, stdout_args);
+ printf("\n");
+ fflush(stdout);
+ va_end(stdout_args);
+ }
+#endif
+}
+
+void
+strcpy_safe(char *dest, char *src, int length_dest)
+{
+ if(src == NULL)
+ {
+ dest[0] = '\0';
+ }
+ else
+ {
+ int length_src = strlen(src);
+ int n = (length_dest < length_src) ? length_dest : length_src;
+ // Copy the string — don’t copy too many bytes.
+ strncpy(dest, src, n);
+ // Ensure null-termination.
+ dest[n] = '\0';
+ }
+}
+
+genValue*
+getValue(conf *fileConf, int numParam, char* param)
+{
+ int i = 0;
+
+ // First scan parameters
+ for (i = 0; i < numParam; i++) {
+ if ((strlen(param) == strlen(fileConf[i].param))
+ && (strncmp(param, fileConf[i].param, strlen(fileConf[i].param))
+ == 0)) {
+ return &(fileConf[i].value);
+ }
+ }
+ return NULL;
+}
+
+int maxConsPerClass(char *password, char *charClass)
+{
+ // find maximum number of consecutive class characters in the password
+
+ int bestMax = 0;
+ int max = 0;
+ int i;
+
+ for(i=0 ; i<strlen(password) ; i++)
+ {
+ if(strchr(charClass,password[i]) != NULL)
+ {
+ // current character is in class
+ max++;
+ // is the new max a better candidate to maxConsecutivePerClass?
+ if(max > bestMax)
+ {
+ // found a better maxConsecutivePerClass
+ bestMax = max;
+ }
+ }
+ else
+ {
+ // current character is not in class
+ // reinitialize max
+ max=0;
+ }
+ }
+ return bestMax;
+}
+
+void
+storeEntry(char *param, char *value, valueType valType,
+ char *min, char *minForPoint, conf * fileConf, int *numParam)
+{
+ int i = 0;
+ int iMin;
+ int iMinForPoint;
+ if (min == NULL || strcmp(min,"") == 0)
+ iMin = 0;
+ else
+ iMin = atoi(min);
+
+ if (minForPoint == NULL || strcmp(minForPoint,"") == 0)
+ iMinForPoint = 0;
+ else
+ iMinForPoint = atoi(minForPoint);
+
+ // First scan parameters
+ for (i = 0; i < *numParam; i++) {
+ if ((strlen(param) == strlen(fileConf[i].param))
+ && (strncmp(param, fileConf[i].param, strlen(fileConf[i].param))
+ == 0)) {
+ // entry found, replace values
+ if(valType == typeInt)
+ fileConf[i].value.iVal = atoi(value);
+ else
+ strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
+ fileConf[i].min = iMin;
+ fileConf[i].minForPoint = iMinForPoint;
+ if(valType == typeInt)
+ ppm_log(LOG_NOTICE, "ppm: Accepted replaced value: %d",
+ fileConf[i].value.iVal);
+ else
+ ppm_log(LOG_NOTICE, "ppm: Accepted replaced value: %s",
+ fileConf[i].value.sVal);
+ return;
+ }
+ }
+ // entry not found, add values
+ strcpy_safe(fileConf[*numParam].param, param, PARAM_MAX_LEN);
+ fileConf[*numParam].iType = valType;
+ if(valType == typeInt)
+ fileConf[i].value.iVal = atoi(value);
+ else
+ strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
+ fileConf[*numParam].min = iMin;
+ fileConf[*numParam].minForPoint = iMinForPoint;
+ ++(*numParam);
+ if(valType == typeInt)
+ ppm_log(LOG_NOTICE, "ppm: Accepted new value: %d",
+ fileConf[*numParam].value.iVal);
+ else
+ ppm_log(LOG_NOTICE, "ppm: Accepted new value: %s",
+ fileConf[*numParam].value.sVal);
+}
+
+int
+typeParam(char* param)
+{
+ int i;
+ int n = sizeof(allowedParameters)/sizeof(params);
+
+ regex_t regex;
+ int reti;
+
+ for(i = 0 ; i < n ; i++ )
+ {
+ // Compile regular expression
+ reti = regcomp(&regex, allowedParameters[i].param, 0);
+ if (reti) {
+ ppm_log(LOG_ERR, "ppm: Cannot compile regex: %s",
+ allowedParameters[i].param);
+ return n;
+ }
+
+ // Execute regular expression
+ reti = regexec(&regex, param, 0, NULL, 0);
+ if (!reti)
+ {
+ regfree(&regex);
+ return i;
+ }
+ regfree(&regex);
+ }
+ return n;
+}
+
+#ifndef PPM_READ_FILE
+
+ /*
+ * read configuration into pwdCheckModuleArg attribute
+ * */
+ static void
+ read_config_attr(conf * fileConf, int *numParam, char *ppm_config_attr)
+ {
+ int nParam = 0; // position of found parameter in allowedParameters
+ int sAllowedParameters = sizeof(allowedParameters)/sizeof(params);
+ char arg[260*256];
+ char *token;
+ char *saveptr1;
+ char *saveptr2;
+
+ strcpy_safe(arg, ppm_config_attr, 260*256);
+ ppm_log(LOG_NOTICE, "ppm: Parsing pwdCheckModuleArg attribute");
+ token = strtok_r(arg, "\n", &saveptr1);
+
+ while (token != NULL) {
+ ppm_log(LOG_NOTICE, "ppm: get line: %s",token);
+ char *start = token;
+ char *word, *value;
+ char *min, *minForPoint;;
+
+ while (isspace(*start) && isascii(*start))
+ start++;
+
+ if (!isascii(*start))
+ {
+ token = strtok_r(NULL, "\n", &saveptr1);
+ continue;
+ }
+ if (start[0] == '#')
+ {
+ token = strtok_r(NULL, "\n", &saveptr1);
+ continue;
+ }
+
+ if ((word = strtok_r(start, " \t", &saveptr2))) {
+ if ((value = strtok_r(NULL, " \t", &saveptr2)) == NULL)
+ {
+ saveptr2 = NULL;
+ ppm_log(LOG_NOTICE, "ppm: No value, goto next parameter");
+ token = strtok_r(NULL, "\n", &saveptr1);
+ continue;
+ }
+ if (strchr(value, '\n') != NULL)
+ strchr(value, '\n')[0] = '\0';
+ min = strtok_r(NULL, " \t", &saveptr2);
+ if (min != NULL)
+ if (strchr(min, '\n') != NULL)
+ strchr(min, '\n')[0] = '\0';
+ minForPoint = strtok_r(NULL, " \t", &saveptr2);
+ if (minForPoint != NULL)
+ if (strchr(minForPoint, '\n') != NULL)
+ strchr(minForPoint, '\n')[0] = '\0';
+
+
+ nParam = typeParam(word); // search for param in allowedParameters
+ if (nParam != sAllowedParameters) // param has been found
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
+ word, value, min, minForPoint);
+
+ storeEntry(word, value, allowedParameters[nParam].iType,
+ min, minForPoint, fileConf, numParam);
+ }
+ else
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Parameter '%s' rejected", word);
+ }
+
+ }
+ token = strtok_r(NULL, "\n", &saveptr1);
+ }
+
+ }
+
+#endif
+
+#ifdef PPM_READ_FILE
+
+ /*
+ * read configuration file (DEPRECATED)
+ * */
+ static void
+ read_config_file(conf * fileConf, int *numParam, char *ppm_config_file)
+ {
+ FILE *config;
+ char line[260] = "";
+ int nParam = 0; // position of found parameter in allowedParameters
+ int sAllowedParameters = sizeof(allowedParameters)/sizeof(params);
+
+ ppm_log(LOG_NOTICE, "ppm: Opening file %s", ppm_config_file);
+ if ((config = fopen(ppm_config_file, "r")) == NULL) {
+ ppm_log(LOG_ERR, "ppm: Opening file %s failed", ppm_config_file);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(line, 256, config) != NULL) {
+ char *start = line;
+ char *word, *value;
+ char *min, *minForPoint;;
+
+ while (isspace(*start) && isascii(*start))
+ start++;
+
+ if (!isascii(*start))
+ continue;
+ if (start[0] == '#')
+ continue;
+
+ if ((word = strtok(start, " \t"))) {
+ if ((value = strtok(NULL, " \t")) == NULL)
+ continue;
+ if (strchr(value, '\n') != NULL)
+ strchr(value, '\n')[0] = '\0';
+ min = strtok(NULL, " \t");
+ if (min != NULL)
+ if (strchr(min, '\n') != NULL)
+ strchr(min, '\n')[0] = '\0';
+ minForPoint = strtok(NULL, " \t");
+ if (minForPoint != NULL)
+ if (strchr(minForPoint, '\n') != NULL)
+ strchr(minForPoint, '\n')[0] = '\0';
+
+
+ nParam = typeParam(word); // search for param in allowedParameters
+ if (nParam != sAllowedParameters) // param has been found
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
+ word, value, min, minForPoint);
+
+ storeEntry(word, value, allowedParameters[nParam].iType,
+ min, minForPoint, fileConf, numParam);
+ }
+ else
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Parameter '%s' rejected", word);
+ }
+
+ }
+ }
+
+ fclose(config);
+ }
+
+#endif
+
+static int
+realloc_error_message(char **target, int curlen, int nextlen)
+{
+ if (curlen < nextlen + MEMORY_MARGIN) {
+ ppm_log(LOG_WARNING,
+ "ppm: Reallocating szErrStr from %d to %d", curlen,
+ nextlen + MEMORY_MARGIN);
+ ber_memfree(*target);
+ curlen = nextlen + MEMORY_MARGIN;
+ *target = (char *) ber_memalloc(curlen);
+ }
+
+ return curlen;
+}
+
+// Does the password contains a token from the RDN ?
+int
+containsRDN(char* passwd, char* DN)
+{
+ char lDN[DN_MAX_LEN];
+ char * tmpToken;
+ char * token;
+ regex_t regex;
+ int reti;
+
+ strcpy_safe(lDN, DN, DN_MAX_LEN);
+
+ // Extract the RDN from the DN
+ tmpToken = strtok(lDN, ",+");
+ tmpToken = strtok(tmpToken, "=");
+ tmpToken = strtok(NULL, "=");
+
+ // Search for each token in the password */
+ token = strtok(tmpToken, TOKENS_DELIMITERS);
+
+ while (token != NULL)
+ {
+ if (strlen(token) > 2)
+ {
+ ppm_log(LOG_NOTICE, "ppm: Checking if %s part of RDN matches the password", token);
+ // Compile regular expression
+ reti = regcomp(&regex, token, REG_ICASE);
+ if (reti) {
+ ppm_log(LOG_ERR, "ppm: Cannot compile regex: %s", token);
+ return 0;
+ }
+
+ // Execute regular expression
+ reti = regexec(&regex, passwd, 0, NULL, 0);
+ if (!reti)
+ {
+ regfree(&regex);
+ return 1;
+ }
+
+ regfree(&regex);
+ }
+ else
+ {
+ ppm_log(LOG_NOTICE, "ppm: %s part of RDN is too short to be checked", token);
+ }
+ token = strtok(NULL, TOKENS_DELIMITERS);
+ }
+
+ return 0;
+}
+
+
+int
+check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
+{
+
+ Entry *pEntry = e;
+ struct berval *pwdCheckModuleArg = pArg;
+ char *szErrStr = (char *) ber_memalloc(MEM_INIT_SZ);
+ int mem_len = MEM_INIT_SZ;
+ int numParam = 0; // Number of params in current configuration
+
+ int useCracklib;
+ char cracklibDict[VALUE_MAX_LEN];
+ char cracklibDictFiles[3][(VALUE_MAX_LEN+5)];
+ char const* cracklibExt[] = { ".hwm", ".pwd", ".pwi" };
+ FILE* fd;
+ char* res;
+ int minQuality;
+ int checkRDN;
+ char forbiddenChars[VALUE_MAX_LEN];
+ int nForbiddenChars = 0;
+ int nQuality = 0;
+ int maxConsecutivePerClass;
+ int nbInClass[CONF_MAX_SIZE];
+ int i,j;
+
+ ppm_log(LOG_NOTICE, "ppm: entry %s", pEntry->e_nname.bv_val);
+
+#ifdef PPM_READ_FILE
+ /* Determine if config file is to be read (DEPRECATED) */
+ char ppm_config_file[FILENAME_MAX_LEN];
+
+ ppm_log(LOG_NOTICE, "ppm: Not reading pwdCheckModuleArg attribute");
+ ppm_log(LOG_NOTICE, "ppm: instead, read configuration file (deprecated)");
+
+ strcpy_safe(ppm_config_file, getenv("PPM_CONFIG_FILE"), FILENAME_MAX_LEN);
+ if (ppm_config_file[0] == '\0') {
+ strcpy_safe(ppm_config_file, CONFIG_FILE, FILENAME_MAX_LEN);
+ }
+ ppm_log(LOG_NOTICE, "ppm: reading config file from %s", ppm_config_file);
+#else
+ if ( !pwdCheckModuleArg || !pwdCheckModuleArg->bv_val ) {
+ ppm_log(LOG_ERR, "ppm: No config provided in pwdCheckModuleArg");
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(GENERIC_ERROR));
+ sprintf(szErrStr, GENERIC_ERROR);
+ goto fail;
+ }
+
+ ppm_log(LOG_NOTICE, "ppm: Reading pwdCheckModuleArg attribute");
+ ppm_log(LOG_NOTICE, "ppm: RAW configuration: %s", pwdCheckModuleArg->bv_val);
+#endif
+
+ for (i = 0; i < CONF_MAX_SIZE; i++)
+ nbInClass[i] = 0;
+
+ /* Set default values */
+ conf fileConf[CONF_MAX_SIZE] = {
+ {"minQuality", typeInt, {.iVal = DEFAULT_QUALITY}, 0, 0
+ }
+ ,
+ {"checkRDN", typeInt, {.iVal = 0}, 0, 0
+ }
+ ,
+ {"forbiddenChars", typeStr, {.sVal = ""}, 0, 0
+ }
+ ,
+ {"maxConsecutivePerClass", typeInt, {.iVal = 0}, 0, 0
+ }
+ ,
+ {"useCracklib", typeInt, {.iVal = 0}, 0, 0
+ }
+ ,
+ {"cracklibDict", typeStr, {.sVal = "/var/cache/cracklib/cracklib_dict"}, 0, 0
+ }
+ ,
+ {"class-upperCase", typeStr, {.sVal = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 0, 1
+ }
+ ,
+ {"class-lowerCase", typeStr, {.sVal = "abcdefghijklmnopqrstuvwxyz"}, 0, 1
+ }
+ ,
+ {"class-digit", typeStr, {.sVal = "0123456789"}, 0, 1
+ }
+ ,
+ {"class-special", typeStr,
+ {.sVal = "<>,?;.:/!§ù%*µ^¨$£²&é~\"#'{([-|è`_\\ç^à@)]°=}+"}, 0, 1
+ }
+ };
+ numParam = 10;
+
+ #ifdef PPM_READ_FILE
+ /* Read configuration file (DEPRECATED) */
+ read_config_file(fileConf, &numParam, ppm_config_file);
+ #else
+ /* Read configuration attribute (pwdCheckModuleArg) */
+ read_config_attr(fileConf, &numParam, (*(struct berval*)pwdCheckModuleArg).bv_val);
+ #endif
+
+ minQuality = getValue(fileConf, numParam, "minQuality")->iVal;
+ checkRDN = getValue(fileConf, numParam, "checkRDN")->iVal;
+ strcpy_safe(forbiddenChars,
+ getValue(fileConf, numParam, "forbiddenChars")->sVal,
+ VALUE_MAX_LEN);
+ maxConsecutivePerClass = getValue(fileConf, numParam, "maxConsecutivePerClass")->iVal;
+ useCracklib = getValue(fileConf, numParam, "useCracklib")->iVal;
+ strcpy_safe(cracklibDict,
+ getValue(fileConf, numParam, "cracklibDict")->sVal,
+ VALUE_MAX_LEN);
+
+
+ /*The password must have at least minQuality strength points with one
+ * point granted if the password contains at least minForPoint characters for each class
+ * It must contains at least min chars of each class
+ * It must not contain any char in forbiddenChar */
+
+ for (i = 0; i < strlen(pPasswd); i++) {
+
+ int n;
+ for (n = 0; n < numParam; n++) {
+ if (strstr(fileConf[n].param, "class-") != NULL) {
+ if (strchr(fileConf[n].value.sVal, pPasswd[i]) != NULL) {
+ ++(nbInClass[n]);
+ }
+ }
+ }
+ if (strchr(forbiddenChars, pPasswd[i]) != NULL) {
+ nForbiddenChars++;
+ }
+ }
+
+ // Password checking done, now loocking for minForPoint criteria
+ for (i = 0; i < CONF_MAX_SIZE; i++) {
+ if (strstr(fileConf[i].param, "class-") != NULL) {
+ if ((nbInClass[i] >= fileConf[i].minForPoint)
+ && strlen(fileConf[i].value.sVal) != 0) {
+ // 1 point granted
+ ++nQuality;
+ ppm_log(LOG_NOTICE, "ppm: 1 point granted for class %s",
+ fileConf[i].param);
+ }
+ }
+ }
+
+ if (nQuality < minQuality) {
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_QUALITY_SZ) +
+ strlen(pEntry->e_nname.bv_val) + 4);
+ sprintf(szErrStr, PASSWORD_QUALITY_SZ, pEntry->e_nname.bv_val,
+ nQuality, minQuality);
+ goto fail;
+ }
+ // Password checking done, now loocking for constraintClass criteria
+ for (i = 0; i < CONF_MAX_SIZE; i++) {
+ if (strstr(fileConf[i].param, "class-") != NULL) {
+ if ((nbInClass[i] < fileConf[i].min) &&
+ strlen(fileConf[i].value.sVal) != 0) {
+ // constraint is not satisfied... goto fail
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_CRITERIA) +
+ strlen(pEntry->e_nname.bv_val) +
+ 2 + PARAM_MAX_LEN);
+ sprintf(szErrStr, PASSWORD_CRITERIA, pEntry->e_nname.bv_val,
+ fileConf[i].min, fileConf[i].param);
+ goto fail;
+ }
+ }
+ }
+
+ // Password checking done, now loocking for forbiddenChars criteria
+ if (nForbiddenChars > 0) { // at least 1 forbidden char... goto fail
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_FORBIDDENCHARS) +
+ strlen(pEntry->e_nname.bv_val) + 2 +
+ VALUE_MAX_LEN);
+ sprintf(szErrStr, PASSWORD_FORBIDDENCHARS, pEntry->e_nname.bv_val,
+ nForbiddenChars, forbiddenChars);
+ goto fail;
+ }
+
+ // Password checking done, now loocking for maxConsecutivePerClass criteria
+ for (i = 0; i < CONF_MAX_SIZE; i++) {
+ if (strstr(fileConf[i].param, "class-") != NULL) {
+ if ( maxConsecutivePerClass != 0 &&
+ (maxConsPerClass(pPasswd,fileConf[i].value.sVal)
+ > maxConsecutivePerClass)) {
+ // Too much consecutive characters of the same class
+ ppm_log(LOG_NOTICE, "ppm: Too much consecutive chars for class %s",
+ fileConf[i].param);
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_MAXCONSECUTIVEPERCLASS) +
+ strlen(pEntry->e_nname.bv_val) + 2 +
+ PARAM_MAX_LEN);
+ sprintf(szErrStr, PASSWORD_MAXCONSECUTIVEPERCLASS, pEntry->e_nname.bv_val,
+ maxConsecutivePerClass, fileConf[i].param);
+ goto fail;
+ }
+ }
+ }
+#ifdef CRACKLIB
+ // Password checking done, now loocking for cracklib criteria
+ if ( useCracklib > 0 ) {
+
+ for( j = 0 ; j < 3 ; j++) {
+ strcpy_safe(cracklibDictFiles[j], cracklibDict, VALUE_MAX_LEN);
+ strcat(cracklibDictFiles[j], cracklibExt[j]);
+ if (( fd = fopen ( cracklibDictFiles[j], "r")) == NULL ) {
+ ppm_log(LOG_NOTICE, "ppm: Error while reading %s file",
+ cracklibDictFiles[j]);
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(GENERIC_ERROR));
+ sprintf(szErrStr, GENERIC_ERROR);
+ goto fail;
+
+ }
+ else {
+ fclose (fd);
+ }
+ }
+ res = (char *) FascistCheck (pPasswd, cracklibDict);
+ if ( res != NULL ) {
+ ppm_log(LOG_NOTICE, "ppm: cracklib does not validate password for entry %s",
+ pEntry->e_nname.bv_val);
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_CRACKLIB) +
+ strlen(pEntry->e_nname.bv_val));
+ sprintf(szErrStr, PASSWORD_CRACKLIB, pEntry->e_nname.bv_val);
+ goto fail;
+
+ }
+
+ }
+#endif
+
+ // Password checking done, now looking for checkRDN criteria
+ if (checkRDN == 1 && containsRDN(pPasswd, pEntry->e_nname.bv_val))
+ // RDN check enabled and a token from RDN is found in password: goto fail
+ {
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(RDN_TOKEN_FOUND) +
+ strlen(pEntry->e_nname.bv_val));
+ sprintf(szErrStr, RDN_TOKEN_FOUND, pEntry->e_nname.bv_val);
+
+ goto fail;
+ }
+
+ *ppErrStr = strdup("");
+ ber_memfree(szErrStr);
+ return (LDAP_SUCCESS);
+
+ fail:
+ *ppErrStr = strdup(szErrStr);
+ ber_memfree(szErrStr);
+ return (EXIT_FAILURE);
+
+}
diff --git a/contrib/slapd-modules/ppm/ppm.example b/contrib/slapd-modules/ppm/ppm.example
new file mode 100644
index 0000000..10cf132
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.example
@@ -0,0 +1,85 @@
+# Example of ppm configuration
+
+# Such configuration must be stored into pwdCheckModuleArg attribute
+# of a password policy entry
+# See slapo-ppolicy for more details
+# Here is an example of such password policy:
+# dn: cn=default,ou=policies,dc=my-domain,dc=com
+# objectClass: pwdPolicy
+# objectClass: top
+# objectClass: pwdPolicyChecker
+# objectClass: person
+# pwdCheckQuality: 2
+# pwdAttribute: userPassword
+# sn: default
+# cn: default
+# pwdMinLength: 6
+# pwdCheckModule: /usr/local/lib/ppm.so
+# pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+#
+# Different parameters are separated by a linefeed (\n)
+# Parameters starting with a # are ignored
+# Use a base64 tool to code / decode the content of pwdCheckModuleArg
+
+
+
+# Parameters
+
+# minQuality parameter
+# Format:
+# minQuality [NUMBER]
+# Description:
+# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+# defines the minimum point numbers for the password to be accepted.
+minQuality 3
+
+# checkRDN parameter
+# Format:
+# checkRDN [0 | 1]
+# Description:
+# If set to 1, password must not contain a token from the RDN.
+# Tokens are separated by these delimiters : space tabulation _ - , ; £
+checkRDN 0
+
+# forbiddenChars parameter
+# Format:
+# forbiddenChars [CHARACTERS_FORBIDDEN]
+# Description:
+# Defines the forbidden characters list (no separator).
+# If one of them is found in the password, then it is rejected.
+forbiddenChars
+
+# maxConsecutivePerClass parameter
+# Format:
+# maxConsecutivePerClass [NUMBER]
+# Description:
+# Defines the maximum number of consecutive character allowed for any class
+maxConsecutivePerClass 0
+
+# useCracklib parameter
+# Format:
+# useCracklib [0 | 1]
+# Description:
+# If set to 1, the password must pass the cracklib check
+useCracklib 0
+
+# cracklibDict parameter
+# Format:
+# cracklibDict [path_to_cracklib_dictionary]
+# Description:
+# directory+filename-prefix that your version of CrackLib will go hunting for
+# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+cracklibDict /var/cache/cracklib/cracklib_dict
+
+# classes parameter
+# Format:
+# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+# Description:
+# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
diff --git a/contrib/slapd-modules/ppm/ppm.h b/contrib/slapd-modules/ppm/ppm.h
new file mode 100644
index 0000000..25b360d
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.h
@@ -0,0 +1,125 @@
+/*
+ * ppm.h for OpenLDAP
+ *
+ * See LICENSE, README and INSTALL files
+ */
+
+#ifndef PPM_H_
+#define PPM_H_
+
+#include <stdlib.h> // for type conversion, such as atoi...
+#include <regex.h> // for matching allowedParameters / conf file
+#include <string.h>
+#include <ctype.h>
+#include <portable.h>
+#include <slap.h>
+
+#if defined(DEBUG)
+#include <syslog.h>
+#endif
+
+//#define PPM_READ_FILE 1 // old deprecated configuration mode
+ // 1: (deprecated) don't read pwdCheckModuleArg
+ // attribute, instead read config file
+ // 0: read pwdCheckModuleArg attribute
+
+/* config file parameters (DEPRECATED) */
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/openldap/ppm.example"
+#endif
+#define FILENAME_MAX_LEN 512
+
+#define DEFAULT_QUALITY 3
+#define MEMORY_MARGIN 50
+#define MEM_INIT_SZ 64
+#define DN_MAX_LEN 512
+
+#define CONF_MAX_SIZE 50
+#define PARAM_MAX_LEN 32
+#define VALUE_MAX_LEN 128
+#define ATTR_NAME_MAX_LEN 150
+
+#define PARAM_PREFIX_CLASS "class-"
+#define TOKENS_DELIMITERS " ,;-_£\t"
+
+
+#define DEBUG_MSG_MAX_LEN 256
+
+#define PASSWORD_QUALITY_SZ \
+ "Password for dn=\"%s\" does not pass required number of strength checks (%d of %d)"
+#define PASSWORD_CRITERIA \
+ "Password for dn=\"%s\" has not reached the minimum number of characters (%d) for class %s"
+#define PASSWORD_MAXCONSECUTIVEPERCLASS \
+ "Password for dn=\"%s\" has reached the maximum number of characters (%d) for class %s"
+#define PASSWORD_FORBIDDENCHARS \
+ "Password for dn=\"%s\" contains %d forbidden characters in %s"
+#define RDN_TOKEN_FOUND \
+ "Password for dn=\"%s\" contains tokens from the RDN"
+#define GENERIC_ERROR \
+ "Error while checking password"
+#define PASSWORD_CRACKLIB \
+ "Password for dn=\"%s\" is too weak"
+#define BAD_PASSWORD_SZ \
+ "Bad password for dn=\"%s\" because %s"
+
+
+
+typedef union genValue {
+ int iVal;
+ char sVal[VALUE_MAX_LEN];
+} genValue;
+
+typedef enum {
+ typeInt,
+ typeStr
+} valueType;
+
+typedef struct params {
+ char param[PARAM_MAX_LEN];
+ valueType iType;
+} params;
+
+// allowed parameters loaded into configuration structure
+// it also contains the type of the corresponding value
+params allowedParameters[7] = {
+ {"^minQuality", typeInt},
+ {"^checkRDN", typeInt},
+ {"^forbiddenChars", typeStr},
+ {"^maxConsecutivePerClass", typeInt},
+ {"^useCracklib", typeInt},
+ {"^cracklibDict", typeStr},
+ {"^class-.*", typeStr}
+};
+
+
+// configuration structure, containing a parameter, a value,
+// a corresponding min and minForPoint indicators if necessary
+// and a type for the value (typeInt or typeStr)
+typedef struct conf {
+ char param[PARAM_MAX_LEN];
+ valueType iType;
+ genValue value;
+ int min;
+ int minForPoint;
+} conf;
+
+void ppm_log(int priority, const char *format, ...);
+int min(char *str1, char *str2);
+#ifndef PPM_READ_FILE
+ static void read_config_attr(conf * fileConf, int *numParam, char *ppm_config_attr);
+#endif
+#ifdef PPM_READ_FILE
+ static void read_config_file(conf * fileConf, int *numParam, char *ppm_config_file);
+#endif
+int check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg);
+int maxConsPerClass(char *password, char *charClass);
+void storeEntry(char *param, char *value, valueType valType,
+ char *min, char *minForPoint, conf * fileConf, int *numParam);
+int typeParam(char* param);
+genValue* getValue(conf *fileConf, int numParam, char* param);
+void strcpy_safe(char *dest, char *src, int length_dest);
+
+
+int ppm_test = 0;
+
+#endif
diff --git a/contrib/slapd-modules/ppm/ppm.md b/contrib/slapd-modules/ppm/ppm.md
new file mode 100644
index 0000000..5b1accb
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.md
@@ -0,0 +1,343 @@
+---
+title: ppm
+section: 5
+header: File Formats Manual
+footer: ppm
+date: August 24, 2021
+---
+
+# NAME
+
+ppm (Password Policy Module) - extension of the password policy overlay
+
+# SYNOPSIS
+
+ETCDIR/ppm.example
+
+# DESCRIPTION
+
+**ppm** is an OpenLDAP module for checking password quality when they are modified.
+Passwords are checked against the presence or absence of certain character classes.
+
+This module is used as an extension of the OpenLDAP password policy controls,
+see slapo-ppolicy(5) section **pwdCheckModule**.
+
+
+# USAGE
+
+Create a password policy entry and indicate the path of the ppm.so library
+and the content of the desired policy.
+Use a base64 tool to code / decode the content of the policy stored into
+**pwdCheckModuleArg**. Here is an example:
+
+```
+dn: cn=default,ou=policies,dc=my-domain,dc=com
+objectClass: pwdPolicy
+objectClass: top
+objectClass: pwdPolicyChecker
+objectClass: person
+pwdCheckQuality: 2
+pwdAttribute: userPassword
+sn: default
+cn: default
+pwdMinLength: 6
+pwdCheckModule: /usr/local/lib/ppm.so
+pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+```
+
+
+See **slapo-ppolicy** for more information, but to sum up:
+
+- enable ppolicy overlay in your database.
+- define a default password policy in OpenLDAP configuration or use pwdPolicySubentry attribute to point to the given policy.
+
+This example show the activation for a **slapd.conf** file
+(see **slapd-config** and **slapo-ppolicy** for more information for
+ **cn=config** configuration)
+
+```
+overlay ppolicy
+ppolicy_default "cn=default,ou=policies,dc=my-domain,dc=com"
+#ppolicy_use_lockout # for having more infos about the lockout
+```
+
+
+# FEATURES
+
+Here are the main features:
+
+- 4 character classes are defined by default:
+upper case, lower case, digits and special characters.
+
+- more character classes can be defined, just write your own.
+
+- passwords must match the amount of quality points.
+A point is validated when at least m characters of the corresponding
+character class are present in the password.
+
+- passwords must have at least n of the corresponding character class
+present, else they are rejected.
+
+- the two previous criteria are checked against any specific character class
+defined.
+
+- if a password contains any of the forbidden characters, then it is
+rejected.
+
+- if a password contains tokens from the RDN, then it is rejected.
+
+- if a password does not pass cracklib check, then it is rejected.
+
+
+# CONFIGURATION
+
+Since OpenLDAP 2.5 version, ppm configuration is held in a binary
+attribute of the password policy: **pwdCheckModuleArg**
+
+The example file (**ETCDIR/ppm.example** by default) is to be
+considered as an example configuration, to import in the **pwdCheckModuleArg**
+attribute. It is also used for testing passwords with the test program
+provided.
+
+If for some reasons, any parameter is not found, it will be given its
+default value.
+
+Note: you can still compile ppm to use the configuration file, by enabling
+**PPM_READ_FILE** in **ppm.h** (but this is deprecated now). If you decide to do so,
+you can use the **PPM_CONFIG_FILE** environment variable for overloading the
+configuration file path.
+
+The syntax of a configuration line is:
+
+```
+parameter value [min] [minForPoint]
+```
+
+with spaces being delimiters and Line Feed (LF) ending the line.
+
+Parameter names **are** case sensitive.
+
+Lines beginning by a **#** are considered as comments.
+
+The default configuration is the following:
+
+```
+# minQuality parameter
+# Format:
+# minQuality [NUMBER]
+# Description:
+# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+# defines the minimum point numbers for the password to be accepted.
+minQuality 3
+
+# checkRDN parameter
+# Format:
+# checkRDN [0 | 1]
+# Description:
+# If set to 1, password must not contain a token from the RDN.
+# Tokens are separated by the following delimiters : space tabulation _ - , ; £
+checkRDN 0
+
+# forbiddenChars parameter
+# Format:
+# forbiddenChars [CHARACTERS_FORBIDDEN]
+# Description:
+# Defines the forbidden characters list (no separator).
+# If one of them is found in the password, then it is rejected.
+forbiddenChars
+
+# maxConsecutivePerClass parameter
+# Format:
+# maxConsecutivePerClass [NUMBER]
+# Description:
+# Defines the maximum number of consecutive character allowed for any class
+maxConsecutivePerClass 0
+
+# useCracklib parameter
+# Format:
+# useCracklib [0 | 1]
+# Description:
+# If set to 1, the password must pass the cracklib check
+useCracklib 0
+
+# cracklibDict parameter
+# Format:
+# cracklibDict [path_to_cracklib_dictionary]
+# Description:
+# directory+filename-prefix that your version of CrackLib will go hunting for
+# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+cracklibDict /var/cache/cracklib/cracklib_dict
+
+# classes parameter
+# Format:
+# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+# Description:
+# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+```
+
+# EXAMPLE
+
+With this policy:
+```
+minQuality 4
+forbiddenChars .?,
+checkRDN 1
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 5
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 12
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+class-myClass :) 1 1``
+```
+
+the password **ThereIsNoCowLevel)** is working, because:
+
+- it has 4 character classes validated : upper, lower, special, and myClass
+- it has no character among .?,
+- it has at least one character among : or )
+
+but it won't work for the user uid=John Cowlevel,ou=people,cn=example,cn=com,
+because the token "Cowlevel" from his RDN exists in the password (case insensitive).
+
+
+# LOGS
+
+If a user password is rejected by **ppm**, the user will get this type of message:
+
+Typical user message from ldappasswd(5):
+
+```
+ Result: Constraint violation (19)
+ Additional info: Password for dn=\"%s\" does not pass required number of strength checks (2 of 3)
+```
+
+A more detailed message is written to the server log.
+
+Server log:
+
+```
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD dn="uid=user,ou=persons,dc=my-domain,dc=com"
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD attr=userPassword
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: entry uid=user,ou=persons,dc=my-domain,dc=com
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reading pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: RAW configuration: # minQuality parameter#012# Format:#012# minQuality [NUMBER]#012# Description:#012# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.#012# defines the minimum point numbers for the password to be accepted.#012minQuality 3#012#012# checkRDN parameter#012# Format:#012# checkRDN [0 | 1]#012# Description:#012# If set to 1, password must not contain a token from the RDN.#012# Tokens are separated by the following delimiters : space tabulation _ - , ; £#012checkRDN 0#012#012# forbiddenChars parameter#012# Format:#012# forbiddenChars [CHARACTERS_FORBIDDEN]#012# Description:#012# Defines the forbidden characters list (no separator).#012# If one of them is found in the password, then it is rejected.#012forbiddenChars#012#012# maxConsecutivePerClass parameter#012# Format:#012# maxConsecutivePerClass [NUMBER]#012# Description:#012# Defines the maximum number of consecutive character allowed for any class#012maxConsecutivePerClass 0#012#012# useCracklib parameter#012# Format:#012# useCracklib [0 | 1]#012# Description:#012# If set to 1, the password must pass the cracklib check#012useCracklib 0#012#012# cracklibDict parameter#012# Format:#012# cracklibDict [path_to_cracklib_dictionary]#012# Description:#012# directory+filename-prefix that your version of CrackLib will go hunting for#012# For example, /var/pw_dict resolves as /var/pw_dict.pwd,#012# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files#012cracklibDict /var/cache/cracklib/cracklib_dict#012#012# classes parameter#012# Format:#012# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]#012# Description:#012# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)#012# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected#012# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class#012class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1#012class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1#012class-digit 0123456789 0 1#012class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Parsing pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # defines the minimum point numbers for the password to be accepted.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: minQuality 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, password must not contain a token from the RDN.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Tokens are separated by the following delimiters : space tabulation _ - , ; £
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: checkRDN 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = checkRDN, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars [CHARACTERS_FORBIDDEN]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the forbidden characters list (no separator).
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If one of them is found in the password, then it is rejected.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: forbiddenChars
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: No value, goto next parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the maximum number of consecutive character allowed for any class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: maxConsecutivePerClass 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = maxConsecutivePerClass, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, the password must pass the cracklib check
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: useCracklib 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = useCracklib, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict [path_to_cracklib_dictionary]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # directory+filename-prefix that your version of CrackLib will go hunting for
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: cracklibDict /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = cracklibDict, value = /var/cache/cracklib/cracklib_dict, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # classes parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: abcdefghijklmnopqrstuvwxyz
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-digit 0123456789 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0123456789
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-special, value = <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-lowerCase
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-digit
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reallocating szErrStr from 64 to 173
+Feb 26 14:46:10 debian-11-64 slapd[1981]: check_password_quality: module error: (/usr/local/lib/ppm.so) Password for dn="uid=user,ou=persons,dc=my-domain,dc=com" does not pass required number of strength checks (2 of 3).[1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 RESULT tag=103 err=19 qtime=0.000020 etime=0.001496 text=Password for dn="uid=user,ou=persons,dc=my-domain,dc=com" does not pass required number of strength checks (2 of 3)
+```
+
+
+# TESTS
+
+There is a unit test script: **unit_tests.sh** that illustrates checking some passwords.
+
+It is possible to test one particular password using directly the test program:
+
+```
+cd /usr/local/lib
+LD_LIBRARY_PATH=. ./ppm_test "uid=test,ou=users,dc=my-domain,dc=com" "my_password" "/usr/local/etc/openldap/ppm.example" && echo OK
+```
+
+
+# FILES
+
+**ETCDIR/ppm.example**
+
+> example of ppm configuration to be inserted in **pwdCheckModuleArg** attribute of given password policy
+
+**ppm.so**
+
+> ppm library, loaded by the **pwdCheckModule** attribute of given password policy
+
+**ppm_test**
+
+> small test program for checking password in a command-line
+
+
+# SEE ALSO
+
+**slapo-ppolicy**(5), **slapd-config**(5), **slapd.conf**(5)
+
+# ACKNOWLEDGEMENTS
+
+This module was developed in 2014-2021 by David Coutadeur.
diff --git a/contrib/slapd-modules/ppm/ppm_test.c b/contrib/slapd-modules/ppm/ppm_test.c
new file mode 100644
index 0000000..520aa0a
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm_test.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "ppm.h"
+
+int main(int argc, char *argv[])
+{
+ /*
+ * argv[1]: user
+ * argv[2]: password
+ * argv[3]: configuration file
+ */
+
+ int ret = 1;
+
+ if(argc > 2)
+ {
+ printf("Testing user %s password: '%s' against %s policy config file \n",
+ argv[1], argv[2], argv[3]
+ );
+
+ /* format user entry */
+ char *errmsg = NULL;
+ Entry pEntry;
+ pEntry.e_nname.bv_val=argv[1];
+ pEntry.e_name.bv_val=argv[1];
+
+ /* get configuration file content */
+ struct berval pArg;
+ FILE *fp;
+ if ((fp = fopen(argv[3],"r")) == NULL)
+ {
+ fprintf(stderr,"Unable to open config file for reading\n");
+ return ret;
+ }
+ char *fcontent = NULL;
+ fseek(fp, 0, SEEK_END);
+ long fsize = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ fcontent = malloc(fsize);
+ fread(fcontent, 1, fsize, fp);
+ fclose(fp);
+ pArg.bv_val = fcontent;
+
+ ppm_test=1; // enable ppm_test for informing ppm not to use syslog
+
+ ret = check_password(argv[2], &errmsg, &pEntry, &pArg);
+
+ if(ret == 0)
+ {
+ printf("Password is OK!\n");
+ }
+ else
+ {
+ printf("Password failed checks : %s\n", errmsg);
+ }
+
+ ber_memfree(errmsg);
+ return ret;
+
+ }
+
+ return ret;
+}
+
+
+
diff --git a/contrib/slapd-modules/ppm/slapm-ppm.5 b/contrib/slapd-modules/ppm/slapm-ppm.5
new file mode 100644
index 0000000..10e9c8d
--- /dev/null
+++ b/contrib/slapd-modules/ppm/slapm-ppm.5
@@ -0,0 +1,360 @@
+.\" Automatically generated by Pandoc 2.9.2.1
+.\"
+.TH "ppm" "5" "August 24, 2021" "ppm" "File Formats Manual"
+.hy
+.SH NAME
+.PP
+ppm (Password Policy Module) - extension of the password policy overlay
+.SH SYNOPSIS
+.PP
+ETCDIR/ppm.example
+.SH DESCRIPTION
+.PP
+\f[B]ppm\f[R] is an OpenLDAP module for checking password quality when
+they are modified.
+Passwords are checked against the presence or absence of certain
+character classes.
+.PP
+This module is used as an extension of the OpenLDAP password policy
+controls, see slapo-ppolicy(5) section \f[B]pwdCheckModule\f[R].
+.SH USAGE
+.PP
+Create a password policy entry and indicate the path of the ppm.so
+library and the content of the desired policy.
+Use a base64 tool to code / decode the content of the policy stored into
+\f[B]pwdCheckModuleArg\f[R].
+Here is an example:
+.IP
+.nf
+\f[C]
+dn: cn=default,ou=policies,dc=my-domain,dc=com
+objectClass: pwdPolicy
+objectClass: top
+objectClass: pwdPolicyChecker
+objectClass: person
+pwdCheckQuality: 2
+pwdAttribute: userPassword
+sn: default
+cn: default
+pwdMinLength: 6
+pwdCheckModule: /usr/local/lib/ppm.so
+pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+\f[R]
+.fi
+.PP
+See \f[B]slapo-ppolicy\f[R] for more information, but to sum up:
+.IP \[bu] 2
+enable ppolicy overlay in your database.
+.IP \[bu] 2
+define a default password policy in OpenLDAP configuration or use
+pwdPolicySubentry attribute to point to the given policy.
+.PP
+This example show the activation for a \f[B]slapd.conf\f[R] file (see
+\f[B]slapd-config\f[R] and \f[B]slapo-ppolicy\f[R] for more information
+for \f[B]cn=config\f[R] configuration)
+.IP
+.nf
+\f[C]
+overlay ppolicy
+ppolicy_default \[dq]cn=default,ou=policies,dc=my-domain,dc=com\[dq]
+#ppolicy_use_lockout # for having more infos about the lockout
+\f[R]
+.fi
+.SH FEATURES
+.PP
+Here are the main features:
+.IP \[bu] 2
+4 character classes are defined by default: upper case, lower case,
+digits and special characters.
+.IP \[bu] 2
+more character classes can be defined, just write your own.
+.IP \[bu] 2
+passwords must match the amount of quality points.
+A point is validated when at least m characters of the corresponding
+character class are present in the password.
+.IP \[bu] 2
+passwords must have at least n of the corresponding character class
+present, else they are rejected.
+.IP \[bu] 2
+the two previous criteria are checked against any specific character
+class defined.
+.IP \[bu] 2
+if a password contains any of the forbidden characters, then it is
+rejected.
+.IP \[bu] 2
+if a password contains tokens from the RDN, then it is rejected.
+.IP \[bu] 2
+if a password does not pass cracklib check, then it is rejected.
+.SH CONFIGURATION
+.PP
+Since OpenLDAP 2.5 version, ppm configuration is held in a binary
+attribute of the password policy: \f[B]pwdCheckModuleArg\f[R]
+.PP
+The example file (\f[B]ETCDIR/ppm.example\f[R] by default) is to be
+considered as an example configuration, to import in the
+\f[B]pwdCheckModuleArg\f[R] attribute.
+It is also used for testing passwords with the test program provided.
+.PP
+If for some reasons, any parameter is not found, it will be given its
+default value.
+.PP
+Note: you can still compile ppm to use the configuration file, by
+enabling \f[B]PPM_READ_FILE\f[R] in \f[B]ppm.h\f[R] (but this is
+deprecated now).
+If you decide to do so, you can use the \f[B]PPM_CONFIG_FILE\f[R]
+environment variable for overloading the configuration file path.
+.PP
+The syntax of a configuration line is:
+.IP
+.nf
+\f[C]
+parameter value [min] [minForPoint]
+\f[R]
+.fi
+.PP
+with spaces being delimiters and Line Feed (LF) ending the line.
+.PP
+Parameter names \f[B]are\f[R] case sensitive.
+.PP
+Lines beginning by a \f[B]#\f[R] are considered as comments.
+.PP
+The default configuration is the following:
+.IP
+.nf
+\f[C]
+# minQuality parameter
+# Format:
+# minQuality [NUMBER]
+# Description:
+# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+# defines the minimum point numbers for the password to be accepted.
+minQuality 3
+
+# checkRDN parameter
+# Format:
+# checkRDN [0 | 1]
+# Description:
+# If set to 1, password must not contain a token from the RDN.
+# Tokens are separated by the following delimiters : space tabulation _ - , ; \[Po]
+checkRDN 0
+
+# forbiddenChars parameter
+# Format:
+# forbiddenChars [CHARACTERS_FORBIDDEN]
+# Description:
+# Defines the forbidden characters list (no separator).
+# If one of them is found in the password, then it is rejected.
+forbiddenChars
+
+# maxConsecutivePerClass parameter
+# Format:
+# maxConsecutivePerClass [NUMBER]
+# Description:
+# Defines the maximum number of consecutive character allowed for any class
+maxConsecutivePerClass 0
+
+# useCracklib parameter
+# Format:
+# useCracklib [0 | 1]
+# Description:
+# If set to 1, the password must pass the cracklib check
+useCracklib 0
+
+# cracklibDict parameter
+# Format:
+# cracklibDict [path_to_cracklib_dictionary]
+# Description:
+# directory+filename-prefix that your version of CrackLib will go hunting for
+# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+cracklibDict /var/cache/cracklib/cracklib_dict
+
+# classes parameter
+# Format:
+# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+# Description:
+# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+\f[R]
+.fi
+.SH EXAMPLE
+.PP
+With this policy:
+.IP
+.nf
+\f[C]
+minQuality 4
+forbiddenChars .?,
+checkRDN 1
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 5
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 12
+class-digit 0123456789 0 1
+class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+class-myClass :) 1 1\[ga]\[ga]
+\f[R]
+.fi
+.PP
+the password \f[B]ThereIsNoCowLevel)\f[R] is working, because:
+.IP \[bu] 2
+it has 4 character classes validated : upper, lower, special, and
+myClass
+.IP \[bu] 2
+it has no character among .?,
+.IP \[bu] 2
+it has at least one character among : or )
+.PP
+but it won\[cq]t work for the user uid=John
+Cowlevel,ou=people,cn=example,cn=com, because the token
+\[lq]Cowlevel\[rq] from his RDN exists in the password (case
+insensitive).
+.SH LOGS
+.PP
+If a user password is rejected by \f[B]ppm\f[R], the user will get this
+type of message:
+.PP
+Typical user message from ldappasswd(5):
+.IP
+.nf
+\f[C]
+ Result: Constraint violation (19)
+ Additional info: Password for dn=\[rs]\[dq]%s\[rs]\[dq] does not pass required number of strength checks (2 of 3)
+\f[R]
+.fi
+.PP
+A more detailed message is written to the server log.
+.PP
+Server log:
+.IP
+.nf
+\f[C]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD dn=\[dq]uid=user,ou=persons,dc=my-domain,dc=com\[dq]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD attr=userPassword
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: entry uid=user,ou=persons,dc=my-domain,dc=com
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reading pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: RAW configuration: # minQuality parameter#012# Format:#012# minQuality [NUMBER]#012# Description:#012# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.#012# defines the minimum point numbers for the password to be accepted.#012minQuality 3#012#012# checkRDN parameter#012# Format:#012# checkRDN [0 | 1]#012# Description:#012# If set to 1, password must not contain a token from the RDN.#012# Tokens are separated by the following delimiters : space tabulation _ - , ; \[Po]#012checkRDN 0#012#012# forbiddenChars parameter#012# Format:#012# forbiddenChars [CHARACTERS_FORBIDDEN]#012# Description:#012# Defines the forbidden characters list (no separator).#012# If one of them is found in the password, then it is rejected.#012forbiddenChars#012#012# maxConsecutivePerClass parameter#012# Format:#012# maxConsecutivePerClass [NUMBER]#012# Description:#012# Defines the maximum number of consecutive character allowed for any class#012maxConsecutivePerClass 0#012#012# useCracklib parameter#012# Format:#012# useCracklib [0 | 1]#012# Description:#012# If set to 1, the password must pass the cracklib check#012useCracklib 0#012#012# cracklibDict parameter#012# Format:#012# cracklibDict [path_to_cracklib_dictionary]#012# Description:#012# directory+filename-prefix that your version of CrackLib will go hunting for#012# For example, /var/pw_dict resolves as /var/pw_dict.pwd,#012# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files#012cracklibDict /var/cache/cracklib/cracklib_dict#012#012# classes parameter#012# Format:#012# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]#012# Description:#012# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)#012# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected#012# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class#012class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1#012class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1#012class-digit 0123456789 0 1#012class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Parsing pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # defines the minimum point numbers for the password to be accepted.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: minQuality 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, password must not contain a token from the RDN.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Tokens are separated by the following delimiters : space tabulation _ - , ; \[Po]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: checkRDN 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = checkRDN, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars [CHARACTERS_FORBIDDEN]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the forbidden characters list (no separator).
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If one of them is found in the password, then it is rejected.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: forbiddenChars
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: No value, goto next parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the maximum number of consecutive character allowed for any class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: maxConsecutivePerClass 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = maxConsecutivePerClass, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, the password must pass the cracklib check
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: useCracklib 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = useCracklib, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict [path_to_cracklib_dictionary]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # directory+filename-prefix that your version of CrackLib will go hunting for
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: cracklibDict /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = cracklibDict, value = /var/cache/cracklib/cracklib_dict, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # classes parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: abcdefghijklmnopqrstuvwxyz
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-digit 0123456789 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0123456789
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-special, value = <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-lowerCase
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-digit
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reallocating szErrStr from 64 to 173
+Feb 26 14:46:10 debian-11-64 slapd[1981]: check_password_quality: module error: (/usr/local/lib/ppm.so) Password for dn=\[dq]uid=user,ou=persons,dc=my-domain,dc=com\[dq] does not pass required number of strength checks (2 of 3).[1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 RESULT tag=103 err=19 qtime=0.000020 etime=0.001496 text=Password for dn=\[dq]uid=user,ou=persons,dc=my-domain,dc=com\[dq] does not pass required number of strength checks (2 of 3)
+\f[R]
+.fi
+.SH TESTS
+.PP
+There is a unit test script: \f[B]unit_tests.sh\f[R] that illustrates
+checking some passwords.
+.PP
+It is possible to test one particular password using directly the test
+program:
+.IP
+.nf
+\f[C]
+cd /usr/local/lib
+LD_LIBRARY_PATH=. ./ppm_test \[dq]uid=test,ou=users,dc=my-domain,dc=com\[dq] \[dq]my_password\[dq] \[dq]/usr/local/etc/openldap/ppm.example\[dq] && echo OK
+\f[R]
+.fi
+.SH FILES
+.PP
+\f[B]ETCDIR/ppm.example\f[R]
+.RS
+.PP
+example of ppm configuration to be inserted in
+\f[B]pwdCheckModuleArg\f[R] attribute of given password policy
+.RE
+.PP
+\f[B]ppm.so\f[R]
+.RS
+.PP
+ppm library, loaded by the \f[B]pwdCheckModule\f[R] attribute of given
+password policy
+.RE
+.PP
+\f[B]ppm_test\f[R]
+.RS
+.PP
+small test program for checking password in a command-line
+.RE
+.SH SEE ALSO
+.PP
+\f[B]slapo-ppolicy\f[R](5), \f[B]slapd-config\f[R](5),
+\f[B]slapd.conf\f[R](5)
+.SH ACKNOWLEDGEMENTS
+.PP
+This module was developed in 2014-2021 by David Coutadeur.
diff --git a/contrib/slapd-modules/ppm/unit_tests.sh b/contrib/slapd-modules/ppm/unit_tests.sh
new file mode 100755
index 0000000..c152c96
--- /dev/null
+++ b/contrib/slapd-modules/ppm/unit_tests.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# Launch unitary tests
+#
+
+
+CONFIG_FILE="ppm.example"
+
+LDAP_SRC="${LDAP_SRC:-../../..}"
+LDAP_BUILD=${LDAP_BUILD:-${LDAP_SRC}}
+CURRENT_DIR=$( dirname $0 )
+LIB_PATH="${LD_LIBRARY_PATH}:${CURRENT_DIR}:${LDAP_BUILD}/libraries/liblber/.libs:${LDAP_BUILD}/libraries/libldap/.libs"
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m'
+
+RESULT=0
+
+PPM_CONF_1='minQuality 3
+checkRDN 0
+forbiddenChars
+maxConsecutivePerClass 0
+useCracklib 0
+cracklibDict /var/cache/cracklib/cracklib_dict
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'\''{([-|è`_\ç^à@)]°=}+ 0 1'
+
+PPM_CONF_2='minQuality 3
+checkRDN 0
+forbiddenChars à
+maxConsecutivePerClass 5
+useCracklib 0
+cracklibDict /var/cache/cracklib/cracklib_dict
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 2 4
+class-lowerCase abcdefghijklmnopqrstuvwxyz 3 4
+class-digit 0123456789 2 4
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'\''{([-|è`_\ç^à@)]°=}+ 0 4'
+
+PPM_CONF_3='minQuality 3
+checkRDN 1
+forbiddenChars
+maxConsecutivePerClass 0
+useCracklib 0
+cracklibDict /var/cache/cracklib/cracklib_dict
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'\''{([-|è`_\ç^à@)]°=}+ 0 1'
+
+
+echo "$PPM_CONF_1" > ppm1.conf
+echo "$PPM_CONF_2" > ppm2.conf
+echo "$PPM_CONF_3" > ppm3.conf
+
+
+launch_test()
+{
+ # launch tests
+ # FORMAT: launch_test [conf_file] [password] [expected_result]
+ # [expected_result] = [PASS|FAIL]
+
+ local CONF="$1"
+ local USER="$2"
+ local PASS="$3"
+ local EXPECT="$4"
+
+ [[ $EXPECT == "PASS" ]] && EXP="0" || EXP="1"
+
+ LD_LIBRARY_PATH="${LIB_PATH}" ./ppm_test "${USER}" "${PASS}" "${CONF}"
+ RES="$?"
+
+ if [ "$RES" -eq "$EXP" ] ; then
+ echo -e "conf=${CONF} user=${USER} pass=${PASS} expect=${EXPECT}... ${GREEN}PASS${NC}"
+ else
+ echo -e "conf=${CONF} user=${USER} pass=${PASS} expect=${EXPECT}... ${RED}FAIL${NC}"
+ ((RESULT+=1))
+ fi
+
+ echo
+}
+
+
+
+
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azerty" "FAIL"
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azeRTY" "FAIL"
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azeRTY123" "PASS"
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azeRTY." "PASS"
+
+
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaa01AAaaa01AAaaa0" "PASS"
+# forbidden char
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaa01AAaaa01AAaaaà" "FAIL"
+# too much consecutive for upper
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaa01AAaaa01AAAAAA" "FAIL"
+# not enough upper
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "Aaaaa01aaaaa01aa.;.;" "FAIL"
+# not enough lower
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "aaAAA01BB0123AAA.;.;" "FAIL"
+# not enough digit
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "1AAAA.;BBB.;.;AA.;.;" "FAIL"
+# not enough points (no point for digit)
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaaBBBBaaa01AAaaaa" "FAIL"
+
+# password in RDN
+launch_test "ppm3.conf" "uid=User_Password10-test,ou=users,dc=my-domain,dc=com" "Password10" "FAIL"
+launch_test "ppm3.conf" "uid=User_Passw0rd-test,ou=users,dc=my-domain,dc=com" "Password10" "PASS"
+launch_test "ppm3.conf" "uid=User-Pw-Test,ou=users,dc=my-domain,dc=com" "Password10" "PASS"
+
+
+echo "${RESULT} error(s) encountered"
+
+rm ppm1.conf ppm2.conf ppm3.conf
+exit ${RESULT}
+
diff --git a/contrib/slapd-modules/proxyOld/Makefile b/contrib/slapd-modules/proxyOld/Makefile
new file mode 100644
index 0000000..d92967c
--- /dev/null
+++ b/contrib/slapd-modules/proxyOld/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2005-2022 The OpenLDAP Foundation.
+# Portions Copyright 2005 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = proxyOld.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+proxyOld.la: proxyOld.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/proxyOld/README b/contrib/slapd-modules/proxyOld/README
new file mode 100644
index 0000000..bc5e4ab
--- /dev/null
+++ b/contrib/slapd-modules/proxyOld/README
@@ -0,0 +1,31 @@
+This directory contains a slapd module proxyOld that provides support
+for the obsolete draft-weltman-ldapb3-proxy-05 revision of the LDAP
+Proxy Authorization control. It is merely intended to provide compatibility
+in environments where other servers only recognize this old control.
+New installations should not use this code.
+
+To use the module, add:
+
+ moduleload <path to>proxyOld.so
+ ...
+
+to your slapd configuration file. Since this is an obsolete feature,
+the control is registered with the SLAP_CTRL_HIDE flag so that it will
+not be advertised in the rootDSE's supportedControls attribute.
+
+This code only works as a dynamically loaded module.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright 2005 Howard Chu, Symas Corp. 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>.
+
diff --git a/contrib/slapd-modules/proxyOld/proxyOld.c b/contrib/slapd-modules/proxyOld/proxyOld.c
new file mode 100644
index 0000000..2da6888
--- /dev/null
+++ b/contrib/slapd-modules/proxyOld/proxyOld.c
@@ -0,0 +1,128 @@
+/* proxyOld.c - module for supporting obsolete (rev 05) proxyAuthz control */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2005 by Howard Chu, Symas Corp.
+ * 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>.
+ */
+
+#include <portable.h>
+
+#include <slap.h>
+
+#include <lber.h>
+/*
+#include <lber_pvt.h>
+#include <lutil.h>
+*/
+
+/* This code is based on draft-weltman-ldapv3-proxy-05. There are a lot
+ * of holes in that draft, it doesn't specify that the control is legal
+ * for Add operations, and it makes no mention of Extended operations.
+ * It also doesn't specify whether an empty LDAPDN is allowed in the
+ * control value.
+ *
+ * For usability purposes, we're copying the op / exop behavior from the
+ * newer -12 draft.
+ */
+#define LDAP_CONTROL_PROXY_AUTHZ05 "2.16.840.1.113730.3.4.12"
+
+static char *proxyOld_extops[] = {
+ LDAP_EXOP_MODIFY_PASSWD,
+ LDAP_EXOP_X_WHO_AM_I,
+ NULL
+};
+
+static int
+proxyOld_parse(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ int rc;
+ BerElement *ber;
+ ber_tag_t tag;
+ struct berval dn = BER_BVNULL;
+ struct berval authzDN = BER_BVNULL;
+
+
+ /* We hijack the flag for the new control. Clearly only one or the
+ * other can be used at any given time.
+ */
+ if ( op->o_proxy_authz != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "proxy authorization control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_proxy_authz = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ /* Parse the control value
+ * proxyAuthzControlValue ::= SEQUENCE {
+ * proxyDN LDAPDN
+ * }
+ */
+ ber = ber_init( &ctrl->ldctl_value );
+ if ( ber == NULL ) {
+ rs->sr_text = "ber_init failed";
+ return LDAP_OTHER;
+ }
+
+ tag = ber_scanf( ber, "{m}", &dn );
+
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "proxyOld control could not be decoded";
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ if ( BER_BVISEMPTY( &dn )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "proxyOld_parse: conn=%lu anonymous\n",
+ op->o_connid );
+ authzDN.bv_val = ch_strdup("");
+ } else {
+ Debug( LDAP_DEBUG_ARGS,
+ "proxyOld_parse: conn %lu ctrl DN=\"%s\"\n",
+ op->o_connid, dn.bv_val );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &authzDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ rc = slap_sasl_authorized( op, &op->o_ndn, &authzDN );
+ if ( rc ) {
+ op->o_tmpfree( authzDN.bv_val, op->o_tmpmemctx );
+ rs->sr_text = "not authorized to assume identity";
+ /* new spec uses LDAP_PROXY_AUTHZ_FAILURE */
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto done;
+ }
+ }
+ free( op->o_ndn.bv_val );
+ free( op->o_dn.bv_val );
+ op->o_ndn = authzDN;
+ ber_dupbv( &op->o_dn, &authzDN );
+
+ Debug( LDAP_DEBUG_STATS, "conn=%lu op=%lu PROXYOLD dn=\"%s\"\n",
+ op->o_connid, op->o_opid,
+ authzDN.bv_len ? authzDN.bv_val : "anonymous" );
+ rc = LDAP_SUCCESS;
+done:
+ ber_free( ber, 1 );
+ return rc;
+}
+
+int init_module(int argc, char *argv[]) {
+ return register_supported_control( LDAP_CONTROL_PROXY_AUTHZ05,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_HIDE|SLAP_CTRL_ACCESS, proxyOld_extops,
+ proxyOld_parse, NULL );
+}
diff --git a/contrib/slapd-modules/rbac/Makefile b/contrib/slapd-modules/rbac/Makefile
new file mode 100755
index 0000000..1180bd6
--- /dev/null
+++ b/contrib/slapd-modules/rbac/Makefile
@@ -0,0 +1,63 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_RBAC=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = rbac.la
+MANPAGES = slapo-rbac.5
+
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+SRCS = rbac.c rbacperm.c rbacsess.c rbacuser.c rbacreq.c rbacaudit.c init.c rbacacl.c util.c jts.c
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+LOBJS = $(patsubst %.c,%.lo,$(SRCS))
+
+.SUFFIXES: .c .lo
+
+%.lo: %.c rbac.h
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+rbac.la: $(LOBJS)
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $^ $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/rbac/init.c b/contrib/slapd-modules/rbac/init.c
new file mode 100644
index 0000000..1925ae5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/init.c
@@ -0,0 +1,324 @@
+/* init.c - RBAC initialization */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static slap_callback nullsc = { NULL, NULL, NULL, NULL };
+
+struct slap_rbac_internal_schema slap_rbac_schema;
+
+extern rbac_tenant_t rbac_tenants;
+extern int initialize_jts( void );
+
+rbac_ad_t rbac_session_ads[] = {
+ { RBAC_SESSION_ID,
+ BER_BVC("rbacSessid"), &slap_rbac_schema.ad_session_id },
+ { RBAC_USER_DN,
+ BER_BVC("rbacUserDN"), &slap_rbac_schema.ad_session_user_dn },
+ { RBAC_ROLES,
+ BER_BVC("rbacRoles"), &slap_rbac_schema.ad_session_roles },
+ { RBAC_ROLE_CONSTRAINTS,
+ BER_BVC("rbacRoleConstraints"),
+ &slap_rbac_schema.ad_session_role_constraints },
+ { RBAC_UID,
+ BER_BVC("uid"), &slap_rbac_schema.ad_uid},
+ { RBAC_TENANT_ID,
+ BER_BVC("tenantid"), &slap_rbac_schema.ad_tenant_id },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t rbac_session_permission_ads[] = {
+ { RBAC_OP_NAME,
+ BER_BVC("rbacOpName"), &slap_rbac_schema.ad_permission_opname },
+ { RBAC_OBJ_NAME,
+ BER_BVC("rbacObjName"), &slap_rbac_schema.ad_permission_objname },
+ { RBAC_ROLE_NAME,
+ BER_BVC("rbacRoleName"), &slap_rbac_schema.ad_permission_rolename },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t audit_ads[] = {
+ { RBAC_AUDIT_OP,
+ BER_BVC("rbacAuditOp"), &slap_rbac_schema.ad_audit_op },
+ { RBAC_AUDIT_ID,
+ BER_BVC("rbacAuditId"), &slap_rbac_schema.ad_audit_id },
+ { RBAC_AUDIT_ROLES,
+ BER_BVC("rbacAuditRoles"), &slap_rbac_schema.ad_audit_roles },
+ { RBAC_AUDIT_REQUESTED_ROLES,
+ BER_BVC("rbacAuditRequestedRoles"),
+ &slap_rbac_schema.ad_audit_requested_roles
+ },
+ { RBAC_AUDIT_TIMESTAMP,
+ BER_BVC("rbacAuditTimestamp"), &slap_rbac_schema.ad_audit_timestamp },
+ { RBAC_AUDIT_RESOURCES,
+ BER_BVC("rbacAuditResources"), &slap_rbac_schema.ad_audit_resources },
+ { RBAC_AUDIT_OBJS,
+ BER_BVC("rbacAuditObjects"), &slap_rbac_schema.ad_audit_objects },
+ { RBAC_AUDIT_OPS,
+ BER_BVC("rbacAuditOperations"), &slap_rbac_schema.ad_audit_operations },
+ { RBAC_AUDIT_RESULT,
+ BER_BVC("rbacAuditResult"), &slap_rbac_schema.ad_audit_result },
+ { RBAC_AUDIT_PROPERTIES,
+ BER_BVC("rbacAuditProperties"), &slap_rbac_schema.ad_audit_properties },
+ { RBAC_AUDIT_MSGS,
+ BER_BVC("rbacAuditMessages"), &slap_rbac_schema.ad_audit_messages },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+/* initialize repository attribute descriptions */
+
+static int
+initialize_sessions()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+ const char *text;
+
+ for ( nattrs = 0; !BER_BVISNULL( &rbac_session_ads[nattrs].attr );
+ nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_schema.session_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad(
+ &rbac_session_ads[i].attr, rbac_session_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ slap_rbac_schema.session_attrs[i].an_name = rbac_session_ads[i].attr;
+ slap_rbac_schema.session_attrs[i].an_desc = *rbac_session_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_schema.session_attrs[nattrs].an_name );
+
+done:;
+ return rc;
+}
+
+static int
+initialize_audit()
+{
+ int i, rc = LDAP_SUCCESS;
+ const char *text;
+
+ /* for audit */
+ for ( i = 0; !BER_BVISNULL( &audit_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad( &audit_ads[i].attr, audit_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+ return rc;
+}
+
+static int
+initialize_tenant(
+ BackendDB *be,
+ ConfigReply *cr,
+ tenant_info_t *tenantp,
+ int init_op )
+{
+ int rc = LDAP_SUCCESS;
+ Entry *e = NULL;
+ OperationBuffer opbuf;
+ Operation *op2;
+ SlapReply rs2 = { REP_RESULT };
+ Connection conn = { 0 };
+ struct berval rbac_container_oc = BER_BVC("rbacContainer");
+ struct berval rbac_audit_container = BER_BVC("audit");
+ struct berval rbac_session_container = BER_BVC("rbac");
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ e = entry_alloc();
+
+ switch ( init_op ) {
+ case INIT_AUDIT_CONTAINER:
+ ber_dupbv( &e->e_name, &tenantp->audit_basedn );
+ ber_dupbv( &e->e_nname, &tenantp->audit_basedn );
+
+ /* container cn */
+ attr_merge_one(
+ e, slap_schema.si_ad_cn, &rbac_audit_container, NULL );
+ break;
+ case INIT_SESSION_CONTAINER:
+ ber_dupbv( &e->e_name, &tenantp->sessions_basedn );
+ ber_dupbv( &e->e_nname, &tenantp->sessions_basedn );
+
+ /* rendered dynmaicObject for session */
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+ /* container cn */
+ attr_merge_one(
+ e, slap_schema.si_ad_cn, &rbac_session_container, NULL );
+ break;
+ default:
+ break;
+ }
+
+ attr_merge_one(
+ e, slap_schema.si_ad_objectClass, &rbac_container_oc, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &rbac_container_oc, NULL );
+
+ /* store RBAC session */
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op2 = &opbuf.ob_op;
+ op2->o_callback = &nullsc;
+ op2->o_tag = LDAP_REQ_ADD;
+ op2->o_protocol = LDAP_VERSION3;
+ op2->o_req_dn = e->e_name;
+ op2->o_req_ndn = e->e_nname;
+ op2->ora_e = e;
+ op2->o_bd = select_backend( &op2->o_req_ndn, 0 );
+ op2->o_dn = op2->o_bd->be_rootdn;
+ op2->o_ndn = op2->o_bd->be_rootndn;
+ rc = op2->o_bd->be_add( op2, &rs2 );
+
+ if ( e ) entry_free( e );
+
+ return rc;
+}
+
+int
+rbac_initialize_tenants( BackendDB *be, ConfigReply *cr )
+{
+ int rc = LDAP_SUCCESS;
+ rbac_tenant_t *tenantp = NULL;
+
+ for ( tenantp = &rbac_tenants; tenantp; tenantp = tenantp->next ) {
+ rc = initialize_tenant(
+ be, cr, &tenantp->tenant_info, INIT_AUDIT_CONTAINER );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "audit container exists, tenant (%s)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ rc = LDAP_SUCCESS;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "failed to initialize (%s): rc (%d)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL",
+ rc );
+ goto done;
+ }
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "created audit container for tenant (%s):\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ }
+ rc = initialize_tenant(
+ be, cr, &tenantp->tenant_info, INIT_SESSION_CONTAINER );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "session container exists, tenant (%s)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ rc = LDAP_SUCCESS;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "failed to initialize (%s): rc (%d)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL",
+ rc );
+ goto done;
+ }
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "created session container for tenant (%s):\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ }
+ }
+
+done:;
+
+ return rc;
+}
+
+static int
+initialize_rbac_session_permissions()
+{
+ int i, rc = LDAP_SUCCESS;
+ const char *text;
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad( &rbac_session_permission_ads[i].attr,
+ rbac_session_permission_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+ return rc;
+}
+
+int
+rbac_initialize_repository()
+{
+ int rc = LDAP_SUCCESS;
+
+ rc = initialize_jts();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_rbac_session_permissions();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_sessions();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_audit();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/jts.c b/contrib/slapd-modules/rbac/jts.c
new file mode 100644
index 0000000..c7c072b
--- /dev/null
+++ b/contrib/slapd-modules/rbac/jts.c
@@ -0,0 +1,198 @@
+/* jts.c - RBAC JTS initialization */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+struct slap_rbac_tenant_schema slap_rbac_jts_schema;
+
+/* to replace all JTS schema initialization */
+rbac_ad_t ft_ads[] = {
+ { RBAC_ROLE_ASSIGNMENT,
+ BER_BVC("ftRA"), &slap_rbac_jts_schema.ad_role },
+ { RBAC_ROLE_CONSTRAINTS,
+ BER_BVC("ftRC"), &slap_rbac_jts_schema.ad_role_constraint },
+ { RBAC_USER_CONSTRAINTS,
+ BER_BVC("ftCstr"), &slap_rbac_jts_schema.ad_user_constraint },
+ { RBAC_UID,
+ BER_BVC("uid"), &slap_rbac_jts_schema.ad_uid },
+ { RBAC_USERS,
+ BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+ { RBAC_ROLES,
+ BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+ { RBAC_OBJ_NAME,
+ BER_BVC("ftObjNm"), &slap_rbac_jts_schema.ad_permission_objname },
+ { RBAC_OP_NAME,
+ BER_BVC("ftOpNm"), &slap_rbac_jts_schema.ad_permission_opname },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_user_ads[] = {
+ { RBAC_ROLE_ASSIGNMENT,
+ BER_BVC("ftRA"), &slap_rbac_jts_schema.ad_role },
+ { RBAC_ROLE_CONSTRAINTS,
+ BER_BVC("ftRC"), &slap_rbac_jts_schema.ad_role_constraint },
+ { RBAC_USER_CONSTRAINTS,
+ BER_BVC("ftCstr"), &slap_rbac_jts_schema.ad_user_constraint },
+ { RBAC_UID,
+ BER_BVC("uid"), &slap_rbac_jts_schema.ad_uid },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_perm_ads[] = {
+ { RBAC_USERS,
+ BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+ { RBAC_ROLES,
+ BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_session_perm_ads[] = {
+ { RBAC_USERS,
+ BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+ { RBAC_ROLES,
+ BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+ { RBAC_OBJ_NAME,
+ BER_BVC("ftObjNm"), &slap_rbac_jts_schema.ad_permission_objname },
+ { RBAC_OP_NAME,
+ BER_BVC("ftOpNm"), &slap_rbac_jts_schema.ad_permission_opname },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+static int
+initialize_jts_session_permission_ads()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+
+ for ( nattrs = 0; !BER_BVISNULL( &ft_session_perm_ads[nattrs].attr );
+ nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_jts_schema.session_perm_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ft_session_perm_ads[i].attr ); i++ ) {
+ slap_rbac_jts_schema.session_perm_attrs[i].an_name =
+ ft_session_perm_ads[i].attr;
+ slap_rbac_jts_schema.session_perm_attrs[i].an_desc =
+ *ft_session_perm_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_jts_schema.session_perm_attrs[nattrs].an_name );
+
+ slap_rbac_jts_schema.session_permissions_ads = ft_session_perm_ads;
+
+ return rc;
+}
+
+static int
+initialize_jts_permission_ads()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+
+ /* jts permissions configuration */
+
+ for ( nattrs = 0; !BER_BVISNULL( &ft_perm_ads[nattrs].attr ); nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_jts_schema.perm_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ft_perm_ads[i].attr ); i++ ) {
+ slap_rbac_jts_schema.perm_attrs[i].an_name = ft_perm_ads[i].attr;
+ slap_rbac_jts_schema.perm_attrs[i].an_desc = *ft_perm_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_jts_schema.perm_attrs[nattrs].an_name );
+
+ slap_rbac_jts_schema.permission_ads = ft_perm_ads;
+
+ return rc;
+}
+
+static int
+initialize_jts_user_ads()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+
+ /* jts user attribute descriptions */
+
+ /* jts user attributes */
+ for ( nattrs = 0; !BER_BVISNULL( &ft_user_ads[nattrs].attr ); nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_jts_schema.user_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ft_user_ads[i].attr ); i++ ) {
+ slap_rbac_jts_schema.user_attrs[i].an_name = ft_user_ads[i].attr;
+ slap_rbac_jts_schema.user_attrs[i].an_desc = *ft_user_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_jts_schema.user_attrs[nattrs].an_name );
+
+ slap_rbac_jts_schema.user_ads = ft_user_ads;
+
+ return rc;
+}
+
+int
+initialize_jts()
+{
+ int i, rc;
+ const char *text;
+
+ /* jts attributes */
+ for ( i = 0; !BER_BVISNULL( &ft_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad( &ft_ads[i].attr, ft_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+ rc = initialize_jts_user_ads();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_jts_permission_ads();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_jts_session_permission_ads();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+done:;
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/ldap_rbac.h b/contrib/slapd-modules/rbac/ldap_rbac.h
new file mode 100644
index 0000000..d57fe6e
--- /dev/null
+++ b/contrib/slapd-modules/rbac/ldap_rbac.h
@@ -0,0 +1,55 @@
+#ifndef LDAP_RBAC_H
+#define LDAP_RBAC_H
+
+/* extended operations for RBAC */
+#define LDAP_RBAC_EXOP_CREATE_SESSION "1.3.6.1.4.1.4203.555.1" /* RFC xxxx */
+#define LDAP_RBAC_EXOP_CHECK_ACCESS "1.3.6.1.4.1.4203.555.2"
+#define LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE "1.3.6.1.4.1.4203.555.3"
+#define LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE "1.3.6.1.4.1.4203.555.4"
+#define LDAP_RBAC_EXOP_DELETE_SESSION "1.3.6.1.4.1.4203.555.5"
+#define LDAP_RBAC_EXOP_SESSION_ROLES "1.3.6.1.4.1.4203.555.6"
+#define LDAP_RBAC_EXOP_SESSION_PERMISSIONS "1.3.6.1.4.1.4203.555.7"
+
+#define LDAP_TAG_EXOP_RBAC_SESSION_ID ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_TENANT_ID ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_USER_ID ((ber_tag_t)0x82U)
+#define LDAP_TAG_EXOP_RBAC_USER ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_AUTHTOK ((ber_tag_t)0x83U)
+#define LDAP_TAG_EXOP_RBAC_ACTIVE_ROLE ((ber_tag_t)0xA4U)
+#define LDAP_TAG_EXOP_RBAC_OPNAME ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_OBJNAME ((ber_tag_t)0x82U)
+#define LDAP_TAG_EXOP_RBAC_OBJID ((ber_tag_t)0x83U)
+#define LDAP_TAG_EXOP_RBAC_PWPOLICY_STATE ((ber_tag_t)0x85U)
+#define LDAP_TAG_EXOP_RBAC_PWPOLICY_VALUE ((ber_tag_t)0x86U)
+#define LDAP_TAG_EXOP_RBAC_ROLES ((ber_tag_t)0x04U)
+
+#define LDAP_TAG_EXOP_RBAC_USER_ID_SESS ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_ROLE_NM_SESS ((ber_tag_t)0x82U)
+
+#define RBAC_REQ_CREATE_SESSION 0
+#define RBAC_REQ_CHECK_ACCESS 1
+#define RBAC_REQ_ADD_ACTIVE_ROLE 2
+#define RBAC_REQ_DROP_ACTIVE_ROLE 3
+#define RBAC_REQ_DELETE_SESSION 4
+#define RBAC_REQ_SESSION_PERMISSIONS 5
+#define RBAC_REQ_SESSION_ROLES 6
+
+/* defines for password policy */
+#define RBAC_BIND_NEW_AUTHTOK_REQD 1
+
+#define RBAC_PASSWORD_GOOD 0
+#define RBAC_PASSWORD_EXPIRATION_WARNING 11
+#define RBAC_PASSWORD_GRACE_WARNING 12
+#define RBAC_PASSWORD_HAS_EXPIRED 100
+#define RBAC_ACCOUNT_LOCKED 101
+#define RBAC_CHANGE_AFTER_RESET 102
+#define RBAC_NO_MODIFICATIONS 103
+#define RBAC_MUST_SUPPLY_OLD 104
+#define RBAC_INSUFFICIENT_QUALITY 105
+#define RBAC_PASSWORD_TOO_SHORT 106
+#define RBAC_PASSWORD_TOO_YOUNG 107
+#define RBAC_HISTORY_VIOLATION 108
+#define RBAC_ACCOUNT_LOCKED_CONSTRAINTS 109
+
+#endif /* LDAP_RBAC_H */
diff --git a/contrib/slapd-modules/rbac/rbac.c b/contrib/slapd-modules/rbac/rbac.c
new file mode 100644
index 0000000..4d2cff5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbac.c
@@ -0,0 +1,2169 @@
+/* rbac.c - RBAC main file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2013-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+#define RBAC_REQ 1
+
+static slap_overinst rbac;
+
+static struct berval slap_EXOP_CREATE_SESSION =
+ BER_BVC(LDAP_RBAC_EXOP_CREATE_SESSION);
+static struct berval slap_EXOP_CHECK_ACCESS =
+ BER_BVC(LDAP_RBAC_EXOP_CHECK_ACCESS);
+static struct berval slap_EXOP_ADD_ACTIVE_ROLE =
+ BER_BVC(LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE);
+static struct berval slap_EXOP_DROP_ACTIVE_ROLE =
+ BER_BVC(LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE);
+static struct berval slap_EXOP_DELETE_SESSION =
+ BER_BVC(LDAP_RBAC_EXOP_DELETE_SESSION);
+static struct berval slap_EXOP_SESSION_ROLES =
+ BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES);
+
+rbac_tenant_t rbac_tenants = {
+ {
+ .schema = &slap_rbac_jts_schema,
+ },
+ NULL
+};
+
+static ConfigDriver rbac_cf_gen;
+
+static ConfigTable rbaccfg[] = {
+ { "rbac-default-users-base-dn", "usersDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_USERS_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.1 NAME 'olcRBACDefaultUsersBaseDn' "
+ "DESC 'default Base DN for RBAC users ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-roles-base-dn", "rolesDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_ROLES_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.2 NAME 'olcRBACDefaultRolesBaseDn' "
+ "DESC 'default base DN for RBAC roles ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-permissions-base-dn", "permissionsDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_PERMISSIONS_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.3 NAME 'olcRBACDefaultPermissionsBaseDn' "
+ "DESC 'default base DN for RBAC permissions ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-sessions-base-dn", "sessionsDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_SESSIONS_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.4 NAME 'olcRBACDefaultSessionsBaseDn' "
+ "DESC 'default base DN for RBAC permissions ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-admin", "adminDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_ADMIN_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.5 NAME 'olcRBACAdminDn' "
+ "DESC 'default admin DN for RBAC repository ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-pwd", "adminPwd", 2, 2, 0,
+ ARG_MAGIC|RBAC_ADMIN_PWD,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.6 NAME 'olcRBACAdminPwd' "
+ "DESC 'default admin pwd for RBAC repository ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-session-admin", "sessionAdminDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_SESSION_ADMIN_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.7 NAME 'olcRBACSessionAdminDn' "
+ "DESC 'admin DN for RBAC session repository ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-session-admin-pwd", "sessionAdminPwd", 2, 2, 0,
+ ARG_MAGIC|RBAC_SESSION_ADMIN_PWD,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.8 NAME 'olcRBACSessionAdminPwd' "
+ "DESC 'admin pwd for RBAC session repository ' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "tenant", "tenant", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_TENANT,
+ rbac_cf_gen, "(OLcfgCtAt:7.9 NAME 'olcRBACTenant' "
+ "DESC 'RBAC tenant ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-audit-base-dn", "auditDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_AUDIT_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.10 NAME 'olcRBACDefaultAuditBaseDn' "
+ "DESC 'default base DN for RBAC audit records ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-tenant-id", "tenantId", 2, 2, 0,
+ ARG_MAGIC|RBAC_DEFAULT_TENANT_ID,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.11 NAME 'olcRBACDefaultTenantId' "
+ "DESC 'default tenant id' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs rbac_ocs[] = {
+ { "( OLcfgCtOc:7.1 "
+ "NAME 'olcRBACConfig' "
+ "DESC 'RBAC configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcRBACDefaultUsersBaseDn $ olcRBACDefaultRolesBaseDn $ "
+ "olcRBACDefaultPermissionsBaseDn $ olcRBACDefaultSessionsBaseDn $ "
+ "olcRBACAdminDn $ olcRBACAdminPwd $ olcRBACSessionAdminDn $ "
+ "olcRBACSessionAdminPwd) )",
+ Cft_Overlay, rbaccfg },
+
+ { NULL, 0, NULL }
+};
+
+static slap_verbmasks rbac_keys[] = {
+ { BER_BVC("default_users_base_dn"), RBAC_DEFAULT_USERS_BASE_DN },
+ { BER_BVC("default_roles_base_dn"), RBAC_DEFAULT_ROLES_BASE_DN },
+ { BER_BVC("default_permissions_base_dn"),
+ RBAC_DEFAULT_PERMISSIONS_BASE_DN },
+ { BER_BVC("tenant"), RBAC_TENANT },
+
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks rbac_tenant_keys[] = {
+ { BER_BVC("id"), RBAC_TENANT_ID },
+ { BER_BVC("users_base_dn"), RBAC_USERS_BASE_DN },
+ { BER_BVC("roles_base_dn"), RBAC_ROLES_BASE_DN },
+ { BER_BVC("permissions_base_dn"), RBAC_PERMISSIONS_BASE_DN },
+
+ { BER_BVNULL, 0 }
+};
+
+static void
+rbac_tenant_parse( char *tenent_info, tenant_info_t *tenants )
+{
+ return;
+}
+
+static int
+rbac_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ rbac_tenant_t *ri = &rbac_tenants;
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch ( c->type ) {
+ case RBAC_DEFAULT_USERS_BASE_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.users_basedn );
+ break;
+ case RBAC_DEFAULT_ROLES_BASE_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.roles_basedn );
+ break;
+ case RBAC_DEFAULT_PERMISSIONS_BASE_DN:
+ value_add_one(
+ &c->rvalue_vals, &ri->tenant_info.permissions_basedn );
+ break;
+ case RBAC_DEFAULT_SESSIONS_BASE_DN:
+ value_add_one(
+ &c->rvalue_vals, &ri->tenant_info.sessions_basedn );
+ break;
+ case RBAC_DEFAULT_AUDIT_BASE_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.audit_basedn );
+ break;
+ case RBAC_ADMIN_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.admin );
+ break;
+ case RBAC_ADMIN_PWD:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.pwd );
+ break;
+ case RBAC_SESSION_ADMIN_DN:
+ value_add_one(
+ &c->rvalue_vals, &ri->tenant_info.session_admin );
+ break;
+ case RBAC_DEFAULT_TENANT_ID:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.tid );
+ break;
+ default:
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* FIXME */
+ return 1;
+ }
+ switch ( c->type ) {
+ case RBAC_DEFAULT_USERS_BASE_DN: {
+ struct berval dn = BER_BVNULL;
+ ber_str2bv( c->argv[1], 0, 0, &dn );
+ rc = dnNormalize(
+ 0, NULL, NULL, &dn, &ri->tenant_info.users_basedn, NULL );
+ break;
+ }
+ case RBAC_DEFAULT_ROLES_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.roles_basedn );
+ break;
+ }
+ case RBAC_DEFAULT_PERMISSIONS_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.permissions_basedn );
+ break;
+ }
+ case RBAC_DEFAULT_SESSIONS_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.sessions_basedn );
+ break;
+ }
+ case RBAC_DEFAULT_AUDIT_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.audit_basedn );
+ break;
+ }
+ case RBAC_ADMIN_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.admin );
+ break;
+ }
+ case RBAC_ADMIN_PWD: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.pwd );
+ break;
+ }
+ case RBAC_SESSION_ADMIN_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.session_admin );
+ break;
+ }
+ case RBAC_SESSION_ADMIN_PWD: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.session_admin_pwd );
+ break;
+ }
+ case RBAC_DEFAULT_TENANT_ID: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.tid );
+ break;
+ }
+ case RBAC_TENANT: {
+ rbac_tenant_parse( c->argv[1], &ri->tenant_info );
+ break;
+ }
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * rbac configuration
+ */
+
+tenant_info_t *
+rbac_tid2tenant( struct berval *tid )
+{
+ /* return the only tenant for now */
+ return &rbac_tenants.tenant_info;
+}
+
+//{ BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES), rbac_session_roles },
+
+static int
+slap_parse_rbac_session_roles(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_session_roles exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_SESSION_ROLES );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_session_roles exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+ struct berval uid;
+ tag = ber_scanf( ber, "m", &uid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->uid, &uid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ //tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+ struct berval sessid;
+ tag = ber_scanf( ber, "m", &sessid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &sessid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+rbac_session_roles( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("SessionRoles");
+ rbac_req_t *reqp = NULL;
+ rbac_session_t *sessp;
+ int rc;
+
+ rs->sr_err = slap_parse_rbac_session_roles(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* checking whether the session is owned by the user */
+ if ( !rbac_is_session_owner( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not owned by user\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_int_delete_session( op, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "unable to delete session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /*
+ * If we wanted to...
+ * load these roles into a response with a sequence nested within a
+ * sequence: (No, we're not actually doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1
+ * 0x04 L2 a b c d
+ * 0x04 L3 e f g h
+ * 0x04 L4 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "t{W}", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+
+ /*
+ * Instead we are...
+ * loading these roles into the response within a sequence: (Yes, we are doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1 a b c d
+ * 0x04 L2 e f g h
+ * 0x04 L3 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "tW", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ BerVarray roles = NULL;
+ if ( sessp->roles ) {
+ struct berval tmpbv;
+ // open the sequence:
+ ber_printf( ber, "{" /*}*/ );
+ //char *role;
+ int i = 0;
+ //BerVarray roles = NULL;
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ tmpbv.bv_val = sessp->roles[i].bv_val;
+ tmpbv.bv_len = sessp->roles[i].bv_len;
+ // add role name:
+ ber_bvarray_add_x( &roles, &tmpbv, NULL );
+
+ //LBER_F( int )
+ //ber_bvecadd_x LDAP_P(( struct berval ***bvec,
+ // struct berval *bv, void *ctx ));
+
+ // first attempt at sequence within a sequence...
+ // open another sequence:
+ /*
+ ber_printf( ber, "{" } );
+ // add role name (again):
+ ber_bvarray_add_x(&roles, &tmpbv, NULL);
+ // close the nested sequence:
+ ber_printf( ber, { "}" );
+*/
+ // end 2nd sequence
+ }
+ /*
+ * This is how we add several octet strings at one time. An array of struct berval's is supplied.
+ * The array is terminated by a struct berval with a NULL bv_val.
+ * Note that a construct like '{W}' is required to get an actual SEQUENCE OF octet strings.
+ * But here we are using 'tW' which allows passing a collection of octets w/out a nesting within a sequence.
+ */
+ ber_printf( ber, "tW",
+ LDAP_TAG_EXOP_RBAC_ROLES, roles);
+
+ // TODO: determine why free on roles array causes a seg fault:
+ //ber_bvarray_free_x(roles, NULL);
+
+ // close the sequence:
+ ber_printf( ber, /*{*/ "N}" );
+ }
+
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ ber_free_buf( ber );
+ // END LOAD ROLES INTO RESPONSE
+
+done:;
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_SESSION_ROLES.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, SessionRoles, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+ return rs->sr_err;
+}
+
+static int
+rbac_session_rolesx( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("SessionRoles");
+ rbac_session_t *sessp = NULL;
+ rbac_req_t *reqp = NULL;
+ int rc;
+
+ rs->sr_err = slap_parse_rbac_session_roles(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* checking whether the session is owned by the user */
+ if ( !rbac_is_session_owner( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not owned by user\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_int_delete_session( op, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "unable to delete session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /*
+ * If we wanted to...
+ * load these roles into a response with a sequence nested within a
+ * sequence: (No, we're not actually doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1
+ * 0x04 L2 a b c d
+ * 0x04 L3 e f g h
+ * 0x04 L4 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "t{W}", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+
+ /*
+ * Instead we are...
+ * loading these roles into the response within a sequence: (Yes, we are doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1 a b c d
+ * 0x04 L2 e f g h
+ * 0x04 L3 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "tW", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ BerVarray roles = NULL;
+ if ( sessp->roles ) {
+ struct berval tmpbv;
+ // open the sequence:
+ ber_printf( ber, "{" /*}*/ );
+ //char *role;
+ int i = 0;
+ //BerVarray roles = NULL;
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ tmpbv.bv_val = sessp->roles[i].bv_val;
+ tmpbv.bv_len = sessp->roles[i].bv_len;
+ // add role name:
+ ber_bvarray_add_x( &roles, &tmpbv, NULL );
+
+ // first attempt at sequence within a sequence...
+ // open another sequence:
+ /*
+ ber_printf( ber, "{" } );
+ // add role name (again):
+ ber_bvarray_add_x(&roles, &tmpbv, NULL);
+ // close the nested sequence:
+ ber_printf( ber, { "}" );
+*/
+ // end 2nd sequence
+ }
+ /*
+ * This is how we add several octet strings at one time. An array of struct berval's is supplied.
+ * The array is terminated by a struct berval with a NULL bv_val.
+ * Note that a construct like '{W}' is required to get an actual SEQUENCE OF octet strings.
+ * But here we are using 'tW' which allows passing a collection of octets w/out a nesting within a sequence.
+ */
+ ber_printf( ber, "tW",
+ LDAP_TAG_EXOP_RBAC_ROLES, roles);
+
+ // TODO: determine why free on roles array causes a seg fault:
+ //ber_bvarray_free_x(roles, NULL);
+
+ // close the sequence:
+ ber_printf( ber, /*{*/ "N}" );
+ }
+
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ ber_free_buf( ber );
+ // END LOAD ROLES INTO RESPONSE
+
+done:;
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_SESSION_ROLES.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, SessionRoles, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+ return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_create_session
+ */
+static int
+slap_parse_rbac_create_session(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_create_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_CREATE_SESSION );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for bac_create_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ // Order: 1. sessionId, 2. tenantId, 3. userId, 4. password and 5. roles
+ /* must-have */
+ tag = ber_peek_tag( ber, &len );
+
+ // 1. SESSIONID
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 2. TENANT ID
+ if ( tag == LDAP_TAG_EXOP_RBAC_TENANT_ID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "tenant id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->tenantid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 3. USERID
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->uid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 4. PASSWORD
+ if ( tag == LDAP_TAG_EXOP_RBAC_AUTHTOK ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv);
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "authtok parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->authtok, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 5. ROLES
+ if ( tag == LDAP_TAG_EXOP_RBAC_ACTIVE_ROLE ) {
+ tag = ber_scanf( ber, "W", &reqp->roles);
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "role parse failed.\n" );
+ goto decoding_error;
+ }
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_create_session: "
+ "SUCCESS\n" );
+
+ *reqpp = reqp;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_create_session: "
+ "NO SUCCESS RC=%d\n", rc );
+
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+/*
+ * rbac_create_session:
+ * 1. authenticate user
+ * 2. evaluate pwd policy
+ * 3. create session
+ */
+static int
+rbac_create_session( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rbac_op = BER_BVC("CreateSession");
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ int rc = LDAP_SUCCESS;
+ rbac_session_t *sessp = NULL;
+ rbac_user_t *userp = NULL;
+ rbac_req_t *reqp = NULL;
+
+ rs->sr_err = slap_parse_rbac_create_session(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* read user entry */
+ userp = rbac_read_user( op, reqp );
+ if ( !userp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "unable to read user entry\n" );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "rbac_create_session: unable to read user entry";
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &userp->password ) ) {
+ /* if request is with pwd, authenticate the user */
+ rc = rbac_authenticate_user( op, userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "rbac_authenticate_user failed!\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ rs->sr_text = "rbac_create_session: invalid credential";
+ goto done;
+ }
+ /*
+ rbac_user_t *ui = op->o_callback->sc_private;
+ int pVal = ui->authz;
+ printf("password reset val=%d", pVal );
+*/
+
+ } else {
+ /* no pwd is provided, check whether the requesting session */
+ /* id has the access privilege to create a session on behalf */
+ /* of the user */
+ rc = rbac_create_session_acl_check( &reqp->sessid, userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "rbac_authenticate_user failed!\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_create_session: session permission denied";
+ goto done;
+ }
+ }
+
+ /* check user temporal constraint */
+ rc = rbac_user_temporal_constraint( userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "rbac_user_temporal_constraint() failed!\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_create_session: temporal constraint violation";
+ goto done;
+ }
+
+ sessp = rbac_alloc_session();
+ if ( !sessp ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_create_session: unable to allocate session";
+ goto done;
+ }
+
+ rc = activate_session_roles( sessp, reqp, userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "failed to activate roles to session!\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text =
+ "rbac_create_session: failed to activate roles into session";
+ goto done;
+ }
+
+ /* store uid and tenant id in session */
+ ber_dupbv( &sessp->userdn, &userp->dn );
+ ber_dupbv( &sessp->uid, &reqp->uid );
+ ber_dupbv( &sessp->tenantid, &reqp->tenantid );
+
+ /* register RBAC session */
+ rc = rbac_register_session( op, rs, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ rc = ber_printf( ber, "{tO}", LDAP_TAG_EXOP_RBAC_SESSION_ID,
+ &sessp->sessid );
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CREATE_SESSION.bv_val );
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ ber_free_buf( ber );
+
+done:;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CREATE_SESSION.bv_val );
+ /* generate audit log */
+ rbac_audit(
+ op, CreateSession, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+
+ //if (rs->sr_err != LDAP_SUCCESS) {
+ //send_ldap_result( op, rs );
+ //}
+
+ return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_check_access
+ */
+static int
+slap_parse_rbac_check_access(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ *text = NULL;
+ reqp = rbac_alloc_req( RBAC_REQ_CHECK_ACCESS );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for slap_parse_rbac_check_access";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_check_access exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ // sessionId is required:
+ tag = ber_peek_tag( ber, &len );
+ if ( tag != LDAP_TAG_EXOP_RBAC_SESSION_ID ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ } else {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // operationName is required:
+ if ( tag != LDAP_TAG_EXOP_RBAC_OPNAME ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ } else {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "opname parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->opname, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // objectName is required:
+ if ( tag != LDAP_TAG_EXOP_RBAC_OBJNAME ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ } else {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "objname parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->objname, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // objectId is optional:
+ if ( tag == LDAP_TAG_EXOP_RBAC_OBJID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "objid parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->objid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_check_access: "
+ "SUCCESS\n" );
+ *reqpp = reqp;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_check_access: "
+ "FAIL\n" );
+ rbac_free_req( reqp );
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+// checkAcess F (ALL)
+static int
+rbac_check_access( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ rbac_permission_t *permp = NULL;
+ rbac_constraint_t *cp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+ int found = 0;
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* read the permission using objectName and OpName */
+ permp = rbac_read_permission( op, reqp );
+ if ( !permp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "permission not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ // Convert the user-role constraint data from BerVarray to rbac_constraint_t format
+ cp = rbac_user_role_constraints( sessp->role_constraints );
+
+ // Now do the actual rbac checkAccess:
+ rc = rbac_check_session_permission( sessp, permp, cp );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_user_permission: "
+ "failed\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ rs->sr_err = rc;
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+
+ /* generate audit log */
+ rbac_audit( op, CheckAccess, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_permission( permp );
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+ rbac_free_constraints( cp );
+
+ return rs->sr_err;
+}
+
+// checkAcess A loop back
+static int
+rbac_check_accessA( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+
+ //rs->sr_err = slap_parse_rbac_check_access(op->ore_reqdata,
+ // &reqp, &rs->sr_text, NULL);
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ return rc;
+}
+
+// checkAcess B parse
+static int
+rbac_check_accessB( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ rbac_free_req( reqp );
+
+ return rc;
+}
+
+// checkAcess C - parse request & read session record
+static int
+rbac_check_accessC( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+ return rc;
+}
+
+// checkAcess D, parse, read perm
+static int
+rbac_check_accessD( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_permission_t *permp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ /*
+ sessp = rbac_session_byid(op, reqp);
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+*/
+
+ /* read the permission using objectName and OpName */
+ permp = rbac_read_permission( op, reqp );
+ if ( !permp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "permission not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ rbac_free_permission( permp );
+ rbac_free_req( reqp );
+
+ return rc;
+}
+
+// checkAcess E everything but the audit insert
+static int
+rbac_check_accessE( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ rbac_permission_t *permp = NULL;
+ rbac_constraint_t *cp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* read the permission using objectName and OpName */
+ permp = rbac_read_permission( op, reqp );
+ if ( !permp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "permission not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ // Convert the user-role constraint data from BerVarray to rbac_constraint_t format
+ cp = rbac_user_role_constraints( sessp->role_constraints );
+
+ // Now do the actual rbac checkAccess:
+ rc = rbac_check_session_permission( sessp, permp, cp );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_user_permission: "
+ "failed\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ rs->sr_err = rc;
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+
+ /* generate audit log */
+ //rbac_audit(op, CheckAccess, sessp, reqp, rs->sr_err,
+ // (char *) rs->sr_text);
+
+ rbac_free_permission( permp );
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+ rbac_free_constraints( cp );
+
+ return rs->sr_err;
+}
+
+/* check whether role exists and role assigned to the user */
+static int
+rbac_check_user_role(
+ rbac_req_t *reqp,
+ rbac_session_t *sessp,
+ rbac_user_t *userp )
+{
+ int rc = 0;
+ int i;
+
+ //assert(!BER_BVISEMPTY(&reqp->roles[0]));
+ assert( !BER_BVISEMPTY( &reqp->role ) );
+
+ for ( i = 0; !BER_BVISNULL( &userp->roles[i] ); i++ ) {
+ //if (!ber_bvstrcasecmp(&userp->roles[i], &reqp->roles[0])) {
+ if ( !ber_bvstrcasecmp( &userp->roles[i], &reqp->role ) ) {
+ rc = 1; /* found the match */
+ goto done;
+ }
+ }
+
+done:;
+
+ return rc;
+}
+
+/* check whether role exists and role assigned to the session */
+static int
+rbac_check_session_role( rbac_req_t *reqp, rbac_session_t *sessp )
+{
+ int rc = 0;
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ //if (!ber_bvstrcasecmp(&sessp->roles[i], &reqp->roles[0])) {
+ if ( !ber_bvstrcasecmp( &sessp->roles[i], &reqp->role ) ) {
+ rc = 1; /* found the match */
+ goto done;
+ }
+ }
+
+done:;
+
+ return rc;
+}
+
+/* make sure user is the owner of the session */
+static int
+rbac_check_user_session( rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ int rc = 0;
+
+ if ( BER_BVISNULL( &sessp->uid ) || BER_BVISNULL( &reqp->uid ) ||
+ sessp->uid.bv_len != reqp->uid.bv_len ) {
+ goto done;
+ }
+
+ if ( !strncasecmp(
+ sessp->uid.bv_val, reqp->uid.bv_val, reqp->uid.bv_len ) ) {
+ rc = 1;
+ goto done;
+ }
+
+done:;
+
+ return rc;
+}
+
+/*
+ * slap_parse_rbac_active_role
+ */
+static int
+slap_parse_rbac_active_role(
+ struct berval *in,
+ int add_or_drop_role,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_create_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( add_or_drop_role );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_add_drop_active_role exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ //if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID ) {
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv( &reqp->uid, &bv );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv( &reqp->sessid, &bv );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_RBAC_ROLE_NM_SESS ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ //tag = ber_scanf( ber, "W", &reqp->roles);
+ //tag = ber_scanf( ber, "m", &reqp->roles);
+ //tag = ber_scanf( ber, "m", &reqp->roles[0]);
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "role parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv( &reqp->role, &bv );
+ //ber_dupbv(&reqp->roles[0], &bv);
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+rbac_add_active_role( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rbac_op = BER_BVC("AddActiveRole");
+ rbac_req_t *reqp = NULL;
+ rbac_user_t *userp = NULL;
+ rbac_session_t *sessp;
+ int rc = LDAP_SUCCESS;
+
+ rs->sr_err = slap_parse_rbac_active_role( op->ore_reqdata,
+ RBAC_REQ_ADD_ACTIVE_ROLE, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "session not found\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_add_active_role: session not found";
+ goto done;
+ }
+
+ /* read user entry */
+ userp = rbac_read_user( op, reqp );
+ if ( !userp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "unable to read user entry\n" );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "rbac_add_active_role: unable to read user entry";
+ goto done;
+ }
+
+ /* make sure role exists and role assigned to the user */
+ if ( !rbac_check_user_role( reqp, sessp, userp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "role not assigned to the user\n" );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "rbac_add_active_role: role not assigned to the user";
+ goto done;
+ }
+
+ /* make sure user is the owner of the session */
+ if ( !rbac_check_user_session( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "user not owner of session\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_add_active_role: user not owner of the session";
+ goto done;
+ }
+
+ /* add the role to the session */
+ rc = rbac_session_add_role( op, sessp, reqp );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ rs->sr_text =
+ "rbac_add_active_role: role already activated in session";
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "role already activated in session\n" );
+ } else {
+ rs->sr_text = "rbac_add_active_role: unable to add role to session";
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "unable to add role to session\n" );
+ }
+ goto done;
+ }
+
+done:
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_ADD_ACTIVE_ROLE.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, AddActiveRole, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_session( sessp );
+ rbac_free_user( userp );
+ rbac_free_req( reqp );
+
+ return rs->sr_err;
+}
+
+static int
+rbac_drop_active_role( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("DropActiveRole");
+ rbac_session_t *sessp;
+ rbac_req_t *reqp = NULL;
+ int rc = LDAP_SUCCESS;
+
+ rs->sr_err = slap_parse_rbac_active_role( op->ore_reqdata,
+ RBAC_REQ_DROP_ACTIVE_ROLE, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &reqp->role ) || !sessp->roles ||
+ BER_BVISNULL( &sessp->roles[0] ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "unavailable role\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* make sure role exists and role assigned to the user */
+ if ( !rbac_check_session_role( reqp, sessp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "role not assigned to session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* make sure user is the owner of the session */
+ if ( !rbac_check_user_session( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "user not owner of session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_drop_active_role: user not owner of the session";
+ goto done;
+ }
+
+ /* drop the role to the session */
+ rc = rbac_session_drop_role( op, sessp, reqp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "unable to drop active role from session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_drop_active_role: unable to drop role from session";
+ goto done;
+ }
+
+done:
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_DROP_ACTIVE_ROLE.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, DropActiveRole, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+
+ return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_delete_session
+ */
+static int
+slap_parse_rbac_delete_session(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_delete_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_DELETE_SESSION );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_delete_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+ struct berval uid;
+ tag = ber_scanf( ber, "m", &uid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->uid, &uid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ //tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+ struct berval sessid;
+ tag = ber_scanf( ber, "m", &sessid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &sessid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+rbac_delete_session( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("DeleteSession");
+ rbac_session_t *sessp = NULL;
+ rbac_req_t *reqp = NULL;
+ int rc;
+
+ rs->sr_err = slap_parse_rbac_delete_session(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_delete_session: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* checking whether the session is owned by the user */
+ if ( !rbac_is_session_owner( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_delete_session: "
+ "session not owned by user\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_int_delete_session( op, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_int_delete_session: "
+ "unable to delete session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:;
+
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_DELETE_SESSION.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, DeleteSession, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+
+ return rs->sr_err;
+}
+
+/* returns the permissions associated with a session */
+static int
+rbac_session_permissions( Operation *op, SlapReply *rs, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("SessionPermissions");
+ rbac_session_t *sessp;
+
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_permissions: "
+ "session id not found\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rs->sr_err = rbac_int_session_permissions( op, rs, reqp, sessp );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_permissions: "
+ "permissions not found\n" );
+ goto done;
+ }
+
+done:;
+ return rs->sr_err;
+}
+
+/* extract session permission info from op */
+int
+rbac_search_parse_session_permissions_req(
+ Operation *op,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval *sessid = NULL;
+ rbac_req_t *reqp = NULL;
+ *text = NULL;
+ struct berval rbac_session_id = BER_BVC("sessionID");
+ struct berval rbac_session_permissions_attr =
+ BER_BVC("sessionPermissions");
+ AttributeDescription *ad = NULL;
+ Filter *f;
+
+ /* check simple assertion (sessionID=<session id>) */
+ f = op->ors_filter;
+ ad = f->f_ava->aa_desc;
+ if ( !ad || ber_bvstrcasecmp( &rbac_session_id, &ad->ad_cname ) ) {
+ goto done;
+ }
+ sessid = &f->f_ava->aa_value;
+
+ if ( !rbac_is_valid_session_id( sessid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search_parse_session_permissions_req: "
+ "invalid session id\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* check requested attr */
+
+ if ( !op->oq_search.rs_attrs ||
+ BER_BVISNULL( &op->oq_search.rs_attrs[0].an_name ) ||
+ ber_bvstrcasecmp( &op->oq_search.rs_attrs[0].an_name,
+ &rbac_session_permissions_attr ) ||
+ !BER_BVISNULL( &op->oq_search.rs_attrs[1].an_name ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search_parse_session_permissions_req: "
+ "only sessionPermissions allowed\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_SESSION_PERMISSIONS );
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_session_permissions req";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* retrieve session id from search filter */
+ ber_dupbv_x( &reqp->sessid, sessid, ctx );
+
+done:;
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ return rc;
+}
+
+static int
+rbac_search( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_ANY, "rbac_search entry\n" );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+static int
+rbac_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_req_t *reqp = NULL;
+ int rc = SLAP_CB_CONTINUE;
+
+ only session_permissions is implemented for now
+ rc = rbac_search_parse_session_permissions_req(
+ op, &reqp, &rs->sr_text, NULL );
+ if ( !reqp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search: "
+ "invalid search for session permissions\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_session_permissions( op, rs, reqp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search: "
+ "session permissions failed\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ send_ldap_result( op, rs );
+
+ return rc;
+}
+*/
+
+static struct exop {
+ struct berval oid;
+ BI_op_extended *extended;
+} rbac_exop_table[] = {
+ { BER_BVC(LDAP_RBAC_EXOP_CREATE_SESSION), rbac_create_session },
+ { BER_BVC(LDAP_RBAC_EXOP_CHECK_ACCESS), rbac_check_access },
+ { BER_BVC(LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE), rbac_add_active_role },
+ { BER_BVC(LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE), rbac_drop_active_role },
+ { BER_BVC(LDAP_RBAC_EXOP_DELETE_SESSION), rbac_delete_session },
+ { BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES), rbac_session_roles },
+
+ { BER_BVNULL, NULL }
+};
+
+static int
+rbac_add( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_bind( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_compare( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_delete( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_modify( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_extended( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int rc = SLAP_CB_CONTINUE;
+ int i;
+
+ for ( i = 0; rbac_exop_table[i].extended != NULL; i++ ) {
+ if ( bvmatch( &rbac_exop_table[i].oid, &op->oq_extended.rs_reqoid ) ) {
+ rc = rbac_exop_table[i].extended( op, rs );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+ case SLAP_CB_CONTINUE:
+ case SLAPD_ABANDON:
+ return rc;
+ default:
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int
+rbac_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+
+ return 0;
+}
+
+static int
+rbac_db_open( BackendDB *be, ConfigReply *cr )
+{
+ int rc = LDAP_SUCCESS;
+
+ rc = rbac_initialize_tenants( be, cr );
+
+ return rc;
+}
+
+static int
+rbac_db_close( BackendDB *be, ConfigReply *cr )
+{
+ return 0;
+}
+
+int
+rbac_initialize()
+{
+ int rc;
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_CREATE_SESSION,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_create_session, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_create_session exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_CHECK_ACCESS,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_check_access, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_check_access exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_ADD_ACTIVE_ROLE,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_add_active_role, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_add_active_role exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_DROP_ACTIVE_ROLE,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_drop_active_role, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_drop_active_role exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_DELETE_SESSION,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_delete_session, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_delete_session exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_SESSION_ROLES,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_session_roles, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_session_roles exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rbac.on_bi.bi_type = "rbac";
+ rbac.on_bi.bi_db_init = rbac_db_init;
+ rbac.on_bi.bi_db_open = rbac_db_open;
+ rbac.on_bi.bi_db_close = rbac_db_close;
+
+ rbac.on_bi.bi_op_add = rbac_add;
+ rbac.on_bi.bi_op_bind = rbac_bind;
+ rbac.on_bi.bi_op_compare = rbac_compare;
+ rbac.on_bi.bi_op_delete = rbac_delete;
+ rbac.on_bi.bi_op_modify = rbac_modify;
+ rbac.on_bi.bi_op_search = rbac_search;
+ rbac.on_bi.bi_extended = rbac_extended;
+ rbac.on_bi.bi_cf_ocs = rbac_ocs;
+
+ /* rbac.on_bi.bi_connection_destroy = rbac_connection_destroy; */
+
+ rc = config_register_schema( rbaccfg, rbac_ocs );
+ if ( rc ) return rc;
+
+ rc = rbac_initialize_repository();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ return overlay_register( &rbac );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return rbac_initialize();
+}
diff --git a/contrib/slapd-modules/rbac/rbac.h b/contrib/slapd-modules/rbac/rbac.h
new file mode 100644
index 0000000..4461236
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbac.h
@@ -0,0 +1,402 @@
+/* rbac.h - */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ *
+ */
+
+#ifndef RBAC_H
+#define RBAC_H
+
+LDAP_BEGIN_DECL
+
+#include "ldap_rbac.h"
+
+#define USE_NEW_THREAD_CONTEXT 1
+#define RBAC_BUFLEN 1024
+
+/* tenant initialization op */
+#define INIT_AUDIT_CONTAINER 0x01
+#define INIT_SESSION_CONTAINER 0x02
+
+typedef struct rbac_ad {
+ int type;
+ struct berval attr;
+ AttributeDescription **ad;
+} rbac_ad_t;
+
+/* RBAC AttributeDescriptions */
+struct slap_rbac_internal_schema {
+ /* slapd schema */
+ AttributeDescription *ad_uid;
+
+ /* RBAC tenant */
+ AttributeDescription *ad_tenant_id;
+
+ /* RBAC sessions */
+ AttributeDescription *ad_session_id;
+ AttributeDescription *ad_session_user_dn;
+ AttributeDescription *ad_session_roles;
+ AttributeDescription *ad_session_role_constraints;
+
+ /* RBAC session permissions */
+ AttributeDescription *ad_permission_opname;
+ AttributeDescription *ad_permission_objname;
+ AttributeDescription *ad_permission_rolename;
+
+ /* RBAC audit */
+ AttributeDescription *ad_audit_op; /* rbac op: create_session */
+ AttributeDescription *ad_audit_id;
+ AttributeDescription *ad_audit_roles;
+ AttributeDescription *ad_audit_requested_roles;
+ AttributeDescription *ad_audit_timestamp;
+ AttributeDescription *ad_audit_resources;
+ AttributeDescription *ad_audit_objects;
+ AttributeDescription *ad_audit_operations; /* resource ops */
+ AttributeDescription *ad_audit_result;
+ AttributeDescription *ad_audit_properties;
+ AttributeDescription *ad_audit_messages;
+
+ /* RBAC session attributes */
+ AttributeName *session_attrs;
+};
+
+extern struct slap_rbac_internal_schema slap_rbac_schema;
+
+/* attributes in tenant repository */
+struct slap_rbac_tenant_schema {
+ /* user role assignments, role constraints, and user constraint */
+ AttributeDescription *ad_role;
+ AttributeDescription *ad_role_constraint;
+ AttributeDescription *ad_user_constraint;
+ AttributeDescription *ad_uid;
+
+ /* session permission */
+ AttributeDescription *ad_permission_users;
+ AttributeDescription *ad_permission_roles;
+ AttributeDescription *ad_permission_objname;
+ AttributeDescription *ad_permission_opname;
+
+ /* the list of attributes when doing searches in the jts repo */
+ AttributeName *user_attrs;
+ AttributeName *perm_attrs; /* attrs to retrieve for check access */
+ AttributeName *session_perm_attrs; /* attrs for session permissions */
+
+ /* the corresponding list of attribute description mapping */
+ rbac_ad_t *user_ads;
+ rbac_ad_t *permission_ads;
+ rbac_ad_t *session_permissions_ads;
+};
+
+extern struct slap_rbac_tenant_schema slap_rbac_jts_schema;
+
+/* types of RBAC requests */
+typedef struct rbac_request {
+ int req_type;
+ struct berval sessid;
+ struct berval tenantid;
+
+ /* session creation */
+ struct berval uid;
+ struct berval authtok;
+ BerVarray roles;
+ struct berval role;
+
+ /* check access */
+ struct berval opname;
+ struct berval objname;
+ struct berval objid;
+} rbac_req_t;
+
+typedef struct rbac_constraint {
+ struct berval name; /* user name or role name */
+ int allowed_inactivity; /* secs */
+ int begin_time; /* secs */
+ int end_time; /* secs */
+ lutil_timet begin_date;
+ lutil_timet end_date;
+ lutil_timet begin_lock_date;
+ lutil_timet end_lock_date;
+ int day_mask;
+ struct rbac_constraint *next;
+} rbac_constraint_t;
+
+/* holds RBAC info */
+typedef struct tenant_info {
+ struct berval tid; /* tenant id */
+ struct berval admin;
+ struct berval pwd;
+ struct berval users_basedn;
+ struct berval roles_basedn;
+ struct berval audit_basedn;
+ struct berval permissions_basedn;
+ struct berval sessions_basedn;
+ struct berval session_admin;
+ struct berval session_admin_pwd;
+ struct slap_rbac_tenant_schema *schema;
+} tenant_info_t;
+
+typedef struct rbac_tenant {
+ tenant_info_t tenant_info;
+ struct rbac_tenant *next;
+} rbac_tenant_t;
+
+/* for RBAC callback */
+typedef struct rbac_callback_info {
+ tenant_info_t *tenantp;
+ void *private;
+} rbac_callback_info_t;
+
+/* RBAC user */
+typedef struct rbac_user {
+ struct berval tenantid;
+ struct berval uid;
+ struct berval dn;
+ struct berval constraints;
+ struct berval password;
+ struct berval msg;
+ int authz; /* flag for bind (pwd policy) info */
+ BerVarray roles;
+ BerVarray role_constraints;
+#if 0 /* additional parameters from Fortress */
+ private String userId;
+ @XmlElement(nillable = true)
+ private char[] password;
+ @XmlElement(nillable = true)
+ private char[] newPassword;
+ private String internalId;
+ @XmlElement(nillable = true)
+ private List<UserRole> roles;
+ @XmlElement(nillable = true)
+ private List<UserAdminRole> adminRoles;
+ private String pwPolicy;
+ private String cn;
+ private String sn;
+ private String dn;
+ private String ou;
+ private String description;
+ private String beginTime;
+ private String endTime;
+ private String beginDate;
+ private String endDate;
+ private String beginLockDate;
+ private String endLockDate;
+ private String dayMask;
+ private String name;
+ private int timeout;
+ private boolean reset;
+ private boolean locked;
+ private Boolean system;
+ @XmlElement(nillable = true)
+ private Props props = new Props();
+ @XmlElement(nillable = true)
+ private Address address;
+ @XmlElement(nillable = true)
+ private List<String> phones;
+ @XmlElement(nillable = true)
+ private List<String> mobiles;
+ @XmlElement(nillable = true)
+ private List<String> emails;
+#endif /* 0 */
+} rbac_user_t;
+
+enum {
+ RBAC_NONE = 0,
+ RBAC_TENANT,
+ RBAC_TENANT_ID,
+ RBAC_USERS_BASE_DN,
+ RBAC_ROLES_BASE_DN,
+ RBAC_PERMISSIONS_BASE_DN,
+ RBAC_ADMIN_DN,
+ RBAC_ADMIN_PWD,
+ RBAC_SESSIONS_BASE_DN,
+ RBAC_SESSION_ADMIN_DN,
+ RBAC_SESSION_ADMIN_PWD,
+ RBAC_ROLE_ASSIGNMENT,
+ RBAC_ROLE_CONSTRAINTS,
+ RBAC_USER_CONSTRAINTS,
+ RBAC_UID,
+ RBAC_USERS,
+ RBAC_ROLES,
+ RBAC_OBJ_NAME,
+ RBAC_OP_NAME,
+ RBAC_ROLE_NAME,
+ RBAC_SESSION_ID,
+ RBAC_USER_DN,
+ RBAC_AUDIT_ROLES,
+ RBAC_AUDIT_RESOURCES,
+ RBAC_AUDIT_RESULT,
+ RBAC_AUDIT_TIMESTAMP,
+ RBAC_AUDIT_PROPERTIES,
+ RBAC_AUDIT_OP,
+ RBAC_AUDIT_ID,
+ RBAC_AUDIT_REQUESTED_ROLES,
+ RBAC_AUDIT_OBJS,
+ RBAC_AUDIT_OPS,
+ RBAC_AUDIT_MSGS,
+ RBAC_LAST
+};
+
+enum {
+ RBAC_DEFAULT_TENANT_ID = RBAC_LAST,
+ RBAC_DEFAULT_USERS_BASE_DN,
+ RBAC_DEFAULT_PERMISSIONS_BASE_DN,
+ RBAC_DEFAULT_ROLES_BASE_DN,
+ RBAC_DEFAULT_SESSIONS_BASE_DN,
+ RBAC_DEFAULT_AUDIT_BASE_DN
+};
+
+typedef struct rbac_user_idlist {
+ char *user_id;
+ struct rbac_user_idlist *next;
+} rbac_user_idlist_t;
+
+/* RBAC sessions */
+#define RBAC_SESSION_RDN_EQ "rbacSessid="
+#define RBAC_AUDIT_RDN_EQ "rbacAuditId="
+
+typedef struct rbac_session {
+ rbac_user_t *user;
+ struct berval tenantid;
+ struct berval sessid;
+ struct berval uid;
+ struct berval userdn;
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+ struct berval sessdn;
+ long last_access;
+ int timeout;
+ int warning_id;
+ int error_id;
+ int grace_logins;
+ int expiration_secs;
+ int is_authenticated; /* boolean */
+ struct berval message;
+ BerVarray roles;
+ BerVarray role_constraints;
+} rbac_session_t;
+
+/* RBAC roles */
+typedef struct rbac_role {
+ char *name;
+ char *description;
+ struct rbac_role *parent;
+ struct rbac_role *next;
+} rbac_role_t;
+
+typedef struct rbac_role_list {
+ char *name;
+ struct rbac_role_list *next;
+} rbac_role_list_t;
+
+/* RBAC permissions */
+typedef struct rbac_permission {
+ struct berval dn;
+ int admin; /* boolean */
+ struct berval internalId;
+ BerVarray opName;
+ BerVarray objName;
+ struct berval objectId;
+ struct berval abstractName;
+ struct berval type;
+ BerVarray roles;
+ BerVarray uids;
+ struct rbac_permission *next;
+} rbac_permission_t;
+
+/* RBAC Audit */
+typedef enum {
+ CreateSession = 0,
+ CheckAccess,
+ AddActiveRole,
+ DropActiveRole,
+ SessionPermissions,
+ DeleteSession,
+ SessionRoles
+} audit_op_t;
+
+/* function prototypes */
+
+int rbac_initialize_repository( void );
+int rbac_initialize_tenants( BackendDB *be, ConfigReply *cr );
+
+/* RBAC tenant information */
+tenant_info_t *rbac_tid2tenant( struct berval *tid );
+
+rbac_req_t *rbac_alloc_req( int type );
+void rbac_free_req( rbac_req_t *reqp );
+
+rbac_user_t *rbac_read_user( Operation *op, rbac_req_t *rabc_reqp );
+int rbac_authenticate_user( Operation *op, rbac_user_t *user );
+int rbac_user_temporal_constraint( rbac_user_t *userp );
+void rbac_free_user( rbac_user_t *user );
+
+rbac_session_t *rbac_alloc_session( void );
+int rbac_is_valid_session_id( struct berval *sessid );
+rbac_session_t *rbac_session_byid( Operation *op, rbac_req_t *reqp );
+int rbac_is_session_owner( rbac_session_t *sessp, rbac_req_t *reqp );
+int rbac_register_session( Operation *op, SlapReply *rs, rbac_session_t *sess );
+int rbac_int_delete_session( Operation *op, rbac_session_t *sessp );
+int rbac_session_add_role(
+ Operation *op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp );
+int rbac_session_drop_role(
+ Operation *op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp );
+int rbac_int_session_permissions(
+ Operation *op,
+ SlapReply *rs,
+ rbac_req_t *reqp,
+ rbac_session_t *sessp );
+int activate_session_roles(
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ rbac_user_t *userp );
+void rbac_free_session( rbac_session_t *sessp );
+
+rbac_constraint_t *rbac_user_role_constraints( BerVarray values );
+rbac_constraint_t *rbac_role2constraint(
+ struct berval *role,
+ rbac_constraint_t *role_constraints );
+rbac_constraint_t *rbac_bv2constraint( struct berval *bv );
+int rbac_check_time_constraint( rbac_constraint_t *cp );
+void rbac_free_constraint( rbac_constraint_t *cp );
+void rbac_free_constraints( rbac_constraint_t *constraints );
+
+rbac_permission_t *rbac_read_permission( Operation *op, rbac_req_t *rbac_reqp );
+int rbac_check_session_permission(
+ rbac_session_t *sessp,
+ rbac_permission_t *permp,
+ rbac_constraint_t *role_constraints );
+void rbac_free_permission( rbac_permission_t *permp );
+
+/* audit functions */
+void rbac_audit(
+ Operation *op,
+ audit_op_t rbac_op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ int result,
+ char *msg );
+
+/* acl functions */
+int rbac_create_session_acl_check( struct berval *sessid, rbac_user_t *userp );
+
+void rbac_to_lower( struct berval *bv );
+
+LDAP_END_DECL
+
+#endif /* RBAC_H */
diff --git a/contrib/slapd-modules/rbac/rbacacl.c b/contrib/slapd-modules/rbac/rbacacl.c
new file mode 100644
index 0000000..269dcf5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacacl.c
@@ -0,0 +1,37 @@
+/* rbacacl.c - RBAC ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+int
+rbac_create_session_acl_check( struct berval *sessid, rbac_user_t *userp )
+{
+ int rc = LDAP_SUCCESS;
+
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/rbacaudit.c b/contrib/slapd-modules/rbac/rbacaudit.c
new file mode 100644
index 0000000..ef04ece
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacaudit.c
@@ -0,0 +1,233 @@
+/* rbacaudit.c - RBAC Audit */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static struct rbac_audit_op {
+ audit_op_t op;
+ struct berval op_bv;
+} rbac_audit_ops[] = {
+ { CreateSession, BER_BVC("CreateSession") },
+ { CheckAccess, BER_BVC("CheckAccess") },
+ { AddActiveRole, BER_BVC("AddActiveRole") },
+ { DropActiveRole, BER_BVC("DropActiveRole") },
+ { SessionPermissions, BER_BVC("SessionPermissions") },
+ { DeleteSession, BER_BVC("DeleteSession") },
+ { SessionRoles, BER_BVC("SessionRoles") },
+
+ { -1, BER_BVNULL }
+};
+
+static int
+rbac_audit_fake_cb( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_ANY, "rbac_audit_fake_cb\n" );
+
+ return 0;
+}
+
+void
+rbac_audit(
+ Operation *op,
+ audit_op_t rbac_op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ int result,
+ char *msg )
+{
+ int op_idx, rc = LDAP_SUCCESS;
+ int found = 0;
+ struct berval timestamp;
+ tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Entry *e = NULL;
+ struct berval auditObjectClass = BER_BVC("rbacAudit");
+ struct berval auditResultSuccess = BER_BVC("success");
+ struct berval auditResultFailed = BER_BVC("failed");
+ struct berval bv, rdn, nrdn;
+ char rdnbuf[RBAC_BUFLEN];
+ time_t now;
+ char nowstr[LDAP_LUTIL_GENTIME_BUFSIZE];
+
+ for ( op_idx = 0; rbac_audit_ops[op_idx].op != -1; op_idx++ ) {
+ if ( rbac_op == rbac_audit_ops[op_idx].op ) {
+ /* legit audit op */
+ found = 1;
+ break;
+ }
+ }
+
+ if ( !found ) goto done;
+
+ e = entry_alloc();
+
+ /* audit timestamp */
+ now = slap_get_time(); /* stored for later consideration */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ /* construct audit record DN; FIXME: random() call */
+ sprintf( rdnbuf, "%s%d", RBAC_AUDIT_RDN_EQ, (int)op->o_tid );
+ strcat( rdnbuf, "-" );
+ strncat( rdnbuf, timestamp.bv_val, timestamp.bv_len );
+ bv.bv_val = &rdnbuf[0];
+ bv.bv_len = strlen( &rdnbuf[0] );
+
+ rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_audit: "
+ "unable to normalize audit rDN (rc=%d)\n", rc );
+ goto done;
+ }
+
+ /* FIXME: audit_basedn should have been normalized */
+ build_new_dn( &e->e_name, &tenantp->audit_basedn, &rdn, NULL );
+ build_new_dn( &e->e_nname, &tenantp->audit_basedn, &nrdn, NULL );
+
+ ch_free( rdn.bv_val );
+ ch_free( nrdn.bv_val );
+
+ /* add timestamp */
+ attr_merge_one( e, slap_rbac_schema.ad_audit_timestamp, &timestamp, NULL );
+
+ /* add rbac audit objectClass */
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &auditObjectClass, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &auditObjectClass, NULL );
+
+ /* audit op */
+ attr_merge_one( e, slap_rbac_schema.ad_audit_op,
+ &rbac_audit_ops[op_idx].op_bv, NULL );
+
+ /* userid */
+ if ( sessp && !BER_BVISNULL( &sessp->uid ) ) {
+ attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+ }
+
+ /* session id */
+
+ if ( sessp && !BER_BVISNULL( &sessp->sessid ) ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ struct berval sessid = BER_BVC("rbacSessid");
+
+ rc = slap_bv2ad( &sessid, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ attr_merge_one( e, ad, &sessp->sessid, NULL );
+ }
+
+ /* audit result */
+ attr_merge_one( e, slap_rbac_schema.ad_audit_result,
+ result == LDAP_SUCCESS ? &auditResultSuccess : &auditResultFailed,
+ NULL );
+
+ switch ( rbac_op ) {
+ case CreateSession:
+ /* audit roles */
+ if ( sessp && sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+ NULL );
+ }
+ if ( reqp && reqp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+ reqp->roles, NULL );
+ }
+ break;
+
+ case CheckAccess:
+ if ( sessp && sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+ NULL );
+ }
+ if ( reqp && !BER_BVISEMPTY( &reqp->opname ) ) {
+ attr_merge_one( e, slap_rbac_schema.ad_audit_operations,
+ &reqp->opname, NULL );
+ }
+ if ( reqp && !BER_BVISEMPTY( &reqp->objname ) ) {
+ attr_merge_one( e, slap_rbac_schema.ad_audit_objects,
+ &reqp->objname, NULL );
+ }
+ break;
+
+ case AddActiveRole:
+ if ( reqp && reqp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+ reqp->roles, NULL );
+ }
+ break;
+
+ case DropActiveRole:
+ /* audit roles */
+ if ( reqp && reqp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+ reqp->roles, NULL );
+ }
+ break;
+
+ case SessionPermissions:
+ if ( sessp && sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+ NULL );
+ }
+ break;
+
+ case DeleteSession:
+ case SessionRoles:
+ default:
+ break;
+ }
+
+ /* record the audit record */
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_audit_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_protocol = LDAP_VERSION3;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+ op2.o_dn = op2.o_bd->be_rootdn;
+ op2.o_ndn = op2.o_bd->be_rootndn;
+
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_add( &op2, &rs2 );
+
+done:
+ if ( e ) entry_free( e );
+
+ return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacperm.c b/contrib/slapd-modules/rbac/rbacperm.c
new file mode 100644
index 0000000..e1f6d79
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacperm.c
@@ -0,0 +1,233 @@
+/* rbacperm.c - RBAC permission */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static int
+rbac_read_permission_cb( Operation *op, SlapReply *rs )
+{
+ rbac_callback_info_t *cbp = op->o_callback->sc_private;
+ rbac_ad_t *permission_ads;
+ rbac_permission_t *permp;
+ int i;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ assert( cbp );
+
+ permp = ch_calloc( 1, sizeof(rbac_permission_t) );
+ permission_ads = cbp->tenantp->schema->permission_ads;
+
+ ber_dupbv( &permp->dn, &rs->sr_entry->e_name );
+ for ( i = 0; !BER_BVISNULL( &permission_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+ attr = attr_find( rs->sr_entry->e_attrs, *permission_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( permission_ads[i].type ) {
+ case RBAC_USERS:
+ ber_bvarray_dup_x( &permp->uids, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLES:
+ ber_bvarray_dup_x( &permp->roles, attr->a_nvals, NULL );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ cbp->private = (void *)permp;
+
+ return 0;
+}
+
+/*
+ * check whether roles assigned to a user allows access to roles in
+ * a permission, subject to role constraints
+ */
+int
+rbac_check_session_permission(
+ rbac_session_t *sessp,
+ rbac_permission_t *permp,
+ rbac_constraint_t *role_constraints )
+{
+ int rc = LDAP_INSUFFICIENT_ACCESS;
+ rbac_constraint_t *cp = NULL;
+ int i, j;
+
+ if ( !sessp->roles || !permp->roles ) goto done;
+
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ for ( j = 0; !BER_BVISNULL( &permp->roles[j] ); j++ ) {
+ if ( ber_bvstrcasecmp( &sessp->roles[i], &permp->roles[j] ) == 0 ) {
+ /* role temporal constraint */
+ cp = rbac_role2constraint( &permp->roles[j], role_constraints );
+ if ( !cp || rbac_check_time_constraint( cp ) == LDAP_SUCCESS ) {
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+ }
+ }
+ }
+done:;
+ return rc;
+}
+
+rbac_permission_t *
+rbac_read_permission( Operation *op, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_callback_info_t rbac_cb;
+ int rc = LDAP_SUCCESS;
+ char fbuf[1024];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ char permbuf[1024];
+ struct berval permdn = { sizeof(permbuf), permbuf };
+ struct berval permndn = BER_BVNULL;
+ char pcls[] = "(objectClass=ftOperation)";
+ SlapReply rs2 = { REP_RESULT };
+ slap_callback cb = { 0 };
+ tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+#if 0 /* check valid object name and op name */
+ if ( !is_valid_opname( &reqp->opname ) ||
+ !is_valid_objname( &reqp->objname ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "invalid opname (%s) or objname (%s)\n",
+ reqp->opname.bv_val, reqp->objname.bv_val );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+#endif
+
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "missing tenant information\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if ( reqp->objid.bv_val != NULL ) {
+ permdn.bv_len = snprintf( permdn.bv_val, permdn.bv_len,
+ "ftObjId=%s+ftOpNm=%s,ftObjNm=%s,%s", reqp->objid.bv_val,
+ reqp->opname.bv_val, reqp->objname.bv_val,
+ tenantp->permissions_basedn.bv_val );
+ } else {
+ permdn.bv_len = snprintf( permdn.bv_val, permdn.bv_len,
+ "ftOpNm=%s,ftObjNm=%s,%s", reqp->opname.bv_val,
+ reqp->objname.bv_val, tenantp->permissions_basedn.bv_val );
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &permdn, &permndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "unable to normalize permission DN\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ filter.bv_val = pcls;
+ filter.bv_len = strlen( pcls );
+ rbac_cb.tenantp = tenantp;
+ rbac_cb.private = NULL;
+
+ Operation op2 = *op;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_read_permission_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->admin;
+ op2.o_ndn = tenantp->admin;
+ op2.o_req_dn = permdn;
+ op2.o_req_ndn = permndn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_attrs = tenantp->schema->perm_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_attrsonly = 0;
+ op2.ors_limit = NULL;
+ op2.o_bd = frontendDB;
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:;
+ ch_free( permndn.bv_val );
+
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_permission((rbac_permission_t *)rbac_cb.private);
+ }
+
+ return (rbac_permission_t *)rbac_cb.private;
+}
+
+void
+rbac_free_permission( rbac_permission_t *permp )
+{
+ if ( !permp ) return;
+
+ if ( !BER_BVISNULL( &permp->dn ) ) {
+ ber_memfree( permp->dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &permp->internalId ) ) {
+ ber_memfree( permp->internalId.bv_val );
+ }
+
+ if ( permp->opName ) {
+ ber_bvarray_free( permp->opName );
+ }
+
+ if ( permp->objName ) {
+ ber_bvarray_free( permp->objName );
+ }
+
+ if ( !BER_BVISNULL( &permp->objectId ) ) {
+ ber_memfree( permp->objectId.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &permp->abstractName ) ) {
+ ber_memfree( permp->abstractName.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &permp->type ) ) {
+ ber_memfree( permp->type.bv_val );
+ }
+
+ if ( permp->roles ) {
+ ber_bvarray_free( permp->roles );
+ }
+
+ if ( permp->uids ) {
+ ber_bvarray_free( permp->uids );
+ }
+ ch_free( permp );
+
+ return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacreq.c b/contrib/slapd-modules/rbac/rbacreq.c
new file mode 100644
index 0000000..9942a00
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacreq.c
@@ -0,0 +1,89 @@
+/* rbacreq.c - RBAC requests */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+rbac_req_t *
+rbac_alloc_req( int type )
+{
+ rbac_req_t *reqp = NULL;
+
+ reqp = ch_calloc( 1, sizeof(rbac_req_t) );
+
+ reqp->req_type = type;
+ BER_BVZERO( &reqp->sessid );
+ BER_BVZERO( &reqp->tenantid );
+ /* session creation */
+ BER_BVZERO( &reqp->uid );
+ BER_BVZERO( &reqp->authtok );
+ reqp->roles = NULL;
+ /* check access */
+ BER_BVZERO( &reqp->opname );
+ BER_BVZERO( &reqp->objname );
+ BER_BVZERO( &reqp->objid );
+ /* add/drop role */
+ BER_BVZERO( &reqp->role );
+
+ return reqp;
+}
+
+void
+rbac_free_req( rbac_req_t *reqp )
+{
+ if ( !reqp ) return;
+
+ if ( !BER_BVISNULL( &reqp->sessid ) )
+ ber_memfree( reqp->sessid.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->tenantid ) )
+ ber_memfree( reqp->tenantid.bv_val );
+
+ /* session creation */
+ if ( !BER_BVISNULL( &reqp->uid ) )
+ ber_memfree( reqp->uid.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->authtok ) )
+ ber_memfree( reqp->authtok.bv_val );
+
+ if ( reqp->roles )
+ ber_bvarray_free( reqp->roles );
+
+ /* check access */
+ if ( !BER_BVISNULL( &reqp->opname ) )
+ ber_memfree( reqp->opname.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->objname ) )
+ ber_memfree( reqp->objname.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->objid ) )
+ ber_memfree( reqp->objid.bv_val );
+
+ ch_free( reqp );
+
+ return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacsess.c b/contrib/slapd-modules/rbac/rbacsess.c
new file mode 100644
index 0000000..d18e312
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacsess.c
@@ -0,0 +1,999 @@
+/* rbacsess.c - RBAC session */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static slap_callback nullsc = { NULL, NULL, NULL, NULL };
+
+extern rbac_ad_t rbac_session_permission_ads[];
+extern rbac_ad_t rbac_session_ads[];
+
+struct berval slapo_session_oc = BER_BVC("rbacSession");
+
+typedef struct session_perm_req {
+ Operation *op;
+ SlapReply *rs;
+ struct berval *sessid;
+ struct berval permdn;
+ tenant_info_t *tenantp;
+} session_perm_req_t;
+
+static int
+rbac_sess_fake_cb( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_ANY, "rbac_sess_fake_cb\n" );
+
+ return 0;
+}
+
+static int
+rbac_send_session_permission(
+ session_perm_req_t *sess_perm_reqp,
+ rbac_permission_t *perm )
+{
+ int i, rc = LDAP_SUCCESS;
+ Operation *op = sess_perm_reqp->op;
+ SlapReply *rs = sess_perm_reqp->rs;
+ struct berval *sessidp = sess_perm_reqp->sessid;
+ struct berval *permdnp = &sess_perm_reqp->permdn;
+
+ Entry *e = entry_alloc();
+ e->e_attrs = NULL;
+ ber_dupbv( &e->e_name, permdnp );
+ ber_dupbv( &e->e_nname, permdnp );
+ e->e_private = NULL;
+ attr_merge_one( e, slap_rbac_schema.ad_session_id, sessidp, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
+ switch ( rbac_session_permission_ads[i].type ) {
+ case RBAC_OP_NAME:
+ attr_merge_one( e, *rbac_session_permission_ads[i].ad,
+ &perm->opName[0], NULL );
+ break;
+ case RBAC_OBJ_NAME:
+ attr_merge_one( e, *rbac_session_permission_ads[i].ad,
+ &perm->objName[0], NULL );
+ break;
+ case RBAC_ROLE_NAME:
+ attr_merge( e, *rbac_session_permission_ads[i].ad, perm->roles,
+ NULL );
+ break;
+ default:
+ break;
+ }
+ }
+
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ rc = send_search_entry( op, rs );
+
+ return rc;
+}
+
+static int
+rbac_session_permissions_cb( Operation *op, SlapReply *rs )
+{
+ session_perm_req_t *sess_perm_reqp = op->o_callback->sc_private;
+ tenant_info_t *tenantp = NULL;
+ rbac_permission_t *permp = NULL;
+ rbac_ad_t *session_permissions_ads;
+ int i;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ assert( sess_perm_reqp );
+
+ tenantp = sess_perm_reqp->tenantp;
+ session_permissions_ads = tenantp->schema->session_permissions_ads;
+
+ permp = ch_calloc( 1, sizeof(rbac_permission_t) );
+
+ for ( i = 0; !BER_BVISNULL( &session_permissions_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+
+ attr = attr_find(
+ rs->sr_entry->e_attrs, *session_permissions_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( session_permissions_ads[i].type ) {
+ case RBAC_USERS:
+ ber_bvarray_dup_x( &permp->uids, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLES:
+ ber_bvarray_dup_x( &permp->roles, attr->a_nvals, NULL );
+ break;
+ case RBAC_OBJ_NAME:
+ ber_bvarray_dup_x( &permp->objName, attr->a_nvals, NULL );
+ break;
+ case RBAC_OP_NAME:
+ ber_bvarray_dup_x( &permp->opName, attr->a_nvals, NULL );
+ break;
+ }
+ }
+ }
+
+ rbac_send_session_permission( sess_perm_reqp, permp );
+ rbac_free_permission( permp );
+ permp = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_read_session_cb( Operation *op, SlapReply *rs )
+{
+ rbac_session_t *sessp = op->o_callback->sc_private;
+ int i;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ ber_dupbv( &sessp->sessdn, &rs->sr_entry->e_name );
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+ attr = attr_find( rs->sr_entry->e_attrs, *rbac_session_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( rbac_session_ads[i].type ) {
+ case RBAC_SESSION_ID:
+ ber_dupbv( &sessp->sessid, &attr->a_vals[0] );
+ break;
+ case RBAC_USER_DN:
+ ber_dupbv( &sessp->userdn, &attr->a_vals[0] );
+ break;
+ case RBAC_ROLES:
+ ber_bvarray_dup_x( &sessp->roles, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLE_CONSTRAINTS:
+ ber_bvarray_dup_x(
+ &sessp->role_constraints, attr->a_nvals, NULL );
+ break;
+ case RBAC_UID:
+ ber_dupbv( &sessp->uid, &attr->a_vals[0] );
+ break;
+ case RBAC_TENANT_ID:
+ ber_dupbv( &sessp->tenantid, &attr->a_vals[0] );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ //return SLAP_CB_CONTINUE;
+ return 0;
+}
+
+/* check whether the session is owned by the user */
+int
+rbac_is_session_owner( rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ int rc = 0;
+
+ if ( BER_BVISEMPTY( &sessp->uid ) || BER_BVISEMPTY( &reqp->uid ) ) {
+ Debug( LDAP_DEBUG_ANY, "session not owned by user\n" );
+ rc = 0;
+ goto done;
+ }
+
+ if ( !ber_bvstrcasecmp( &sessp->uid, &reqp->uid ) ) {
+ rc = 1;
+ goto done;
+ }
+
+done:;
+ return rc;
+}
+
+int
+rbac_session_add_role( Operation *op, rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ tenant_info_t *tenantp = NULL;
+ struct berval vals[2];
+ Modifications mod;
+ int rc = LDAP_SUCCESS;
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_add_role: "
+ "no tenant info with the req\n" );
+ goto done;
+ }
+
+ // convert the role name to lower case:
+ rbac_to_lower( &reqp->role );
+
+ //ber_dupbv( &vals[0], &reqp->roles[0]);
+ ber_dupbv( &vals[0], &reqp->role );
+ BER_BVZERO( &vals[1] );
+
+ /* create mod list */
+ mod.sml_op = LDAP_MOD_ADD;
+ mod.sml_flags = 0;
+ mod.sml_type = slap_rbac_schema.ad_session_roles->ad_cname;
+ mod.sml_desc = slap_rbac_schema.ad_session_roles;
+ mod.sml_numvals = 1;
+ mod.sml_values = vals;
+ mod.sml_nvalues = NULL;
+ mod.sml_next = NULL;
+
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.orm_modlist = &mod;
+ op2.o_req_dn = sessp->sessdn;
+ op2.o_req_ndn = sessp->sessdn;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+ op2.o_dn = op2.o_bd->be_rootdn;
+ op2.o_ndn = op2.o_bd->be_rootdn;
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_modify( &op2, &rs2 );
+ ch_free( vals[0].bv_val );
+
+done:;
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "role already activated in session\n" );
+ }
+ return rc;
+}
+
+int
+rbac_session_drop_role( Operation *op, rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ tenant_info_t *tenantp = NULL;
+ Modifications *m = NULL;
+ int rc = LDAP_SUCCESS;
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_drop_role: "
+ "no tenant info with the req\n" );
+ goto done;
+ }
+
+ /* create mod list */
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = slap_rbac_schema.ad_session_roles->ad_cname;
+ m->sml_desc = slap_rbac_schema.ad_session_roles;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+ //ber_dupbv( &m->sml_values[0], &reqp->roles[0]);
+
+ // convert the role name to lower case:
+ rbac_to_lower( &reqp->role );
+
+ ber_dupbv( &m->sml_values[0], &reqp->role );
+
+ // todo: determine if this needs to be done:
+ //BER_BVZERO(&m->sml_values[1]);
+
+ ber_dupbv( &m->sml_nvalues[0], &reqp->role );
+ BER_BVZERO( &m->sml_nvalues[1] );
+
+ //ber_dupbv( &m->sml_nvalues[0], &reqp->roles[0]);
+ //ber_dupbv( &m->sml_nvalues[0], &reqp->role);
+ //BER_BVZERO(&m->sml_nvalues[1]);
+
+ m->sml_next = NULL;
+
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.orm_modlist = m;
+ op2.o_req_dn = sessp->sessdn;
+ op2.o_req_ndn = sessp->sessdn;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_modify( &op2, &rs2 );
+
+done:;
+ if ( m ) {
+ slap_mods_free( m, 1 );
+ }
+
+ return rc;
+}
+
+/* delete the session */
+int
+rbac_int_delete_session( Operation *op, rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ tenant_info_t *tenantp = NULL;
+ int rc = LDAP_SUCCESS;
+
+ tenantp = rbac_tid2tenant( &sessp->tenantid );
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_drop_role: "
+ "no tenant info with the req\n" );
+ goto done;
+ }
+
+ /* delete RBAC session */
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_tag = LDAP_REQ_DELETE;
+ op2.o_req_dn = sessp->sessdn;
+ op2.o_req_ndn = sessp->sessdn;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+ rc = op2.o_bd->be_delete( &op2, &rs2 );
+
+done:;
+ return rc;
+}
+
+rbac_session_t *
+rbac_alloc_session()
+{
+ rbac_session_t *sessp = NULL;
+
+ sessp = ch_malloc( sizeof(rbac_session_t) );
+ sessp->sessid.bv_len =
+ lutil_uuidstr( sessp->uuidbuf, sizeof(sessp->uuidbuf) );
+ sessp->sessid.bv_val = sessp->uuidbuf;
+
+ sessp->user = NULL;
+ BER_BVZERO( &sessp->tenantid );
+ BER_BVZERO( &sessp->uid );
+ BER_BVZERO( &sessp->userdn );
+ BER_BVZERO( &sessp->sessdn );
+ BER_BVZERO( &sessp->message );
+
+ sessp->last_access = 0;
+ sessp->timeout = 0;
+ sessp->warning_id = 0;
+ sessp->error_id = 0;
+ sessp->grace_logins = 0;
+ sessp->expiration_secs = 0;
+ sessp->is_authenticated = 0;
+
+ sessp->roles = NULL;
+ sessp->role_constraints = NULL;
+
+ return sessp;
+}
+
+int
+rbac_register_session( Operation *op, SlapReply *rs, rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rdn, nrdn;
+ SlapReply rs2 = { REP_RESULT };
+ OperationBuffer opbuf;
+ Operation *op2;
+ Connection conn = { 0 };
+ Entry *e = NULL;
+ int rc = LDAP_SUCCESS;
+ char rdnbuf[
+ STRLENOF(RBAC_SESSION_RDN_EQ) + LDAP_LUTIL_UUIDSTR_BUFSIZE + 1];
+ tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+#ifdef USE_NEW_THREAD_CONTEXT
+ void *thrctx = ldap_pvt_thread_pool_context();
+#else
+ void *thrctx = op->o_tmpmemctx;
+#endif
+
+ if ( !sessp ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* dynamic objects */
+ e = entry_alloc();
+
+ strcpy( rdnbuf, RBAC_SESSION_RDN_EQ );
+ strncat( rdnbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+ rdn.bv_val = rdnbuf;
+ rdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+ nrdn.bv_val = rdnbuf;
+ nrdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+
+ build_new_dn( &e->e_name, &tenantp->sessions_basedn, &rdn, NULL );
+ build_new_dn( &e->e_nname, &tenantp->sessions_basedn, &nrdn, NULL );
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_rbac_schema.ad_session_id, &sessp->sessid, NULL );
+
+ if ( !BER_BVISNULL( &sessp->uid ) ) {
+ attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+ }
+
+ /* add tenant id */
+ if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_tenant_id, &sessp->tenantid, NULL );
+ }
+
+ /* add the userdn */
+ if ( !BER_BVISNULL( &sessp->userdn ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_session_user_dn, &sessp->userdn, NULL );
+ }
+
+ if ( sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_session_roles, sessp->roles, NULL );
+ }
+
+ // TODO: ensure this is correct way to store constraints in session:
+ if ( sessp->role_constraints ) {
+ attr_merge( e, slap_rbac_schema.ad_session_role_constraints,
+ sessp->role_constraints, NULL );
+ }
+ /* rendered dynmaicObject */
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+ /* store RBAC session */
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op2 = &opbuf.ob_op;
+ //Operation op2 = *op;
+ //op2.o_callback = &nullsc;
+ //rbac_callback_info_t rbac_cb;
+ //cb.sc_private = &rbac_cb;
+ //cb.sc_response = rbac_sess_fake_cb;
+ //op2.o_callback = &cb;
+ //op2.ors_limit = NULL;
+ op->o_callback = &nullsc;
+ op2->o_dn = tenantp->session_admin;
+ op2->o_ndn = tenantp->session_admin;
+ op2->o_tag = LDAP_REQ_ADD;
+ op2->o_protocol = LDAP_VERSION3;
+ op2->o_req_dn = e->e_name;
+ op2->o_req_ndn = e->e_nname;
+ op2->ora_e = e;
+ op2->o_bd = frontendDB;
+
+ rc = op2->o_bd->be_add( op2, &rs2 );
+
+done:;
+ if ( e ) entry_free( e );
+ return rc;
+}
+
+int
+rbac_register_session2( Operation *op, SlapReply *rs, rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rdn, nrdn;
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ //OperationBuffer opbuf;
+ //Connection conn = {0};
+ Entry *e = NULL;
+ int rc = LDAP_SUCCESS;
+ char rdnbuf[STRLENOF(RBAC_SESSION_RDN_EQ) + LDAP_LUTIL_UUIDSTR_BUFSIZE +
+ 1];
+ tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+ slap_callback cb = { 0 };
+ //#ifdef USE_NEW_THREAD_CONTEXT
+ // void *thrctx = ldap_pvt_thread_pool_context();
+ //#else
+ // void *thrctx = op->o_tmpmemctx;
+ //#endif
+
+ if ( !sessp ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* dynamic objects */
+ e = entry_alloc();
+
+ strcpy( rdnbuf, RBAC_SESSION_RDN_EQ );
+ strncat( rdnbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+ rdn.bv_val = rdnbuf;
+ rdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+ nrdn.bv_val = rdnbuf;
+ nrdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+
+ build_new_dn( &e->e_name, &tenantp->sessions_basedn, &rdn, NULL );
+ build_new_dn( &e->e_nname, &tenantp->sessions_basedn, &nrdn, NULL );
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_rbac_schema.ad_session_id, &sessp->sessid, NULL );
+
+ if ( !BER_BVISNULL( &sessp->uid ) ) {
+ attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+ }
+
+ /* add tenant id */
+ if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_tenant_id, &sessp->tenantid, NULL );
+ }
+
+ /* add the userdn */
+ if ( !BER_BVISNULL( &sessp->userdn ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_session_user_dn, &sessp->userdn, NULL );
+ }
+
+ if ( sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_session_roles, sessp->roles, NULL );
+ }
+
+ // TODO: ensure this is correct way to store constraints in session:
+ if ( sessp->role_constraints ) {
+ attr_merge( e, slap_rbac_schema.ad_session_role_constraints,
+ sessp->role_constraints, NULL );
+ }
+ /* rendered dynmaicObject */
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+ /* store RBAC session */
+ //connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ //op2 = &opbuf.ob_op;
+ //op2.o_ctrlflag = op->o_ctrlflag;
+ // todo this ain't right"
+ //op2.o_ctrlflag = 0;
+ //OperationBuffer *opbuf;
+ //memset( opbuf, 0, sizeof(OperationBuffer));
+ //op2.o_hdr = &opbuf->ob_hdr;
+ //op2.o_controls = opbuf->ob_controls;
+
+ // fails on modify.c:353 with segfault
+
+ //op2.o_callback = &nullsc;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_protocol = LDAP_VERSION3;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_bd = frontendDB;
+ //op2.ors_limit = NULL;
+
+ rc = op2.o_bd->be_add( &op2, &rs2 );
+
+done:;
+ if ( e ) entry_free( e );
+
+ return rc;
+}
+
+int
+rbac_is_valid_session_id( struct berval *sessid )
+{
+ /* TODO: simple test */
+ if ( !sessid || sessid->bv_len != 36 ) {
+ if ( !sessid ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_is_valid_session_id: "
+ "null sessid\n" );
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_is_valid_session_id: "
+ "len (%lu)\n",
+ sessid->bv_len );
+ }
+ return 0;
+ }
+
+ else {
+ return 1;
+ }
+}
+
+/* create an rbac request with the session ID */
+rbac_req_t *
+rbac_is_search_session_permissions( Operation *op )
+{
+ rbac_req_t *reqp = NULL;
+
+ /* check whether the search for sessionPermissions and *
+ * with a valid sessionID */
+
+ return reqp;
+}
+
+rbac_session_t *
+rbac_session_byid_fake( Operation *op, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ int rc = LDAP_SUCCESS;
+ char fbuf[RBAC_BUFLEN];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ slap_callback cb = { 0 };
+ tenant_info_t *tenantp = NULL;
+
+ if ( !rbac_is_valid_session_id( &reqp->sessid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "invalid session id (%s)\n",
+ reqp->sessid.bv_val );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ sessp = rbac_alloc_session();
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "unable to allocate session memory\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+ /* session id filter */
+ memset( fbuf, 0, sizeof(fbuf) );
+ strcpy( fbuf, RBAC_SESSION_RDN_EQ );
+ strncpy( &fbuf[0] + sizeof(RBAC_SESSION_RDN_EQ) - 1, reqp->sessid.bv_val,
+ reqp->sessid.bv_len );
+ filter.bv_val = fbuf;
+ filter.bv_len = strlen( fbuf );
+
+ //cb.sc_private = sessp;
+ //cb.sc_response = rbac_read_session_cb;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_req_dn = tenantp->sessions_basedn;
+ op2.o_req_ndn = tenantp->sessions_basedn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_attrs = slap_rbac_schema.session_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.o_bd = frontendDB;
+ // hyc change to fix seg fault:
+ op2.ors_limit = NULL;
+
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:
+ // TODO: find equivalent way of check nentries (broke with fake connection fix)
+ //if ( rc != LDAP_SUCCESS || rs2.sr_nentries <= 0 ) {
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_session( sessp );
+ sessp = NULL;
+ }
+
+ return sessp;
+}
+
+rbac_session_t *
+rbac_session_byid( Operation *op, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ int rc = LDAP_SUCCESS;
+ char fbuf[RBAC_BUFLEN];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ slap_callback cb = { 0 };
+ tenant_info_t *tenantp = NULL;
+
+ if ( !rbac_is_valid_session_id( &reqp->sessid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "invalid session id (%s)\n",
+ reqp->sessid.bv_val );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ sessp = rbac_alloc_session();
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "unable to allocate session memory\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+ /* session id filter */
+ memset( fbuf, 0, sizeof(fbuf) );
+ strcpy( fbuf, RBAC_SESSION_RDN_EQ );
+ strncpy( &fbuf[0] + sizeof(RBAC_SESSION_RDN_EQ) - 1, reqp->sessid.bv_val,
+ reqp->sessid.bv_len );
+ filter.bv_val = fbuf;
+ filter.bv_len = strlen( fbuf );
+
+ cb.sc_private = sessp;
+ cb.sc_response = rbac_read_session_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_req_dn = tenantp->sessions_basedn;
+ op2.o_req_ndn = tenantp->sessions_basedn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_attrs = slap_rbac_schema.session_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.o_bd = frontendDB;
+ // hyc change to fix seg fault:
+ op2.ors_limit = NULL;
+
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:
+ // TODO: find equivalent way of check nentries (broke with fake connection fix)
+ //if ( rc != LDAP_SUCCESS || rs2.sr_nentries <= 0 ) {
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_session( sessp );
+ sessp = NULL;
+ }
+
+ return sessp;
+}
+
+static char *
+rbac_int_session_permissions_filterstr( Operation *op, rbac_session_t *sessp )
+{
+ char filterbuf[RBAC_BUFLEN];
+ int i;
+
+ memset( filterbuf, 0, sizeof(filterbuf) );
+
+ strcat( filterbuf, "(&(objectClass=ftOperation)(|" );
+ strcat( filterbuf, "(ftUsers=" );
+ strcat( filterbuf, sessp->uid.bv_val );
+ strcat( filterbuf, ")" );
+
+ /* add ftRoles filters */
+ for ( i = 0; !BER_BVISEMPTY( &sessp->roles[i] ); i++ ) {
+ strcat( filterbuf, "(ftRoles=" );
+ strncat( filterbuf, sessp->roles[i].bv_val, sessp->roles[i].bv_len );
+ strcat( filterbuf, ")" );
+ }
+ strcat( filterbuf, "))" );
+ return strdup( filterbuf );
+}
+
+int
+rbac_int_session_permissions(
+ Operation *op,
+ SlapReply *rs,
+ rbac_req_t *reqp,
+ rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ tenant_info_t *tenantp = NULL;
+ int rc;
+ struct berval filter;
+ char *filterstr;
+ struct berval permndn = BER_BVNULL;
+ OperationBuffer opbuf;
+ Connection conn = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation *op2;
+ slap_callback cb = { 0 };
+ char permbuf[1024];
+ session_perm_req_t sess_perm_req;
+#ifdef USE_NEW_THREAD_CONTEXT
+ void *thrctx = ldap_pvt_thread_pool_context();
+#else
+ void *thrctx = op->o_tmpmemctx;
+#endif
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+ /* construct session permissions dn */
+ memset( permbuf, 0, sizeof(permbuf) );
+ strcat( permbuf, "rbacSessid=" );
+ strncat( permbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+ strcat( permbuf, ",dc=rbac" );
+ sess_perm_req.op = op;
+ sess_perm_req.rs = rs;
+ sess_perm_req.permdn.bv_val = permbuf;
+ sess_perm_req.permdn.bv_len = strlen( permbuf );
+ sess_perm_req.sessid = &reqp->sessid;
+ sess_perm_req.tenantp = tenantp;
+
+ filterstr = rbac_int_session_permissions_filterstr( op, sessp );
+ if ( !filterstr ) {
+ Debug( LDAP_DEBUG_ANY, "unable to construct filter for session permissions\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+ filter.bv_val = filterstr;
+ filter.bv_len = strlen( filterstr );
+
+ rc = dnNormalize(
+ 0, NULL, NULL, &tenantp->permissions_basedn, &permndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "unable to normalize permission DN\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op2 = &opbuf.ob_op;
+ //Operation op2 = *op;
+ cb.sc_private = &sess_perm_req;
+ cb.sc_response = rbac_session_permissions_cb;
+ op2->o_callback = &cb;
+ op2->o_tag = LDAP_REQ_SEARCH;
+ op2->o_dn = tenantp->admin;
+ op2->o_ndn = tenantp->admin;
+ op2->o_req_dn = tenantp->permissions_basedn;
+ op2->o_req_ndn = permndn;
+ op2->ors_filterstr = filter;
+ op2->ors_filter = str2filter_x( op, filter.bv_val );
+ op2->ors_scope = LDAP_SCOPE_SUB;
+ op2->ors_attrs = tenantp->schema->session_perm_attrs;
+ op2->ors_tlimit = SLAP_NO_LIMIT;
+ op2->ors_slimit = SLAP_NO_LIMIT;
+ op2->ors_attrsonly = 0;
+ op2->o_bd = frontendDB;
+ //op2.ors_limit = NULL;
+ rc = op2->o_bd->be_search( op2, &rs2 );
+ filter_free_x( op, op2->ors_filter, 1 );
+
+done:;
+ /* generate audit log */
+ rbac_audit( op, SessionPermissions, sessp, reqp, rc, (char *)rs->sr_text );
+
+ rs->sr_err = rc;
+ return rs->sr_err;
+}
+
+void
+rbac_free_session( rbac_session_t *sessp )
+{
+ if ( !sessp ) return;
+
+ if ( sessp->user ) {
+ rbac_free_user( sessp->user );
+ }
+
+ if ( !BER_BVISNULL( &sessp->uid ) ) {
+ ber_memfree( sessp->uid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+ ber_memfree( sessp->tenantid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->userdn ) ) {
+ ber_memfree( sessp->userdn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->sessdn ) ) {
+ ber_memfree( sessp->sessdn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->message ) ) {
+ ber_memfree( sessp->message.bv_val );
+ }
+
+ if ( sessp->roles ) {
+ ber_bvarray_free( sessp->roles );
+ }
+
+ if ( sessp->role_constraints ) {
+ ber_bvarray_free( sessp->role_constraints );
+ }
+
+ ch_free( sessp );
+
+ return;
+}
+
+/* roles included from request are activated into a session only when
+ * they exist and have been assigned to the user. If no roles included in request, all
+ * roles assigned to the user are activated into the rbac session.
+ */
+int
+activate_session_roles(
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ rbac_user_t *userp )
+{
+ int i, j, rc = LDAP_UNWILLING_TO_PERFORM;
+ if ( !sessp || !reqp || !userp ) {
+ goto done;
+ }
+
+ /* no role requested, assign all roles from the user to the session. */
+ if ( reqp->roles == NULL || BER_BVISNULL( &reqp->roles[0] ) ) {
+ //if (!reqp->roles || BER_BVISNULL(&reqp->roles[0])) {
+ /* no roles assigned to the user */
+ if ( !userp->roles || BER_BVISNULL( &userp->roles[0] ) ) goto done;
+ for ( i = 0; !BER_BVISNULL( &userp->roles[i] ); i++ ) {
+ struct berval role;
+ ber_dupbv_x( &role, &userp->roles[i], NULL );
+ ber_bvarray_add( &sessp->roles, &role );
+ rc = LDAP_SUCCESS;
+ }
+
+ // TODO: smm 20141218 - make sure this is correct way to add constraints to user session.
+ for ( i = 0; !BER_BVISNULL( &userp->role_constraints[i] ); i++ ) {
+ struct berval roleconstraint;
+ ber_dupbv_x( &roleconstraint, &userp->role_constraints[i], NULL );
+ ber_bvarray_add( &sessp->role_constraints, &roleconstraint );
+ rc = LDAP_SUCCESS;
+ }
+
+ } else {
+ for ( i = 0; !BER_BVISNULL( &reqp->roles[i] ); i++ ) {
+ for ( j = 0; !BER_BVISNULL( &userp->roles[j] ); j++ ) {
+ if ( !ber_bvstrcasecmp( &reqp->roles[i], &userp->roles[j] ) ) {
+ /* requested role is assigned to the user */
+ struct berval role;
+ ber_dupbv_x( &role, &userp->roles[i], NULL );
+ ber_bvarray_add( &sessp->roles, &role );
+ rc = LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+
+done:;
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/rbacuser.c b/contrib/slapd-modules/rbac/rbacuser.c
new file mode 100644
index 0000000..59d3c01
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacuser.c
@@ -0,0 +1,620 @@
+/* rbacuser.c - RBAC users */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static int ppolicy_cid = -1;
+
+static rbac_user_t *
+rbac_alloc_user()
+{
+ rbac_user_t *userp = ch_calloc( 1, sizeof(rbac_user_t) );
+
+ BER_BVZERO( &userp->tenantid );
+ BER_BVZERO( &userp->uid );
+ BER_BVZERO( &userp->dn );
+ BER_BVZERO( &userp->password );
+ BER_BVZERO( &userp->constraints );
+ BER_BVZERO( &userp->msg );
+ userp->roles = NULL;
+ userp->role_constraints = NULL;
+
+ return userp;
+}
+
+static int
+rbac_read_user_cb( Operation *op, SlapReply *rs )
+{
+ rbac_callback_info_t *cbp = op->o_callback->sc_private;
+ rbac_ad_t *user_ads;
+ rbac_user_t *userp = NULL;
+ int rc = 0, i;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb\n" );
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
+ "sr_type != REP_SEARCH\n" );
+ return 0;
+ }
+
+ assert( cbp );
+
+ user_ads = cbp->tenantp->schema->user_ads;
+
+ userp = rbac_alloc_user();
+ if ( !userp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
+ "rbac_alloc_user failed\n" );
+
+ goto done;
+ }
+
+ ber_dupbv( &userp->dn, &rs->sr_entry->e_name );
+
+ Debug( LDAP_DEBUG_ANY, "DEBUG rbac_read_user_cb (%s): "
+ "rc (%d)\n",
+ userp->dn.bv_val, rc );
+
+ for ( i = 0; !BER_BVISNULL( &user_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+
+ attr = attr_find( rs->sr_entry->e_attrs, *user_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( user_ads[i].type ) {
+ case RBAC_ROLE_ASSIGNMENT:
+ ber_bvarray_dup_x( &userp->roles, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLE_CONSTRAINTS:
+ ber_bvarray_dup_x(
+ &userp->role_constraints, attr->a_nvals, NULL );
+ break;
+ case RBAC_USER_CONSTRAINTS:
+ ber_dupbv_x( &userp->constraints, &attr->a_nvals[0], NULL );
+ break;
+ case RBAC_UID:
+ ber_dupbv_x( &userp->uid, &attr->a_nvals[0], NULL );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+done:;
+ cbp->private = userp;
+
+ return 0;
+}
+
+static int
+rbac_bind_cb( Operation *op, SlapReply *rs )
+{
+ rbac_user_t *ui = op->o_callback->sc_private;
+
+ LDAPControl *ctrl = ldap_control_find(
+ LDAP_CONTROL_PASSWORDPOLICYRESPONSE, rs->sr_ctrls, NULL );
+ if ( ctrl ) {
+ LDAP *ld;
+ ber_int_t expire, grace;
+ LDAPPasswordPolicyError error;
+
+ ldap_create( &ld );
+ if ( ld ) {
+ int rc = ldap_parse_passwordpolicy_control(
+ ld, ctrl, &expire, &grace, &error );
+ if ( rc == LDAP_SUCCESS ) {
+ ui->authz = RBAC_PASSWORD_GOOD;
+ if ( grace > 0 ) {
+ //ui->msg.bv_len = sprintf(ui->msg.bv_val,
+ // "Password expired; %d grace logins remaining",
+ // grace);
+ ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
+ } else if ( error != PP_noError ) {
+ ber_str2bv( ldap_passwordpolicy_err2txt( error ), 0, 0,
+ &ui->msg );
+
+ switch ( error ) {
+ case PP_passwordExpired:
+ ui->authz = RBAC_PASSWORD_EXPIRATION_WARNING;
+
+ if ( expire >= 0 ) {
+ char *unit = "seconds";
+ if ( expire > 60 ) {
+ expire /= 60;
+ unit = "minutes";
+ }
+ if ( expire > 60 ) {
+ expire /= 60;
+ unit = "hours";
+ }
+ if ( expire > 24 ) {
+ expire /= 24;
+ unit = "days";
+ }
+#if 0 /* Who warns about expiration so far in advance? */
+ if (expire > 7) {
+ expire /= 7;
+ unit = "weeks";
+ }
+ if (expire > 4) {
+ expire /= 4;
+ unit = "months";
+ }
+ if (expire > 12) {
+ expire /= 12;
+ unit = "years";
+ }
+#endif
+ }
+
+ //rs->sr_err = ;
+ break;
+ case PP_accountLocked:
+ ui->authz = RBAC_ACCOUNT_LOCKED;
+ //rs->sr_err = ;
+ break;
+ case PP_changeAfterReset:
+ ui->authz = RBAC_CHANGE_AFTER_RESET;
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ case PP_passwordModNotAllowed:
+ ui->authz = RBAC_NO_MODIFICATIONS;
+ //rs->sr_err = ;
+ break;
+ case PP_mustSupplyOldPassword:
+ ui->authz = RBAC_MUST_SUPPLY_OLD;
+ //rs->sr_err = ;
+ break;
+ case PP_insufficientPasswordQuality:
+ ui->authz = RBAC_INSUFFICIENT_QUALITY;
+ //rs->sr_err = ;
+ break;
+ case PP_passwordTooShort:
+ ui->authz = RBAC_PASSWORD_TOO_SHORT;
+ //rs->sr_err = ;
+ break;
+ case PP_passwordTooYoung:
+ ui->authz = RBAC_PASSWORD_TOO_YOUNG;
+ //rs->sr_err = ;
+ break;
+ case PP_passwordInHistory:
+ ui->authz = RBAC_HISTORY_VIOLATION;
+ //rs->sr_err = ;
+ break;
+ case PP_noError:
+ default:
+ // do nothing
+ //ui->authz = RBAC_PASSWORD_GOOD;
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+// switch (error) {
+// case PP_passwordExpired:
+ /* report this during authz */
+// rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+// case PP_changeAfterReset:
+// ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
+// }
+ }
+ }
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ }
+
+ return 0;
+}
+
+/* exported user functions */
+int
+rbac_authenticate_user( Operation *op, rbac_user_t *userp )
+{
+ int rc = LDAP_SUCCESS;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ LDAPControl *sctrls[4];
+ LDAPControl sctrl[3];
+ int nsctrls = 0;
+ LDAPControl c;
+ struct berval ber_bvnull = BER_BVNULL;
+ struct berval dn, ndn;
+
+ rc = dnPrettyNormal( 0, &userp->dn, &dn, &ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ cb.sc_response = rbac_bind_cb;
+ cb.sc_private = userp;
+ op2.o_callback = &cb;
+ op2.o_dn = ber_bvnull;
+ op2.o_ndn = ber_bvnull;
+ op2.o_tag = LDAP_REQ_BIND;
+ op2.o_protocol = LDAP_VERSION3;
+ op2.orb_method = LDAP_AUTH_SIMPLE;
+ op2.orb_cred = userp->password;
+ op2.o_req_dn = dn;
+ op2.o_req_ndn = ndn;
+
+ // loading the ldap pw policy controls loaded into here, added by smm:
+ c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+ c.ldctl_value.bv_val = NULL;
+ c.ldctl_value.bv_len = 0;
+ c.ldctl_iscritical = 0;
+ sctrl[nsctrls] = c;
+ sctrls[nsctrls] = &sctrl[nsctrls];
+ sctrls[++nsctrls] = NULL;
+ op2.o_ctrls = sctrls;
+
+ if ( ppolicy_cid < 0 ) {
+ rc = slap_find_control_id( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ &ppolicy_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+ // smm - need to set the control flag too:
+ op2.o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_CRITICAL;
+
+ slap_op_time( &op2.o_time, &op2.o_tincr );
+ op2.o_bd = frontendDB;
+ rc = op2.o_bd->be_bind( &op2, &rs2 );
+ if ( userp->authz > 0 ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
+ "password policy violation (%d)\n",
+ userp->dn.bv_val ? userp->dn.bv_val : "NULL", userp->authz );
+ }
+
+done:;
+ ch_free( dn.bv_val );
+ ch_free( ndn.bv_val );
+
+ Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
+ "rc (%d)\n",
+ userp->dn.bv_val ? userp->dn.bv_val : "NULL", rc );
+ return rc;
+}
+
+/*
+ isvalidusername(): from OpenLDAP ~/contrib/slapd-modules/nssov/passwd.c
+ Checks to see if the specified name is a valid user name.
+
+ This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
+ and 3.276 Portable Filename Character Set):
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+ The standard defines user names valid if they contain characters from
+ the set [A-Za-z0-9._-] where the hyphen should not be used as first
+ character. As an extension this test allows the dolar '$' sign as the last
+ character to support Samba special accounts.
+*/
+static int
+isvalidusername( struct berval *bv )
+{
+ int i;
+ char *name = bv->bv_val;
+ if ( (name == NULL) || ( name[0] == '\0' ) ) return 0;
+ /* check first character */
+ if ( !( ( name[0] >= 'A' && name[0] <= 'Z' ) ||
+ ( name[0] >= 'a' && name[0] <= 'z' ) ||
+ ( name[0] >= '0' && name[0] <= '9' ) || name[0] == '.' ||
+ name[0] == '_' ) )
+ return 0;
+ /* check other characters */
+ for ( i = 1; i < bv->bv_len; i++ ) {
+ if ( name[i] == '$' ) {
+ /* if the char is $ we require it to be the last char */
+ if ( name[i + 1] != '\0' ) return 0;
+ } else if ( !( ( name[i] >= 'A' && name[i] <= 'Z' ) ||
+ ( name[i] >= 'a' && name[i] <= 'z' ) ||
+ ( name[i] >= '0' && name[i] <= '9' ) ||
+ name[i] == '.' || name[i] == '_' ||
+ name[i] == '-' ) )
+ return 0;
+ }
+ /* no test failed so it must be good */
+ return -1;
+}
+
+rbac_user_t *
+rbac_read_user( Operation *op, rbac_req_t *reqp )
+{
+ int rc = LDAP_SUCCESS;
+ tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
+ rbac_user_t *userp = NULL;
+ char fbuf[RBAC_BUFLEN];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ slap_callback cb = { 0 };
+ rbac_callback_info_t rbac_cb;
+
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+ "missing tenant information\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* uid is a pre-requisite for reading the user information */
+ if ( BER_BVISNULL( &reqp->uid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+ "missing uid, unable to read user entry\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if ( !isvalidusername( &reqp->uid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+ "invalid user id\n" );
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+
+ rbac_cb.tenantp = tenantp;
+ rbac_cb.private = NULL;
+
+ memset( fbuf, 0, sizeof(fbuf) );
+ strcpy( fbuf, "uid=" );
+ strncat( fbuf, reqp->uid.bv_val, reqp->uid.bv_len );
+ filter.bv_val = fbuf;
+ filter.bv_len = strlen( fbuf );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "invalid DN syntax\n" );
+ goto done;
+ }
+
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_read_user_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->admin;
+ op2.o_ndn = tenantp->admin;
+ op2.o_req_dn = tenantp->users_basedn;
+ op2.o_req_ndn = tenantp->users_basedn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_attrs = tenantp->schema->user_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_attrsonly = 0;
+ op2.o_bd = frontendDB;
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:;
+ if ( rc == LDAP_SUCCESS && rbac_cb.private ) {
+ userp = (rbac_user_t *)rbac_cb.private;
+ if ( !BER_BVISNULL( &reqp->authtok ) )
+ ber_dupbv( &userp->password, &reqp->authtok );
+ rbac_cb.private = NULL;
+ return userp;
+ } else {
+ userp = (rbac_user_t *)rbac_cb.private;
+ rbac_free_user( userp );
+ return NULL;
+ }
+}
+
+/* evaluate temporal constraints for the user */
+int
+rbac_user_temporal_constraint( rbac_user_t *userp )
+{
+ int rc = LDAP_SUCCESS;
+ rbac_constraint_t *cp = NULL;
+
+ if ( BER_BVISNULL( &userp->constraints ) ) {
+ /* no temporal constraint */
+ goto done;
+ }
+
+ cp = rbac_bv2constraint( &userp->constraints );
+ if ( !cp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_user_temporal_constraint: "
+ "invalid user constraint \n" );
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ rc = rbac_check_time_constraint( cp );
+
+done:;
+ rbac_free_constraint( cp );
+
+ return rc;
+}
+
+/*
+rbac_constraint_t *
+rbac_user_role_constraintsx(rbac_user_t *userp)
+{
+ rbac_constraint_t *tmp, *cp = NULL;
+ int i = 0;
+
+ if (!userp || !userp->role_constraints)
+ goto done;
+
+ while (!BER_BVISNULL(&userp->role_constraints[i])) {
+ tmp = rbac_bv2constraint(&userp->role_constraints[i++]);
+ if (tmp) {
+ if (!cp) {
+ cp = tmp;
+ } else {
+ tmp->next = cp;
+ cp = tmp;
+ }
+ }
+ }
+
+done:;
+ return cp;
+}
+*/
+
+rbac_constraint_t *
+rbac_user_role_constraints( BerVarray values )
+{
+ rbac_constraint_t *curr, *head = NULL;
+ int i = 0;
+
+ if ( values ) {
+ while ( !BER_BVISNULL( &values[i] ) ) {
+ curr = rbac_bv2constraint( &values[i++] );
+ if ( curr ) {
+ curr->next = head;
+ head = curr;
+ }
+ }
+ }
+
+ return head;
+}
+
+/*
+
+void main() {
+ item * curr, * head;
+ int i;
+
+ head = NULL;
+
+ for(i=1;i<=10;i++) {
+ curr = (item *)malloc(sizeof(item));
+ curr->val = i;
+ curr->next = head;
+ head = curr;
+ }
+
+ curr = head;
+
+ while(curr) {
+ printf("%d\n", curr->val);
+ curr = curr->next ;
+ }
+}
+
+ */
+
+/*
+ *
+rbac_user_role_constraints2(BerVarray values)
+{
+ rbac_constraint_t *tmp, *cp = NULL;
+ int i = 0;
+
+ if (!values)
+ goto done;
+
+ while (!BER_BVISNULL(&values[i])) {
+ tmp = rbac_bv2constraint(&values[i++]);
+ if (tmp) {
+ if (!cp) {
+ cp = tmp;
+ } else {
+ tmp->next = cp;
+ cp = tmp;
+ //cp->next = tmp;
+ //cp = tmp->next;
+
+ }
+ }
+ }
+
+done:;
+ return cp;
+}
+
+
+rbac_user_role_constraints3(rbac_constraint_t *values)
+{
+ rbac_constraint_t *tmp, *cp = NULL;
+ int i = 0;
+
+ if (!values)
+ goto done;
+
+ while (!BER_BVISNULL(values[i])) {
+ tmp = rbac_bv2constraint(&values[i++]);
+ if (tmp) {
+ if (!cp) {
+ cp = tmp;
+ } else {
+ tmp->next = cp;
+ cp = tmp;
+ }
+ }
+ }
+
+done:;
+ return cp;
+}
+*/
+
+void
+rbac_free_user( rbac_user_t *userp )
+{
+ if ( !userp ) return;
+
+ if ( !BER_BVISNULL( &userp->tenantid ) ) {
+ ber_memfree( userp->tenantid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->uid ) ) {
+ ber_memfree( userp->uid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->dn ) ) {
+ ber_memfree( userp->dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->constraints ) ) {
+ ber_memfree( userp->constraints.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->password ) ) {
+ ber_memfree( userp->password.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->msg ) ) {
+ ber_memfree( userp->msg.bv_val );
+ }
+
+ if ( userp->roles ) ber_bvarray_free( userp->roles );
+
+ if ( userp->role_constraints ) ber_bvarray_free( userp->role_constraints );
+
+ ch_free( userp );
+}
diff --git a/contrib/slapd-modules/rbac/slapo-rbac.5 b/contrib/slapd-modules/rbac/slapo-rbac.5
new file mode 100644
index 0000000..453bcbc
--- /dev/null
+++ b/contrib/slapd-modules/rbac/slapo-rbac.5
@@ -0,0 +1,157 @@
+.TH SLAPO_RBAC 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1999-2021 SYMAS Corporation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-rbac \- RBAC0 overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+.LP
+The
+.B slapo-rbac
+overlay
+is an implementation of the ANSI INCITS 359 Role-Based Access Control (RBAC) Core.
+When instantiated, it intercepts, decodes and enforces specific RBAC policies per the Apache Fortress RBAC data formats.
+.P
+The overlay provides a set of extended operations.
+They include session create/delete, checkAccess, addActiveRole, dropActiveRole and sessionRoles.
+.P
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+configuration options apply to the slapo-rbac overlay.
+
+.TP
+.B overlay rbac
+This tag gets applied to the RBAC configuration db (see example below).
+.TP
+.B rbac-default-users-base-dn "ou=People,dc=example,dc=com"
+Points to the container that contains the Apache Fortress users.
+.TP
+.B rbac-default-roles-base-dn "ou=Roles,ou=RBAC,dc=example,dc=com"
+Points to the container that contains the Apache Fortress roles.
+.TP
+.B rbac-default-permissions-base-dn "ou=Permissions,ou=RBAC,dc=example,dc=com"
+Points to the container that contains the Apache Fortress perms.
+.TP
+.B rbac-default-sessions-base-dn "cn=rbac"
+Points to the suffix of the RBAC sessions.
+.TP
+.B rbac-default-audit-base-dn "cn=audit"
+Points to the suffix where the audit records are stored.
+.TP
+.B rbac-admin "cn=manager,dc=example,dc=com"
+A service account that has read access to the entire Apache Fortress DIT.
+.TP
+.B rbac-pwd "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
+The password according to the service account.
+.TP
+.B rbac-session-admin "cn=manager,cn=rbac"
+The root dn of the RBAC sessions database.
+.TP
+.B rbac-session-admin-pwd {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+The password corresponding with the session database.
+.TP
+.RE
+
+.SH EXAMPLES
+.LP
+.RS
+.nf
+
+This overlay requires the
+.B rbac.schema
+loaded and three additional database config sections, one to store rbac
+sessions, second to store the audit records and third to hold the overlay's
+config parameters. They should appear after the existing Apache Fortress db
+config.
+
+.TP
+1. Session Database: Used to store the RBAC sessions corresponding to a logged in user.
+.B database mdb
+.B suffix "cn=rbac"
+.B rootdn "cn=manager,cn=rbac"
+.B rootpw {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B index rbacSessid eq
+.B directory "/var/openldap/rbacsess"
+.B overlay dds
+.B dds-default-ttl 3600
+.B dds-max-dynamicObjects 100000
+.B dbnosync
+.B checkpoint 64 5
+.PP
+
+.TP
+2. Audit Database: Stores records that track user's activities.
+.B database mdb
+.B suffix "cn=audit"
+.B rootdn "cn=manager,cn=audit"
+.B rootpw {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B directory "/var/openldap/rbacaudit"
+.B dbnosync
+.B checkpoint 64 5
+
+.PP
+
+.TP
+3. Config Database: Stores the parameters needed for this overlay to work.
+.B database mdb
+.B suffix "dc=rbac"
+.B rootdn "cn=manager,dc=rbac"
+.B rootpw {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B directory "/var/openldap/rbacoverlay"
+.B overlay rbac
+.B rbac-default-tenant-id "example"
+.B rbac-default-users-base-dn "ou=People,dc=example,dc=com"
+.B rbac-default-roles-base-dn "ou=Roles,ou=RBAC,dc=example,dc=com"
+.B rbac-default-permissions-base-dn "ou=Permissions,ou=RBAC,dc=example,dc=com"
+.B rbac-default-sessions-base-dn "cn=rbac"
+.B rbac-default-audit-base-dn "cn=audit"
+.B rbac-admin "cn=manager,dc=example,dc=com"
+.B rbac-pwd "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
+.B rbac-session-admin "cn=manager,cn=rbac"
+.B rbac-session-admin-pwd {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+
+.fi
+.RE
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.UR https://profsandhu.com/journals/tissec/ANSI+INCITS+359-2004.pdf
+.UE ANSI INCITS 359 Role-Based Access Control specification
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/README.md
+.UE Apache Fortress README
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/README-QUICKSTART-SLAPD.md
+.UE Apache Fortress OpenLDAP Quickstart
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/ldap/schema/fortress.schema
+.UE Apache Fortress RBAC schema
+
+.SH BUGS
+This overlay is experimental.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2013 by Ted Cheng of Symas Corporation
+with a little help from Matt Hardin, Howard Chu, Shawn McKinney.
+.P
+.so ../Project
diff --git a/contrib/slapd-modules/rbac/util.c b/contrib/slapd-modules/rbac/util.c
new file mode 100644
index 0000000..11a5e54
--- /dev/null
+++ b/contrib/slapd-modules/rbac/util.c
@@ -0,0 +1,531 @@
+/* util.c - RBAC utility */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+#define DELIMITER '$'
+
+#define SUNDAY 0x01
+#define MONDAY 0x02
+#define TUESDAY 0x04
+#define WEDNESDAY 0x08
+#define THURSDAY 0x10
+#define FRIDAY 0x20
+#define SATURDAY 0x40
+
+#define ALL_WEEK "all"
+
+void
+rbac_free_constraint( rbac_constraint_t *cp )
+{
+ if ( !cp ) return;
+
+ if ( !BER_BVISNULL( &cp->name ) ) {
+ ch_free( cp->name.bv_val );
+ }
+
+ ch_free( cp );
+}
+
+void
+rbac_free_constraints( rbac_constraint_t *constraints )
+{
+ rbac_constraint_t *cp, *tmp;
+
+ if ( !constraints ) return;
+
+ tmp = constraints;
+ while ( tmp ) {
+ cp = tmp->next;
+ rbac_free_constraint( tmp );
+ tmp = cp;
+ }
+
+ return;
+}
+
+rbac_constraint_t *
+rbac_alloc_constraint()
+{
+ rbac_constraint_t *cp = NULL;
+
+ cp = ch_calloc( 1, sizeof(rbac_constraint_t) );
+ return cp;
+}
+
+static int
+is_well_formed_constraint( struct berval *bv )
+{
+ int rc = LDAP_SUCCESS;
+
+ /* assume well-formed role/user-constraints, for the moment */
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "is_well_formed_constraint: "
+ "rbac role/user constraint not well-formed: %s\n",
+ bv->bv_val );
+ }
+
+ return rc;
+}
+
+/* input contains 4 digits, representing time */
+/* in hhmm format */
+static int
+constraint_parse_time( char *input )
+{
+ int btime;
+ char *ptr = input;
+
+ btime = ( *ptr++ - '0' ) * 12;
+ btime += ( *ptr++ - '0' );
+ btime *= 60; /* turning into mins */
+ btime += ( *ptr++ - '0' ) * 10;
+ btime += ( *ptr++ - '0' );
+ btime *= 60; /* turning into secs */
+
+ return btime;
+}
+
+/* input contains 4 digits, representing year */
+/* in yyyy format */
+static int
+constraint_parse_year( char *input )
+{
+ int i;
+ int year = 0;
+ char *ptr = input;
+
+ for ( i = 0; i <= 3; i++, ptr++ ) {
+ year = year * 10 + *ptr - '0';
+ }
+
+ return year;
+}
+
+/* input contains 2 digits, representing month */
+/* in mm format */
+static int
+constraint_parse_month( char *input )
+{
+ int i;
+ int month = 0;
+ char *ptr = input;
+
+ for ( i = 0; i < 2; i++, ptr++ ) {
+ month = month * 10 + *ptr - '0';
+ }
+
+ return month;
+}
+
+/* input contains 2 digits, representing day in month */
+/* in dd format */
+static int
+constraint_parse_day_in_month( char *input )
+{
+ int i;
+ int day_in_month = 0;
+ char *ptr = input;
+
+ for ( i = 0; i < 2; i++, ptr++ ) {
+ day_in_month = day_in_month * 10 + *ptr - '0';
+ }
+
+ return day_in_month;
+}
+
+rbac_constraint_t *
+rbac_bv2constraint( struct berval *bv )
+{
+ rbac_constraint_t *cp = NULL;
+ int rc = LDAP_SUCCESS;
+ char *ptr, *endp = NULL;
+ int len = 0;
+ int year, month, mday;
+
+ if ( !bv || BER_BVISNULL( bv ) ) goto done;
+
+ rc = is_well_formed_constraint( bv );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ cp = rbac_alloc_constraint();
+ if ( !cp ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* constraint name */
+ ptr = bv->bv_val;
+ endp = ptr;
+ while ( *endp != DELIMITER ) {
+ endp++;
+ len++;
+ }
+
+ if ( len > 0 ) {
+ cp->name.bv_val = ch_malloc( len + 1 );
+ strncpy( cp->name.bv_val, ptr, len );
+ cp->name.bv_val[len] = '\0';
+ cp->name.bv_len = len;
+ } else {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* allowed inactivity period */
+ ptr = endp;
+ endp++;
+ if ( isdigit( *endp ) ) {
+ int secs = 0;
+ while ( isdigit( *endp ) ) {
+ secs = secs * 10 + *endp - '0';
+ endp++;
+ }
+ cp->allowed_inactivity = secs;
+ } else if ( *endp != DELIMITER ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ ptr = endp;
+ endp = ptr + 1;
+
+ /* begin time */
+ if ( isdigit( *endp ) ) {
+ cp->begin_time = constraint_parse_time( endp );
+ while ( isdigit( *endp ) )
+ endp++;
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* end time */
+ if ( isdigit( *endp ) ) {
+ cp->end_time = constraint_parse_time( endp );
+ while ( isdigit( *endp ) )
+ endp++;
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* begin year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->begin_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* end year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->end_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* begin lock year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->begin_lock_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* end lock year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->end_lock_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* dayMask */
+
+ /* allow "all" to mean the entire week */
+ if ( strncasecmp( endp, ALL_WEEK, strlen( ALL_WEEK ) ) == 0 ) {
+ cp->day_mask = SUNDAY | MONDAY | TUESDAY | WEDNESDAY | THURSDAY |
+ FRIDAY | SATURDAY;
+ }
+
+ while ( *endp && isdigit( *endp ) ) {
+ switch ( *endp - '0' ) {
+ case 1:
+ cp->day_mask |= SUNDAY;
+ break;
+ case 2:
+ cp->day_mask |= MONDAY;
+ break;
+ case 3:
+ cp->day_mask |= TUESDAY;
+ break;
+ case 4:
+ cp->day_mask |= WEDNESDAY;
+ break;
+ case 5:
+ cp->day_mask |= THURSDAY;
+ break;
+ case 6:
+ cp->day_mask |= FRIDAY;
+ break;
+ case 7:
+ cp->day_mask |= SATURDAY;
+ break;
+ default:
+ /* should not be here */
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ endp++;
+ }
+
+done:;
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_constraint( cp );
+ cp = NULL;
+ }
+
+ return cp;
+}
+
+static int
+constraint_day_of_week( rbac_constraint_t *cp, int wday )
+{
+ int rc = LDAP_UNWILLING_TO_PERFORM;
+
+ /* assumption: Monday is 1st day of a week */
+ switch ( wday ) {
+ case 1:
+ if ( !(cp->day_mask & MONDAY) ) goto done;
+ break;
+ case 2:
+ if ( !(cp->day_mask & TUESDAY) ) goto done;
+ break;
+ case 3:
+ if ( !(cp->day_mask & WEDNESDAY) ) goto done;
+ break;
+ case 4:
+ if ( !(cp->day_mask & THURSDAY) ) goto done;
+ break;
+ case 5:
+ if ( !(cp->day_mask & FRIDAY) ) goto done;
+ break;
+ case 6:
+ if ( !(cp->day_mask & SATURDAY) ) goto done;
+ break;
+ case 0:
+ case 7:
+ if ( !(cp->day_mask & SUNDAY) ) goto done;
+ break;
+ default:
+ /* should not be here */
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:;
+ return rc;
+}
+
+int
+rbac_check_time_constraint( rbac_constraint_t *cp )
+{
+ int rc = LDAP_UNWILLING_TO_PERFORM;
+ time_t now;
+ struct tm result, *resultp;
+
+ now = slap_get_time();
+
+ /*
+ * does slapd support day-of-week (wday)?
+ * using native routine for now.
+ * Win32's gmtime call is already thread-safe, to the _r
+ * decorator is unneeded.
+ */
+#ifdef _WIN32
+ resultp = gmtime( &now );
+#else
+ resultp = gmtime_r( &now, &result );
+#endif
+ if ( !resultp ) goto done;
+#if 0
+ timestamp.bv_val = timebuf;
+ timestamp.bv_len = sizeof(timebuf);
+ slap_timestamp(&now, &timestamp);
+ lutil_parsetime(timestamp.bv_val, &now_tm);
+ lutil_tm2time(&now_tm, &now_tt);
+#endif
+
+ if ( ( cp->begin_date.tt_sec > 0 && cp->begin_date.tt_sec > now ) ||
+ ( cp->end_date.tt_sec > 0 && cp->end_date.tt_sec < now ) ) {
+ /* not within allowed time period */
+ goto done;
+ }
+
+ /* allowed time period during a day */
+ if ( cp->begin_time > 0 && cp->end_time > 0 ) {
+ int timeofday = ( resultp->tm_hour * 60 + resultp->tm_min ) * 60 +
+ resultp->tm_sec;
+ if ( timeofday < cp->begin_time || timeofday > cp->end_time ) {
+ /* not within allowed time period in a day */
+ goto done;
+ }
+ }
+
+ /* allowed day in a week */
+ if ( cp->day_mask > 0 ) {
+ rc = constraint_day_of_week( cp, resultp->tm_wday );
+ if ( rc != LDAP_SUCCESS ) goto done;
+ }
+
+ /* during lock-out period? */
+ if ( ( cp->begin_lock_date.tt_sec > 0 &&
+ cp->begin_lock_date.tt_sec < now ) &&
+ ( cp->end_lock_date.tt_sec > 0 &&
+ cp->end_lock_date.tt_sec > now ) ) {
+ /* within locked out period */
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* passed all tests */
+ rc = LDAP_SUCCESS;
+
+done:;
+ return rc;
+}
+
+rbac_constraint_t *
+rbac_role2constraint( struct berval *role, rbac_constraint_t *role_constraints )
+{
+ rbac_constraint_t *cp = NULL;
+
+ if ( !role_constraints || !role ) goto done;
+
+ cp = role_constraints;
+ while ( cp ) {
+ if ( ber_bvstrcasecmp( role, &cp->name ) == 0 ) {
+ /* found the role constraint */
+ goto done;
+ }
+ cp = cp->next;
+ }
+
+done:;
+ return cp;
+}
+
+void
+rbac_to_lower( struct berval *bv )
+{
+ // convert the berval to lower case:
+ int i;
+ for ( i = 0; i < bv->bv_len; i++ ) {
+ bv->bv_val[i] = tolower( bv->bv_val[i] );
+ }
+}
diff --git a/contrib/slapd-modules/samba4/Makefile b/contrib/slapd-modules/samba4/Makefile
new file mode 100644
index 0000000..f53d130
--- /dev/null
+++ b/contrib/slapd-modules/samba4/Makefile
@@ -0,0 +1,68 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_RDNVAL=SLAPD_MOD_DYNAMIC \
+ -DSLAPD_OVER_PGUID=SLAPD_MOD_DYNAMIC \
+ -DSLAPD_OVER_VERNUM=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pguid.la rdnval.la vernum.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pguid.la: pguid.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+rdnval.la: rdnval.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+vernum.la: vernum.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/samba4/README b/contrib/slapd-modules/samba4/README
new file mode 100644
index 0000000..65745b1
--- /dev/null
+++ b/contrib/slapd-modules/samba4/README
@@ -0,0 +1,72 @@
+# $OpenLDAP$
+
+This directory contains slapd overlays specific to samba4 LDAP backend:
+
+ - pguid (not used)
+ - rdnval (under evaluation)
+ - vernum (under evaluation)
+
+
+ - PGUID
+
+This overlay maintains the operational attribute "parentUUID". It contains
+the entryUUID of the parent entry. This overlay is not being considered
+right now.
+
+
+ - RDNVAL
+
+This overlay maintains the operational attribute "rdnValue". It contains
+the value of the entry's RDN. This attribute is defined by the overlay
+itself as
+
+ ( 1.3.6.1.4.1.4203.666.1.58
+ NAME 'rdnValue'
+ DESC 'the value of the naming attributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ EQUALITY caseIgnoreMatch
+ USAGE dSAOperation
+ NO-USER-MODIFICATION )
+
+under OpenLDAP's development OID arc. This OID is temporary.
+
+To use the overlay, add:
+
+ moduleload <path to>rdnval.so
+ ...
+
+ database <whatever>
+ ...
+ overlay rdnval
+
+to your slapd configuration file. An instance is required for each database
+that needs to maintain this attribute.
+
+
+ - VERNUM
+
+This overlay increments a counter any time an attribute is modified.
+It is intended to increment the counter 'msDS-KeyVersionNumber' when
+the attribute 'unicodePwd' is modified.
+
+
+These overlays are only set up to be built as a dynamically loaded modules.
+On most platforms, in order for the modules to be usable, all of the
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlays statically, you will have to move them
+into the slapd/overlays directory and edit the Makefile and overlays.c
+to reference them.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+Copyright 2009-2022 The OpenLDAP Foundation.
+
+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>.
+
diff --git a/contrib/slapd-modules/samba4/pguid.c b/contrib/slapd-modules/samba4/pguid.c
new file mode 100644
index 0000000..4b0b066
--- /dev/null
+++ b/contrib/slapd-modules/samba4/pguid.c
@@ -0,0 +1,460 @@
+/* pguid.c - Parent GUID value overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_PGUID
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * Maintain an attribute (parentUUID) that contains the value
+ * of the entryUUID of the parent entry (used by Samba4)
+ */
+
+static AttributeDescription *ad_parentUUID;
+
+static slap_overinst pguid;
+
+static int
+pguid_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ struct berval pdn, pndn;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+
+ /* don't care about suffix entry */
+ if ( dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[0] ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ dnParent( &op->o_req_dn, &pdn );
+ dnParent( &op->o_req_ndn, &pndn );
+
+ rc = overlay_entry_get_ov( op, &pndn, NULL, slap_schema.si_ad_entryUUID, 0, &e, on );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_add: unable to get parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_add: unable to find entryUUID of parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+
+ } else {
+ assert( a->a_numvals == 1 );
+
+ if ( op->ora_e != NULL ) {
+ attr_merge_one( op->ora_e, ad_parentUUID, &a->a_vals[0], a->a_nvals == a->a_vals ? NULL : &a->a_nvals[0] );
+
+ } else {
+ Modifications *ml;
+ Modifications *mod;
+
+ assert( op->ora_modlist != NULL );
+
+ for ( ml = op->ora_modlist; ml != NULL; ml = ml->sml_next ) {
+ if ( ml->sml_mod.sm_desc == slap_schema.si_ad_entryUUID ) {
+ break;
+ }
+ }
+
+ if ( ml == NULL ) {
+ ml = op->ora_modlist;
+ }
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_ADD;
+ mod->sml_desc = ad_parentUUID;
+ mod->sml_type = ad_parentUUID->ad_cname;
+ mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 1;
+
+ ber_dupbv( &mod->sml_values[0], &a->a_vals[0] );
+ BER_BVZERO( &mod->sml_values[1] );
+
+ mod->sml_next = ml->sml_next;
+ ml->sml_next = mod;
+ }
+ }
+
+ if ( e != NULL ) {
+ (void)overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pguid_op_rename( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+
+ if ( op->orr_nnewSup == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = overlay_entry_get_ov( op, op->orr_nnewSup, NULL, slap_schema.si_ad_entryUUID, 0, &e, on );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_rename: unable to get newSuperior entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, op->orr_newSup->bv_val, rc );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_rename: unable to find entryUUID of newSuperior entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, op->orr_newSup->bv_val, rc );
+
+ } else {
+ Modifications *mod;
+
+ assert( a->a_numvals == 1 );
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_desc = ad_parentUUID;
+ mod->sml_type = ad_parentUUID->ad_cname;
+ mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 1;
+
+ ber_dupbv( &mod->sml_values[0], &a->a_vals[0] );
+ BER_BVZERO( &mod->sml_values[1] );
+
+ mod->sml_next = op->orr_modlist;
+ op->orr_modlist = mod;
+ }
+
+ if ( e != NULL ) {
+ (void)overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pguid_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid_db_init: pguid cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid_db_init: database must have suffix\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
+ be->be_suffix[ 0 ].bv_val );
+ }
+
+ return 0;
+}
+
+typedef struct pguid_mod_t {
+ struct berval ndn;
+ struct berval pguid;
+ struct pguid_mod_t *next;
+} pguid_mod_t;
+
+typedef struct {
+ slap_overinst *on;
+ pguid_mod_t *mods;
+} pguid_repair_cb_t;
+
+static int
+pguid_repair_cb( Operation *op, SlapReply *rs )
+{
+ int rc;
+ pguid_repair_cb_t *pcb = op->o_callback->sc_private;
+ Entry *e = NULL;
+ Attribute *a;
+ struct berval pdn, pndn;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ return rs->sr_err;
+
+ default:
+ assert( 0 );
+ }
+
+ assert( rs->sr_entry != NULL );
+
+ dnParent( &rs->sr_entry->e_name, &pdn );
+ dnParent( &rs->sr_entry->e_nname, &pndn );
+
+ rc = overlay_entry_get_ov( op, &pndn, NULL, slap_schema.si_ad_entryUUID, 0, &e, pcb->on );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_repair_cb: unable to get parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+ return 0;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_repair_cb: unable to find entryUUID of parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+
+ } else {
+ ber_len_t len;
+ pguid_mod_t *mod;
+
+ assert( a->a_numvals == 1 );
+
+ len = sizeof( pguid_mod_t ) + rs->sr_entry->e_nname.bv_len + 1 + a->a_vals[0].bv_len + 1;
+ mod = op->o_tmpalloc( len, op->o_tmpmemctx );
+ mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ mod->ndn.bv_val = (char *)&mod[1];
+ mod->pguid.bv_len = a->a_vals[0].bv_len;
+ mod->pguid.bv_val = (char *)&mod->ndn.bv_val[mod->ndn.bv_len + 1];
+ lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+ lutil_strncopy( mod->pguid.bv_val, a->a_vals[0].bv_val, a->a_vals[0].bv_len );
+
+ mod->next = pcb->mods;
+ pcb->mods = mod;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: pguid_repair_cb: scheduling entry DN=\"%s\" for repair\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+ }
+
+ if ( e != NULL ) {
+ (void)overlay_entry_release_ov( op, e, 0, pcb->on );
+ }
+
+ return 0;
+}
+
+static int
+pguid_repair( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ void *ctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback sc = { 0 };
+ pguid_repair_cb_t pcb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ pguid_mod_t *pmod;
+ int nrepaired = 0;
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBORDINATE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_parentUUID->ad_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(!(%s=*))", ad_parentUUID->ad_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = pguid_repair_cb;
+ sc.sc_private = &pcb;
+ pcb.on = on;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+ memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
+
+ for ( pmod = pcb.mods; pmod != NULL; ) {
+ pguid_mod_t *pnext;
+
+ Modifications *mod;
+ SlapReply rs2 = { REP_RESULT };
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_desc = ad_parentUUID;
+ mod->sml_type = ad_parentUUID->ad_cname;
+ mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 1;
+ mod->sml_next = NULL;
+
+ ber_dupbv( &mod->sml_values[0], &pmod->pguid );
+ BER_BVZERO( &mod->sml_values[1] );
+
+ op->o_req_dn = pmod->ndn;
+ op->o_req_ndn = pmod->ndn;
+
+ op->orm_modlist = mod;
+ op->o_bd->be_modify( op, &rs2 );
+ slap_mods_free( op->orm_modlist, 1 );
+ if ( rs2.sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: pguid_repair: entry DN=\"%s\" repaired\n",
+ op->o_log_prefix, pmod->ndn.bv_val );
+ nrepaired++;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_repair: entry DN=\"%s\" repair failed (%d)\n",
+ op->o_log_prefix, pmod->ndn.bv_val, rs2.sr_err );
+ }
+
+ pnext = pmod->next;
+ op->o_tmpfree( pmod, op->o_tmpmemctx );
+ pmod = pnext;
+ }
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "pguid: repaired=%d\n", nrepaired );
+
+ return rs.sr_err;
+}
+
+/* search all entries without parentUUID; "repair" them */
+static int
+pguid_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ pguid_repair( be );
+
+ return 0;
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.59 "
+ "NAME 'parentUUID' "
+ "DESC 'the value of the entryUUID of the parent' "
+ "EQUALITY UUIDMatch "
+ "ORDERING UUIDOrderingMatch "
+ "SYNTAX 1.3.6.1.1.16.1 "
+ "USAGE dSAOperation "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ ")",
+ &ad_parentUUID },
+ { NULL }
+};
+
+int
+pguid_initialize(void)
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "pguid_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
+ (*as[ i ].adp)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+
+ pguid.on_bi.bi_type = "pguid";
+
+ pguid.on_bi.bi_op_add = pguid_op_add;
+ pguid.on_bi.bi_op_modrdn = pguid_op_rename;
+
+ pguid.on_bi.bi_db_init = pguid_db_init;
+ pguid.on_bi.bi_db_open = pguid_db_open;
+
+ return overlay_register( &pguid );
+}
+
+#if SLAPD_OVER_PGUID == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return pguid_initialize();
+}
+#endif /* SLAPD_OVER_PGUID == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_PGUID */
diff --git a/contrib/slapd-modules/samba4/rdnval.c b/contrib/slapd-modules/samba4/rdnval.c
new file mode 100644
index 0000000..dfe0e47
--- /dev/null
+++ b/contrib/slapd-modules/samba4/rdnval.c
@@ -0,0 +1,657 @@
+/* rdnval.c - RDN value overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RDNVAL
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * Maintain an attribute (rdnValue) that contains the values of each AVA
+ * that builds up the RDN of an entry. This is required for interoperation
+ * with Samba4. It mimics the "name" attribute provided by Active Directory.
+ * The naming attributes must be directoryString-valued, or compatible.
+ * For example, IA5String values are cast into directoryString unless
+ * consisting of the empty string ("").
+ */
+
+static AttributeDescription *ad_rdnValue;
+static Syntax *syn_IA5String;
+
+static slap_overinst rdnval;
+
+static int
+rdnval_is_valid( AttributeDescription *desc, struct berval *value )
+{
+ if ( desc->ad_type->sat_syntax == slap_schema.si_syn_directoryString ) {
+ return 1;
+ }
+
+ if ( desc->ad_type->sat_syntax == syn_IA5String
+ && !BER_BVISEMPTY( value ) )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+rdnval_unique_check_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ int *p = (int *)op->o_callback->sc_private;
+ (*p)++;
+ }
+
+ return 0;
+}
+
+static int
+rdnval_unique_check( Operation *op, BerVarray vals )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ BackendDB db = *op->o_bd;
+ Operation op2 = *op;
+ SlapReply rs2 = { 0 };
+ int i;
+ BerVarray fvals;
+ char *ptr;
+ int gotit = 0;
+ slap_callback cb = { 0 };
+
+ /* short-circuit attempts to add suffix entry */
+ if ( op->o_tag == LDAP_REQ_ADD
+ && be_issuffix( op->o_bd, &op->o_req_ndn ) )
+ {
+ return LDAP_SUCCESS;
+ }
+
+ op2.o_bd = &db;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.o_callback = &cb;
+ cb.sc_response = rdnval_unique_check_cb;
+ cb.sc_private = (void *)&gotit;
+
+ dnParent( &op->o_req_ndn, &op2.o_req_dn );
+ op2.o_req_ndn = op2.o_req_dn;
+
+ op2.ors_limit = NULL;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_attrsonly = 1;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_scope = LDAP_SCOPE_ONELEVEL;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ )
+ /* just count */ ;
+
+ fvals = op->o_tmpcalloc( sizeof( struct berval ), i + 1,
+ op->o_tmpmemctx );
+
+ op2.ors_filterstr.bv_len = 0;
+ if ( i > 1 ) {
+ op2.ors_filterstr.bv_len = STRLENOF( "(&)" );
+ }
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ ldap_bv2escaped_filter_value_x( &vals[ i ], &fvals[ i ],
+ 1, op->o_tmpmemctx );
+ op2.ors_filterstr.bv_len += ad_rdnValue->ad_cname.bv_len
+ + fvals[ i ].bv_len + STRLENOF( "(=)" );
+ }
+
+ op2.ors_filterstr.bv_val = op->o_tmpalloc( op2.ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+
+ ptr = op2.ors_filterstr.bv_val;
+ if ( i > 1 ) {
+ ptr = lutil_strcopy( ptr, "(&" );
+ }
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ *ptr++ = '(';
+ ptr = lutil_strncopy( ptr, ad_rdnValue->ad_cname.bv_val, ad_rdnValue->ad_cname.bv_len );
+ *ptr++ = '=';
+ ptr = lutil_strncopy( ptr, fvals[ i ].bv_val, fvals[ i ].bv_len );
+ *ptr++ = ')';
+ }
+
+ if ( i > 1 ) {
+ *ptr++ = ')';
+ }
+ *ptr = '\0';
+
+ assert( ptr == op2.ors_filterstr.bv_val + op2.ors_filterstr.bv_len );
+ op2.ors_filter = str2filter_x( op, op2.ors_filterstr.bv_val );
+ assert( op2.ors_filter != NULL );
+
+ (void)op2.o_bd->be_search( &op2, &rs2 );
+
+ filter_free_x( op, op2.ors_filter, 1 );
+ op->o_tmpfree( op2.ors_filterstr.bv_val, op->o_tmpmemctx );
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ if ( vals[ i ].bv_val != fvals[ i ].bv_val ) {
+ op->o_tmpfree( fvals[ i ].bv_val, op->o_tmpmemctx );
+ }
+ }
+ op->o_tmpfree( fvals, op->o_tmpmemctx );
+
+ if ( rs2.sr_err != LDAP_SUCCESS || gotit > 0 ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+rdnval_rdn2vals(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *dn,
+ struct berval *ndn,
+ BerVarray *valsp,
+ BerVarray *nvalsp,
+ int *numvalsp )
+{
+ LDAPRDN rdn = NULL, nrdn = NULL;
+ int nAVA, i;
+
+ assert( *valsp == NULL );
+ assert( *nvalsp == NULL );
+
+ *numvalsp = 0;
+
+ if ( ldap_bv2rdn_x( dn, &rdn, (char **)&rs->sr_text,
+ LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: can't figure out "
+ "type(s)/value(s) of rdn DN=\"%s\"\n",
+ op->o_log_prefix, dn->bv_val );
+ rs->sr_err = LDAP_INVALID_DN_SYNTAX;
+ rs->sr_text = "unknown type(s) used in RDN";
+
+ goto done;
+ }
+
+ if ( ldap_bv2rdn_x( ndn, &nrdn,
+ (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: can't figure out "
+ "type(s)/value(s) of normalized rdn DN=\"%s\"\n",
+ op->o_log_prefix, ndn->bv_val );
+ rs->sr_err = LDAP_INVALID_DN_SYNTAX;
+ rs->sr_text = "unknown type(s) used in RDN";
+
+ goto done;
+ }
+
+ for ( nAVA = 0; rdn[ nAVA ]; nAVA++ )
+ /* count'em */ ;
+
+ /* NOTE: we assume rdn and nrdn contain the same AVAs! */
+
+ *valsp = ch_calloc( sizeof( struct berval ), nAVA + 1 );
+ *nvalsp = ch_calloc( sizeof( struct berval ), nAVA + 1 );
+
+ /* Add new attribute values to the entry */
+ for ( i = 0; rdn[ i ]; i++ ) {
+ AttributeDescription *desc = NULL;
+
+ rs->sr_err = slap_bv2ad( &rdn[ i ]->la_attr,
+ &desc, &rs->sr_text );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: %s: %s\n",
+ op->o_log_prefix,
+ rs->sr_text,
+ rdn[ i ]->la_attr.bv_val );
+ goto done;
+ }
+
+ if ( !rdnval_is_valid( desc, &rdn[ i ]->la_value ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: syntax of naming attribute '%s' "
+ "not compatible with directoryString",
+ op->o_log_prefix, rdn[ i ]->la_attr.bv_val );
+ continue;
+ }
+
+ if ( value_find_ex( desc,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ *nvalsp,
+ &nrdn[ i ]->la_value,
+ op->o_tmpmemctx )
+ == LDAP_NO_SUCH_ATTRIBUTE )
+ {
+ ber_dupbv( &(*valsp)[ *numvalsp ], &rdn[ i ]->la_value );
+ ber_dupbv( &(*nvalsp)[ *numvalsp ], &nrdn[ i ]->la_value );
+
+ (*numvalsp)++;
+ }
+ }
+
+ if ( rdnval_unique_check( op, *valsp ) != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "rdnValue not unique within siblings";
+ goto done;
+ }
+
+done:;
+ if ( rdn != NULL ) {
+ ldap_rdnfree_x( rdn, op->o_tmpmemctx );
+ }
+
+ if ( nrdn != NULL ) {
+ ldap_rdnfree_x( nrdn, op->o_tmpmemctx );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( *valsp != NULL ) {
+ ber_bvarray_free( *valsp );
+ ber_bvarray_free( *nvalsp );
+ *valsp = NULL;
+ *nvalsp = NULL;
+ *numvalsp = 0;
+ }
+ }
+
+ return rs->sr_err;
+}
+
+static int
+rdnval_op_add( Operation *op, SlapReply *rs )
+{
+ Attribute *a, **ap;
+ int numvals = 0;
+ BerVarray vals = NULL, nvals = NULL;
+ int rc;
+
+ /* NOTE: should we accept an entry still in mods format? */
+ assert( op->ora_e != NULL );
+
+ if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( op->ora_e->e_attrs, ad_rdnValue );
+ if ( a != NULL ) {
+ /* TODO: check consistency? */
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = rdnval_rdn2vals( op, rs, &op->ora_e->e_name, &op->ora_e->e_nname,
+ &vals, &nvals, &numvals );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ }
+
+ a = attr_alloc( ad_rdnValue );
+
+ a->a_vals = vals;
+ a->a_nvals = nvals;
+ a->a_numvals = numvals;
+
+ for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* goto tail */ ;
+
+ *ap = a;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rdnval_op_rename( Operation *op, SlapReply *rs )
+{
+ Modifications *ml, **mlp;
+ int numvals = 0;
+ BerVarray vals = NULL, nvals = NULL;
+ struct berval old;
+ int rc;
+
+ dnRdn( &op->o_req_ndn, &old );
+ if ( dn_match( &old, &op->orr_nnewrdn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = rdnval_rdn2vals( op, rs, &op->orr_newrdn, &op->orr_nnewrdn,
+ &vals, &nvals, &numvals );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ }
+
+ ml = ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_values = vals;
+ ml->sml_nvalues = nvals;
+
+ ml->sml_numvals = numvals;
+
+ ml->sml_op = LDAP_MOD_REPLACE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = ad_rdnValue;
+ ml->sml_type = ad_rdnValue->ad_cname;
+
+ for ( mlp = &op->orr_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next )
+ /* goto tail */ ;
+
+ *mlp = ml;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rdnval_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval_db_init: rdnval cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval_db_init: database must have suffix\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
+ be->be_suffix[ 0 ].bv_val );
+ }
+
+ return 0;
+}
+
+typedef struct rdnval_mod_t {
+ struct berval ndn;
+ BerVarray vals;
+ BerVarray nvals;
+ int numvals;
+ struct rdnval_mod_t *next;
+} rdnval_mod_t;
+
+typedef struct {
+ BackendDB *bd;
+ rdnval_mod_t *mods;
+} rdnval_repair_cb_t;
+
+static int
+rdnval_repair_cb( Operation *op, SlapReply *rs )
+{
+ int rc;
+ rdnval_repair_cb_t *rcb = op->o_callback->sc_private;
+ rdnval_mod_t *mod;
+ BerVarray vals = NULL, nvals = NULL;
+ int numvals = 0;
+ ber_len_t len;
+ BackendDB *save_bd = op->o_bd;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ return rs->sr_err;
+
+ default:
+ assert( 0 );
+ }
+
+ assert( rs->sr_entry != NULL );
+
+ op->o_bd = rcb->bd;
+ rc = rdnval_rdn2vals( op, rs, &rs->sr_entry->e_name, &rs->sr_entry->e_nname,
+ &vals, &nvals, &numvals );
+ op->o_bd = save_bd;
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ len = sizeof( rdnval_mod_t ) + rs->sr_entry->e_nname.bv_len + 1;
+ mod = op->o_tmpalloc( len, op->o_tmpmemctx );
+ mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ mod->ndn.bv_val = (char *)&mod[1];
+ lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+ mod->vals = vals;
+ mod->nvals = nvals;
+ mod->numvals = numvals;
+
+ mod->next = rcb->mods;
+ rcb->mods = mod;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair_cb: scheduling entry DN=\"%s\" for repair\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+
+ return 0;
+}
+
+static int
+rdnval_repair( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ void *ctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ BackendDB db;
+ slap_callback sc = { 0 };
+ rdnval_repair_cb_t rcb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ rdnval_mod_t *rmod;
+ int nrepaired = 0;
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) );
+
+ op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
+ assert( op->o_bd != NULL );
+ assert( op->o_bd->be_nsuffix != NULL );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_rdnValue->ad_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(!(%s=*))", ad_rdnValue->ad_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = rdnval_repair_cb;
+ sc.sc_private = &rcb;
+ rcb.bd = &db;
+ db = *be;
+ db.bd_info = (BackendInfo *)on;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+ memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
+
+ for ( rmod = rcb.mods; rmod != NULL; ) {
+ rdnval_mod_t *rnext;
+
+ Modifications *mod;
+ SlapReply rs2 = { REP_RESULT };
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_desc = ad_rdnValue;
+ mod->sml_type = ad_rdnValue->ad_cname;
+ mod->sml_values = rmod->vals;
+ mod->sml_nvalues = rmod->nvals;
+ mod->sml_numvals = rmod->numvals;
+ mod->sml_next = NULL;
+
+ op->o_req_dn = rmod->ndn;
+ op->o_req_ndn = rmod->ndn;
+
+ op->orm_modlist = mod;
+
+ op->o_bd->be_modify( op, &rs2 );
+
+ slap_mods_free( op->orm_modlist, 1 );
+ if ( rs2.sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair: entry DN=\"%s\" repaired\n",
+ op->o_log_prefix, rmod->ndn.bv_val );
+ nrepaired++;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: rdnval_repair: entry DN=\"%s\" repair failed (%d)\n",
+ op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err );
+ }
+
+ rnext = rmod->next;
+ op->o_tmpfree( rmod, op->o_tmpmemctx );
+ rmod = rnext;
+ }
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "rdnval: repaired=%d\n", nrepaired );
+
+ return 0;
+}
+
+/* search all entries without parentUUID; "repair" them */
+static int
+rdnval_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ return rdnval_repair( be );
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.58 "
+ "NAME 'rdnValue' "
+ "DESC 'the value of the naming attributes' "
+ "SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' "
+ "EQUALITY caseIgnoreMatch "
+ "USAGE dSAOperation "
+ "NO-USER-MODIFICATION "
+ ")",
+ &ad_rdnValue },
+ { NULL }
+};
+
+int
+rdnval_initialize(void)
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "rdnval_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
+ (*as[ i ].adp)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+
+ syn_IA5String = syn_find( "1.3.6.1.4.1.1466.115.121.1.26" );
+ if ( syn_IA5String == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "rdnval_initialize: unable to find syntax '1.3.6.1.4.1.1466.115.121.1.26' (IA5String)\n" );
+ return LDAP_OTHER;
+ }
+
+ rdnval.on_bi.bi_type = "rdnval";
+
+ rdnval.on_bi.bi_op_add = rdnval_op_add;
+ rdnval.on_bi.bi_op_modrdn = rdnval_op_rename;
+
+ rdnval.on_bi.bi_db_init = rdnval_db_init;
+ rdnval.on_bi.bi_db_open = rdnval_db_open;
+
+ return overlay_register( &rdnval );
+}
+
+#if SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return rdnval_initialize();
+}
+#endif /* SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_RDNVAL */
diff --git a/contrib/slapd-modules/samba4/vernum.c b/contrib/slapd-modules/samba4/vernum.c
new file mode 100644
index 0000000..d70dc92
--- /dev/null
+++ b/contrib/slapd-modules/samba4/vernum.c
@@ -0,0 +1,459 @@
+/* vernum.c - RDN value overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_VERNUM
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * Maintain an attribute (e.g. msDS-KeyVersionNumber) that consists
+ * in a counter of modifications of another attribute (e.g. unicodePwd).
+ */
+
+typedef struct vernum_t {
+ AttributeDescription *vn_attr;
+ AttributeDescription *vn_vernum;
+} vernum_t;
+
+static AttributeDescription *ad_msDS_KeyVersionNumber;
+
+static struct berval val_init = BER_BVC( "0" );
+static slap_overinst vernum;
+
+static int
+vernum_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ Attribute *a, **ap;
+ int rc;
+
+ /* NOTE: should we accept an entry still in mods format? */
+ assert( op->ora_e != NULL );
+
+ if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( op->ora_e->e_attrs, vn->vn_attr );
+ if ( a == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( attr_find( op->ora_e->e_attrs, vn->vn_vernum ) != NULL ) {
+ /* already present - leave it alone */
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_alloc( vn->vn_vernum );
+
+ value_add_one( &a->a_vals, &val_init );
+ a->a_nvals = a->a_vals;
+ a->a_numvals = 1;
+
+ for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* goto tail */ ;
+
+ *ap = a;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+vernum_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ Modifications *ml, **mlp;
+ struct berval val = BER_BVC( "1" );
+ int rc;
+ unsigned got = 0;
+
+ for ( ml = op->orm_modlist; ml != NULL; ml = ml->sml_next ) {
+ if ( ml->sml_desc == vn->vn_vernum ) {
+ /* already present - leave it alone
+ * (or should we increment it anyway?) */
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( ml->sml_desc == vn->vn_attr ) {
+ got = 1;
+ }
+ }
+
+ if ( !got ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( mlp = &op->orm_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next )
+ /* goto tail */ ;
+
+ /* ITS#6561 */
+#ifdef SLAP_MOD_ADD_IF_NOT_PRESENT
+ /* the initial value is only added if the vernum attr is not present */
+ ml = ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_values = ch_calloc( sizeof( struct berval ) , 2 );
+ value_add_one( &ml->sml_values, &val_init );
+ ml->sml_nvalues = NULL;
+ ml->sml_numvals = 1;
+ ml->sml_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = vn->vn_vernum;
+ ml->sml_type = vn->vn_vernum->ad_cname;
+
+ *mlp = ml;
+ mlp = &ml->sml_next;
+#endif /* SLAP_MOD_ADD_IF_NOT_PRESENT */
+
+ /* this increments by 1 the vernum attr */
+ ml = ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_values = ch_calloc( sizeof( struct berval ) , 2 );
+ value_add_one( &ml->sml_values, &val );
+ ml->sml_nvalues = NULL;
+ ml->sml_numvals = 1;
+ ml->sml_op = LDAP_MOD_INCREMENT;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = vn->vn_vernum;
+ ml->sml_type = vn->vn_vernum->ad_cname;
+
+ *mlp = ml;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+vernum_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ vernum_t *vn = NULL;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum_db_init: vernum cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum_db_init: database must have suffix\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
+ be->be_suffix[ 0 ].bv_val );
+ }
+
+ vn = (vernum_t *)ch_calloc( 1, sizeof( vernum_t ) );
+
+ on->on_bi.bi_private = (void *)vn;
+
+ return 0;
+}
+
+typedef struct vernum_mod_t {
+ struct berval ndn;
+ struct vernum_mod_t *next;
+} vernum_mod_t;
+
+typedef struct {
+ BackendDB *bd;
+ vernum_mod_t *mods;
+} vernum_repair_cb_t;
+
+static int
+vernum_repair_cb( Operation *op, SlapReply *rs )
+{
+ int rc;
+ vernum_repair_cb_t *rcb = op->o_callback->sc_private;
+ vernum_mod_t *mod;
+ ber_len_t len;
+ BackendDB *save_bd = op->o_bd;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ return rs->sr_err;
+
+ default:
+ assert( 0 );
+ }
+
+ assert( rs->sr_entry != NULL );
+
+ len = sizeof( vernum_mod_t ) + rs->sr_entry->e_nname.bv_len + 1;
+ mod = op->o_tmpalloc( len, op->o_tmpmemctx );
+ mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ mod->ndn.bv_val = (char *)&mod[1];
+ lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+
+ mod->next = rcb->mods;
+ rcb->mods = mod;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: vernum_repair_cb: scheduling entry DN=\"%s\" for repair\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+
+ return 0;
+}
+
+static int
+vernum_repair( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+ void *ctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ BackendDB db;
+ slap_callback sc = { 0 };
+ vernum_repair_cb_t rcb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ vernum_mod_t *rmod;
+ int nrepaired = 0;
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) );
+
+ op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
+ assert( op->o_bd != NULL );
+ assert( op->o_bd->be_nsuffix != NULL );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(&(=*)(!(=*)))" )
+ + vn->vn_attr->ad_cname.bv_len
+ + vn->vn_vernum->ad_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(&(%s=*)(!(%s=*)))",
+ vn->vn_attr->ad_cname.bv_val,
+ vn->vn_vernum->ad_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = vernum_repair_cb;
+ sc.sc_private = &rcb;
+ rcb.bd = &db;
+ db = *be;
+ db.bd_info = (BackendInfo *)on;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+ memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
+
+ for ( rmod = rcb.mods; rmod != NULL; ) {
+ vernum_mod_t *rnext;
+ Modifications mod;
+ struct berval vals[2] = { BER_BVNULL };
+ SlapReply rs2 = { REP_RESULT };
+
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_desc = vn->vn_vernum;
+ mod.sml_type = vn->vn_vernum->ad_cname;
+ mod.sml_values = vals;
+ mod.sml_values[0] = val_init;
+ mod.sml_nvalues = NULL;
+ mod.sml_numvals = 1;
+ mod.sml_next = NULL;
+
+ op->o_req_dn = rmod->ndn;
+ op->o_req_ndn = rmod->ndn;
+
+ op->orm_modlist = &mod;
+
+ op->o_bd->be_modify( op, &rs2 );
+
+ slap_mods_free( op->orm_modlist->sml_next, 1 );
+ if ( rs2.sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: vernum_repair: entry DN=\"%s\" repaired\n",
+ op->o_log_prefix, rmod->ndn.bv_val );
+ nrepaired++;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: vernum_repair: entry DN=\"%s\" repair failed (%d)\n",
+ op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err );
+ }
+
+ rnext = rmod->next;
+ op->o_tmpfree( rmod, op->o_tmpmemctx );
+ rmod = rnext;
+ }
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "vernum: repaired=%d\n", nrepaired );
+
+ return 0;
+}
+
+static int
+vernum_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ /* default: unicodePwd & msDS-KeyVersionNumber */
+ if ( vn->vn_attr == NULL ) {
+ const char *text = NULL;
+ int rc;
+
+ rc = slap_str2ad( "unicodePwd", &vn->vn_attr, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "vernum: unable to find attribute 'unicodePwd' (%d: %s)\n",
+ rc, text );
+ return 1;
+ }
+
+ vn->vn_vernum = ad_msDS_KeyVersionNumber;
+ }
+
+ return vernum_repair( be );
+}
+
+static int
+vernum_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ if ( vn ) {
+ ch_free( vn );
+ on->on_bi.bi_private = NULL;
+ }
+
+ return 0;
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.2.840.113556.1.4.1782 "
+ "NAME 'msDS-KeyVersionNumber' "
+ "DESC 'in the original specification the syntax is 2.5.5.9' "
+ "SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' "
+ "EQUALITY integerMatch "
+ "SINGLE-VALUE "
+ "USAGE dSAOperation "
+ "NO-USER-MODIFICATION "
+ ")",
+ &ad_msDS_KeyVersionNumber },
+ { NULL }
+};
+
+int
+vernum_initialize(void)
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "vernum_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
+ (*as[ i ].adp)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+
+ vernum.on_bi.bi_type = "vernum";
+
+ vernum.on_bi.bi_op_add = vernum_op_add;
+ vernum.on_bi.bi_op_modify = vernum_op_modify;
+
+ vernum.on_bi.bi_db_init = vernum_db_init;
+ vernum.on_bi.bi_db_open = vernum_db_open;
+ vernum.on_bi.bi_db_destroy = vernum_db_destroy;
+
+ return overlay_register( &vernum );
+}
+
+#if SLAPD_OVER_VERNUM == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return vernum_initialize();
+}
+#endif /* SLAPD_OVER_VERNUM == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_VERNUM */
diff --git a/contrib/slapd-modules/smbk5pwd/Makefile b/contrib/slapd-modules/smbk5pwd/Makefile
new file mode 100644
index 0000000..a1c2c8e
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/Makefile
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+SSL_INC =
+SSL_LIB = -lcrypto
+
+HEIMDAL_INC = -I/usr/heimdal/include
+HEIMDAL_LIB = -L/usr/heimdal/lib -lkrb5 -lkadm5srv
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+# Omit DO_KRB5, DO_SAMBA or DO_SHADOW if you don't want to support it.
+DEFS = -DDO_KRB5 -DDO_SAMBA -DDO_SHADOW
+INCS = $(LDAP_INC) $(HEIMDAL_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(HEIMDAL_LIB) $(SSL_LIB)
+
+PROGRAMS = smbk5pwd.la
+MANPAGES = slapo-smbk5pwd.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+smbk5pwd.la: smbk5pwd.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/smbk5pwd/README b/contrib/slapd-modules/smbk5pwd/README
new file mode 100644
index 0000000..2f02195
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/README
@@ -0,0 +1,94 @@
+This directory contains a slapd overlay, smbk5pwd, that extends the
+PasswordModify Extended Operation to update Kerberos keys and Samba
+password hashes for an LDAP user.
+
+The Kerberos support is written for Heimdal using its hdb-ldap backend.
+If a PasswordModify is performed on an entry that has the krb5KDCEntry
+objectclass, then the krb5Key and krb5KeyVersionNumber will be updated
+using the new password in the PasswordModify request. Additionally, a
+new "{K5KEY}" password hash mechanism is provided. For krb5KDCEntries that
+have this hash specifier in their userPassword attribute, Simple Binds
+will be checked against the Kerberos keys of the Entry. No data is
+needed after the "{K5KEY}" hash specifier in the userPassword, it is
+looked up from the Entry directly.
+
+The Samba support is written using the Samba 3.0 LDAP schema. If a
+PasswordModify is performed on an entry that has the sambaSamAccount
+objectclass, then the sambaNTPassword and sambaPwdLastSet attributes
+will be updated accordingly.
+
+To use the overlay, add:
+
+ include <path to>/krb5-kdc.schema
+ include <path to>/samba.schema
+
+ moduleload <path to>smbk5pwd.so
+ ...
+
+ database mdb
+ ...
+ overlay smbk5pwd
+
+to your slapd configuration file. (You should obtain the necessary schema
+files from the Heimdal and/or Samba distributions. At this time, there
+are several known errors in these schema files that you will have to
+correct before they will load in slapd. As of Samba 3.0 the schema looks
+fine as shipped.)
+
+All modules compiled in (i.e. krb5 and samba) are enabled; the statement
+
+ smbk5pwd-enable <module>
+
+can be used to enable only the desired one(s); legal values for <module>
+are "krb5", "samba" and "shadow", if they are respectively enabled by defining
+DO_KRB5, DO_SAMBA and DO_SHADOW.
+
+The samba module also supports the
+
+ smbk5pwd-must-change <seconds>
+
+which sets the "sambaPwdMustChange" attribute accordingly to force passwd
+expiry. A value of 0 disables this feature.
+
+The overlay now supports table-driven configuration, and thus can be run-time
+loaded and configured via back-config. The layout of the entry is
+
+ # {0}smbk5pwd, {1}bdb, config
+ dn: olcOverlay={0}smbk5pwd,olcDatabase={1}bdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcSmbK5PwdConfig
+ olcOverlay: {0}smbk5pwd
+ olcSmbK5PwdEnable: krb5
+ olcSmbK5PwdEnable: samba
+ olcSmbK5PwdMustChange: 2592000
+
+which enables both krb5 and samba modules with a password expiry time
+of 30 days.
+
+The provided Makefile builds both Kerberos and Samba support by default.
+You must edit the Makefile to insure that the correct include and library
+paths are used. You can change the DEFS macro if you only want one or the
+other of Kerberos or Samba support.
+
+This overlay is only set up to be built as a dynamically loaded module.
+On most platforms, in order for the module to be usable, all of the
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlay statically, you will have to move it into the
+slapd/overlays directory and edit the Makefile and overlays.c to reference
+it. You will also have to define SLAPD_OVER_SMBK5PWD to SLAPD_MOD_STATIC,
+and add the relevant libraries to the main slapd link command.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+Copyright 2004-2022 The OpenLDAP Foundation.
+Portions Copyright 2004-2005 Howard Chu, Symas Corp. 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>.
+
diff --git a/contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5 b/contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5
new file mode 100644
index 0000000..c9a0162
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5
@@ -0,0 +1,177 @@
+.TH SLAPO-SMBK5PWD 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-smbk5pwd \- Samba & Kerberos password sync overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+include
+.B "<path to>/krb5-kdc.schema"
+.LP
+include
+.B "<path to>/samba.schema"
+.LP
+moduleload
+.B smbk5pwd.so
+.LP
+ ...
+.LP
+database mdb
+.LP
+ ...
+.LP
+overlay
+.B smbk5pwd
+.RE
+
+.SH DESCRIPTION
+.LP
+The
+.B smbk5pwd
+overlay to
+.BR slapd (8)
+overloads the Password Modify Extended Operation (RFC 3062) to update
+Kerberos keys and Samba password hashes for an LDAP user, as well as
+updating password change related attributes for Kerberos, Samba and/or
+UNIX user accounts.
+.LP
+The Samba support is written using the Samba 3.0 LDAP schema;
+Kerberos support is written for Heimdal using its hdb-ldap backend.
+.LP
+Additionally, a new
+.B {K5KEY}
+password hash mechanism is provided.
+For
+.B krb5KDCEntry
+objects that have this scheme specifier in their
+.I userPassword
+attribute, Simple Binds will be checked against the Kerberos keys of the entry.
+No data is needed after the
+.B {K5KEY}
+scheme specifier in the
+.IR userPassword ,
+it is looked up from the entry directly.
+
+.SH CONFIGURATION
+The
+.B smbk5pwd
+overlay supports the following
+.B slapd.conf
+configuration options, which should appear after the
+.B overlay
+directive:
+.TP
+.BI smbk5pwd-enable " <module>"
+can be used to enable only the desired modules.
+Legal values for
+.I <module>
+are
+.LP
+.RS
+.TP
+.B krb5
+If the user has the
+.B krb5KDCEntry
+objectclass, update the
+.B krb5Key
+and
+.B krb5KeyVersionNumber
+attributes using the new password in the Password Modify operation,
+provided the Kerberos account is not expired.
+Exiration is determined by evaluating the
+.B krb5ValidEnd
+attribute.
+.TP
+.B samba
+If the user is a
+.B sambaSamAccount
+object, synchronize the
+.B sambaNTPassword
+to the password entered in the Password Modify operation, and update
+.B sambaPwdLastSet
+accordingly.
+.TP
+.B shadow
+Update the attribute
+.BR shadowLastChange ,
+if the entry has the objectclass
+.BR shadowAccount .
+.LP
+By default all modules compiled in are enabled.
+Setting the config statement restricts the enabled modules to the ones
+explicitly mentioned.
+.RE
+.TP
+.BI smbk5pwd-can-change " <seconds>"
+If the
+.B samba
+module is enabled and the user is a
+.BR sambaSamAccount ,
+update the attribute
+.B sambaPwdCanChange
+to point
+.I <seconds>
+into the future, essentially denying any Samba password change until then.
+A value of
+.B 0
+disables this feature.
+.TP
+.BI smbk5pwd-must-change " <seconds>"
+If the
+.B samba
+module is enabled and the user is a
+.BR sambaSamAccount ,
+update the attribute
+.B sambaPwdMustChange
+to point
+.I <seconds>
+into the future, essentially setting the Samba password expiration time.
+A value of
+.B 0
+disables this feature.
+.LP
+Alternatively, the overlay supports table-driven configuration,
+and thus can be run-time loaded and configured via back-config.
+
+.SH EXAMPLE
+The layout of a slapd.d based, table-driven configuration entry looks like:
+.LP
+.EX
+ # {0}smbk5pwd, {1}mdb, config
+ dn: olcOverlay={0}smbk5pwd,olcDatabase={1}mdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcSmbK5PwdConfig
+ olcOverlay: {0}smbk5pwd
+ olcSmbK5PwdEnable: krb5
+ olcSmbK5PwdEnable: samba
+ olcSmbK5PwdMustChange: 2592000
+.EE
+.LP
+which enables both
+.B krb5
+and
+.B samba
+modules with a Samba password expiration time of 30 days (=
+.B 2592000
+seconds).
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by Howard Chu.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
+
diff --git a/contrib/slapd-modules/smbk5pwd/smbk5pwd.c b/contrib/slapd-modules/smbk5pwd/smbk5pwd.c
new file mode 100644
index 0000000..642140d
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/smbk5pwd.c
@@ -0,0 +1,1084 @@
+/* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004-2005 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * Support for table-driven configuration added by Pierangelo Masarati.
+ * Support for sambaPwdMustChange and sambaPwdCanChange added by Marco D'Ettorre.
+ * Support for shadowLastChange added by SATOH Fumiyasu @ OSS Technology, Inc.
+ */
+
+#include <portable.h>
+
+#ifndef SLAPD_OVER_SMBK5PWD
+#define SLAPD_OVER_SMBK5PWD SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_OVER_SMBK5PWD
+
+#include <slap.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+
+#include "slap-config.h"
+
+#ifdef DO_KRB5
+#include <lber.h>
+#include <lber_pvt.h>
+#include <lutil.h>
+
+/* make ASN1_MALLOC_ENCODE use our allocator */
+#define malloc ch_malloc
+
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include <hdb.h>
+
+#ifndef HDB_INTERFACE_VERSION
+#define HDB_MASTER_KEY_SET master_key_set
+#else
+#define HDB_MASTER_KEY_SET hdb_master_key_set
+#endif
+
+static krb5_context context;
+static void *kadm_context;
+static kadm5_config_params conf;
+static HDB *db;
+
+static AttributeDescription *ad_krb5Key;
+static AttributeDescription *ad_krb5KeyVersionNumber;
+static AttributeDescription *ad_krb5PrincipalName;
+static AttributeDescription *ad_krb5ValidEnd;
+static ObjectClass *oc_krb5KDCEntry;
+#endif
+
+#ifdef DO_SAMBA
+#ifdef HAVE_GNUTLS
+#include <nettle/md4.h>
+#elif HAVE_OPENSSL
+#include <openssl/md4.h>
+#else
+#error Unsupported crypto backend.
+#endif
+#include "ldap_utf8.h"
+
+static AttributeDescription *ad_sambaNTPassword;
+static AttributeDescription *ad_sambaPwdLastSet;
+static AttributeDescription *ad_sambaPwdMustChange;
+static AttributeDescription *ad_sambaPwdCanChange;
+static ObjectClass *oc_sambaSamAccount;
+#endif
+
+#ifdef DO_SHADOW
+static AttributeDescription *ad_shadowLastChange;
+static ObjectClass *oc_shadowAccount;
+#endif
+
+/* Per-instance configuration information */
+typedef struct smbk5pwd_t {
+ unsigned mode;
+#define SMBK5PWD_F_KRB5 (0x1U)
+#define SMBK5PWD_F_SAMBA (0x2U)
+#define SMBK5PWD_F_SHADOW (0x4U)
+
+#define SMBK5PWD_DO_KRB5(pi) ((pi)->mode & SMBK5PWD_F_KRB5)
+#define SMBK5PWD_DO_SAMBA(pi) ((pi)->mode & SMBK5PWD_F_SAMBA)
+#define SMBK5PWD_DO_SHADOW(pi) ((pi)->mode & SMBK5PWD_F_SHADOW)
+
+#ifdef DO_KRB5
+ /* nothing yet */
+#endif
+
+#ifdef DO_SAMBA
+ /* How many seconds before forcing a password change? */
+ time_t smb_must_change;
+ /* How many seconds after allowing a password change? */
+ time_t smb_can_change;
+#endif
+
+#ifdef DO_SHADOW
+ /* nothing yet */
+#endif
+} smbk5pwd_t;
+
+static const unsigned SMBK5PWD_F_ALL =
+ 0
+#ifdef DO_KRB5
+ | SMBK5PWD_F_KRB5
+#endif
+#ifdef DO_SAMBA
+ | SMBK5PWD_F_SAMBA
+#endif
+#ifdef DO_SHADOW
+ | SMBK5PWD_F_SHADOW
+#endif
+;
+
+static int smbk5pwd_modules_init( smbk5pwd_t *pi );
+
+#ifdef DO_SAMBA
+static const char hex[] = "0123456789abcdef";
+
+#define MAX_PWLEN 256
+#define HASHLEN 16
+
+static void hexify(
+ const char in[HASHLEN],
+ struct berval *out
+)
+{
+ int i;
+ char *a;
+ unsigned char *b;
+
+ out->bv_val = ch_malloc(HASHLEN*2 + 1);
+ out->bv_len = HASHLEN*2;
+
+ a = out->bv_val;
+ b = (unsigned char *)in;
+ for (i=0; i<HASHLEN; i++) {
+ *a++ = hex[*b >> 4];
+ *a++ = hex[*b++ & 0x0f];
+ }
+ *a++ = '\0';
+}
+
+static void nthash(
+ struct berval *passwd,
+ struct berval *hash
+)
+{
+ /* Windows currently only allows 14 character passwords, but
+ * may support up to 256 in the future. We assume this means
+ * 256 UCS2 characters, not 256 bytes...
+ */
+ char hbuf[HASHLEN];
+#ifdef HAVE_OPENSSL
+ MD4_CTX ctx;
+#elif defined(HAVE_GNUTLS)
+ struct md4_ctx ctx;
+#endif
+
+ if (passwd->bv_len > MAX_PWLEN*2)
+ passwd->bv_len = MAX_PWLEN*2;
+
+#ifdef HAVE_OPENSSL
+ MD4_Init( &ctx );
+ MD4_Update( &ctx, passwd->bv_val, passwd->bv_len );
+ MD4_Final( (unsigned char *)hbuf, &ctx );
+#elif defined(HAVE_GNUTLS)
+ md4_init( &ctx );
+ md4_update( &ctx, passwd->bv_len, (unsigned char *)passwd->bv_val );
+ md4_digest( &ctx, sizeof(hbuf), (unsigned char *)hbuf );
+#endif
+
+ hexify( hbuf, hash );
+}
+#endif /* DO_SAMBA */
+
+#ifdef DO_KRB5
+
+static int smbk5pwd_op_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *cb;
+
+ /* clear out the current key */
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup,
+ NULL, 0, NULL, NULL );
+
+ /* free the callback */
+ cb = op->o_callback;
+ op->o_callback = cb->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ return 0;
+}
+
+static int smbk5pwd_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* If this is a simple Bind, stash the Op pointer so our chk
+ * function can find it. Set a cleanup callback to clear it
+ * out when the Bind completes.
+ */
+ if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
+ slap_callback *cb;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ smbk5pwd_op_cleanup, op, 0, NULL, NULL );
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = smbk5pwd_op_cleanup;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static LUTIL_PASSWD_CHK_FUNC k5key_chk;
+static LUTIL_PASSWD_HASH_FUNC k5key_hash;
+static const struct berval k5key_scheme = BER_BVC("{K5KEY}");
+
+/* This password scheme stores no data in the userPassword attribute
+ * other than the scheme name. It assumes the invoking entry is a
+ * krb5KDCentry and compares the passed-in credentials against the
+ * krb5Key attribute. The krb5Key may be multi-valued, but they are
+ * simply multiple keytypes generated from the same input string, so
+ * only the first value needs to be compared here.
+ *
+ * Since the lutil_passwd API doesn't pass the Entry object in, we
+ * have to fetch it ourselves in order to get access to the other
+ * attributes. We accomplish this with the help of the overlay's Bind
+ * function, which stores the current Operation pointer in thread-specific
+ * storage so we can retrieve it here. The Operation provides all
+ * the necessary context for us to get Entry from the database.
+ */
+static int k5key_chk(
+ const struct berval *sc,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ void *ctx, *op_tmp;
+ Operation *op;
+ int rc;
+ Entry *e;
+ Attribute *a;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_salt salt;
+ hdb_entry ent;
+
+ /* Find our thread context, find our Operation */
+ ctx = ldap_pvt_thread_pool_context();
+
+ if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, &op_tmp, NULL )
+ || !op_tmp )
+ return LUTIL_PASSWD_ERR;
+ op = op_tmp;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR;
+
+ rc = LUTIL_PASSWD_ERR;
+ do {
+ size_t l;
+ Key ekey = {0};
+
+ a = attr_find( e->e_attrs, ad_krb5PrincipalName );
+ if (!a ) break;
+
+ memset( &ent, 0, sizeof(ent) );
+ ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
+ if ( ret ) break;
+
+ a = attr_find( e->e_attrs, ad_krb5ValidEnd );
+ if (a) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ if ( lutil_parsetime( a->a_vals[0].bv_val, &tm ) == 0 &&
+ lutil_tm2time( &tm, &tt ) == 0 && tt.tt_sec < op->o_time ) {
+ /* Account is expired */
+ rc = LUTIL_PASSWD_ERR;
+ break;
+ }
+ }
+
+ krb5_get_pw_salt( context, ent.principal, &salt );
+ krb5_free_principal( context, ent.principal );
+
+ a = attr_find( e->e_attrs, ad_krb5Key );
+ if ( !a ) break;
+
+ ent.keys.len = 1;
+ ent.keys.val = &ekey;
+ decode_Key((unsigned char *) a->a_vals[0].bv_val,
+ (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l);
+ if ( db->HDB_MASTER_KEY_SET )
+ hdb_unseal_keys( context, db, &ent );
+
+ krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val,
+ salt, &key );
+
+ krb5_free_salt( context, salt );
+
+ if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data,
+ key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK;
+
+ krb5_free_keyblock_contents( context, &key );
+ krb5_free_keyblock_contents( context, &ekey.key );
+
+ } while(0);
+ be_entry_release_r( op, e );
+ return rc;
+}
+
+static int k5key_hash(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ ber_dupbv( hash, (struct berval *)&k5key_scheme );
+ return LUTIL_PASSWD_OK;
+}
+#endif /* DO_KRB5 */
+
+static int smbk5pwd_exop_passwd(
+ Operation *op,
+ SlapReply *rs )
+{
+ int rc;
+ req_pwdexop_s *qpw = &op->oq_pwdexop;
+ Entry *e;
+ Modifications *ml;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ smbk5pwd_t *pi = on->on_bi.bi_private;
+ char term;
+
+ /* Not the operation we expected, pass it on... */
+ if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ term = qpw->rs_new.bv_val[qpw->rs_new.bv_len];
+ qpw->rs_new.bv_val[qpw->rs_new.bv_len] = '\0';
+
+#ifdef DO_KRB5
+ /* Kerberos stuff */
+ do {
+ krb5_error_code ret;
+ hdb_entry ent;
+ struct berval *keys;
+ size_t nkeys;
+ int kvno, i;
+ Attribute *a;
+
+ if ( !SMBK5PWD_DO_KRB5( pi ) ) break;
+
+ if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break;
+
+ a = attr_find( e->e_attrs, ad_krb5PrincipalName );
+ if ( !a ) break;
+
+ memset( &ent, 0, sizeof(ent) );
+ ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
+ if ( ret ) break;
+
+ a = attr_find( e->e_attrs, ad_krb5KeyVersionNumber );
+ kvno = 0;
+ if ( a ) {
+ if ( lutil_atoi( &kvno, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: "
+ "dn=\"%s\" unable to parse krb5KeyVersionNumber=\"%s\"\n",
+ op->o_log_prefix, e->e_name.bv_val, a->a_vals[0].bv_val );
+ }
+
+ } else {
+ /* shouldn't happen, this is a required attr */
+ Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: "
+ "dn=\"%s\" missing krb5KeyVersionNumber\n",
+ op->o_log_prefix, e->e_name.bv_val );
+ }
+
+ ret = hdb_generate_key_set_password(context, ent.principal,
+ qpw->rs_new.bv_val, &ent.keys.val, &nkeys);
+ ent.keys.len = nkeys;
+ hdb_seal_keys(context, db, &ent);
+ krb5_free_principal( context, ent.principal );
+
+ keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval));
+
+ for (i = 0; i < ent.keys.len; i++) {
+ unsigned char *buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret);
+ if (ret != 0)
+ break;
+
+ keys[i].bv_val = (char *)buf;
+ keys[i].bv_len = len;
+ }
+ BER_BVZERO( &keys[i] );
+
+ hdb_free_keys(context, ent.keys.len, ent.keys.val);
+
+ if ( i != ent.keys.len ) {
+ ber_bvarray_free( keys );
+ break;
+ }
+
+ ml = ch_malloc(sizeof(Modifications));
+ if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ ml->sml_desc = ad_krb5Key;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = i;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ ml->sml_desc = ad_krb5KeyVersionNumber;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = ch_malloc( 2 * sizeof(struct berval));
+ ml->sml_values[0].bv_val = ch_malloc( 64 );
+ ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val,
+ "%d", kvno+1 );
+ BER_BVZERO( &ml->sml_values[1] );
+ ml->sml_nvalues = NULL;
+ } while ( 0 );
+#endif /* DO_KRB5 */
+
+#ifdef DO_SAMBA
+ /* Samba stuff */
+ if ( SMBK5PWD_DO_SAMBA( pi ) && is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) {
+ struct berval *keys;
+ ber_len_t j,l;
+ wchar_t *wcs, wc;
+ char *c;
+ struct berval pwd;
+
+ /* Expand incoming UTF8 string to UCS4 */
+ l = ldap_utf8_chars(qpw->rs_new.bv_val);
+ wcs = ch_malloc((l+1) * sizeof(wchar_t));
+
+ ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l );
+
+ /* Truncate UCS4 to UCS2 */
+ c = (char *)wcs;
+ for (j=0; j<l; j++) {
+ wc = wcs[j];
+ *c++ = wc & 0xff;
+ *c++ = (wc >> 8) & 0xff;
+ }
+ *c++ = 0;
+ pwd.bv_val = (char *)wcs;
+ pwd.bv_len = l * 2;
+
+ ml = ch_malloc(sizeof(Modifications));
+ if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ BER_BVZERO( &keys[1] );
+ nthash( &pwd, keys );
+
+ ml->sml_desc = ad_sambaNTPassword;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+
+ ch_free(wcs);
+
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", slap_get_time());
+ BER_BVZERO( &keys[1] );
+
+ ml->sml_desc = ad_sambaPwdLastSet;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+
+ if (pi->smb_must_change)
+ {
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", slap_get_time() + pi->smb_must_change);
+ BER_BVZERO( &keys[1] );
+
+ ml->sml_desc = ad_sambaPwdMustChange;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+ }
+
+ if (pi->smb_can_change)
+ {
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", slap_get_time() + pi->smb_can_change);
+ BER_BVZERO( &keys[1] );
+
+ ml->sml_desc = ad_sambaPwdCanChange;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+ }
+ }
+#endif /* DO_SAMBA */
+
+#ifdef DO_SHADOW
+ /* shadow stuff */
+ if ( SMBK5PWD_DO_SHADOW( pi ) && is_entry_objectclass(e, oc_shadowAccount, 0 ) ) {
+ struct berval *keys;
+
+ ml = ch_malloc(sizeof(Modifications));
+ if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( sizeof(struct berval) * 2);
+ BER_BVZERO( &keys[1] );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", (long)(slap_get_time() / (60 * 60 * 24)));
+
+ ml->sml_desc = ad_shadowLastChange;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+ }
+#endif /* DO_SHADOW */
+
+ be_entry_release_r( op, e );
+ qpw->rs_new.bv_val[qpw->rs_new.bv_len] = term;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst smbk5pwd;
+
+/* back-config stuff */
+enum {
+ PC_SMB_MUST_CHANGE = 1,
+ PC_SMB_CAN_CHANGE,
+ PC_SMB_ENABLE
+};
+
+static ConfigDriver smbk5pwd_cf_func;
+
+/*
+ * NOTE: uses OID arcs OLcfgCtAt:1 and OLcfgCtOc:1
+ */
+
+static ConfigTable smbk5pwd_cfats[] = {
+ { "smbk5pwd-enable", "arg",
+ 2, 0, 0, ARG_MAGIC|PC_SMB_ENABLE, smbk5pwd_cf_func,
+ "( OLcfgCtAt:1.1 NAME 'olcSmbK5PwdEnable' "
+ "DESC 'Modules to be enabled' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "smbk5pwd-must-change", "time",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_MUST_CHANGE, smbk5pwd_cf_func,
+ "( OLcfgCtAt:1.2 NAME 'olcSmbK5PwdMustChange' "
+ "DESC 'Credentials validity interval' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "smbk5pwd-can-change", "time",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_CAN_CHANGE, smbk5pwd_cf_func,
+ "( OLcfgCtAt:1.3 NAME 'olcSmbK5PwdCanChange' "
+ "DESC 'Credentials minimum validity interval' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs smbk5pwd_cfocs[] = {
+ { "( OLcfgCtOc:1.1 "
+ "NAME 'olcSmbK5PwdConfig' "
+ "DESC 'smbk5pwd overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcSmbK5PwdEnable "
+ "$ olcSmbK5PwdMustChange "
+ "$ olcSmbK5PwdCanChange "
+ ") )", Cft_Overlay, smbk5pwd_cfats },
+
+ { NULL, 0, NULL }
+};
+
+/*
+ * add here other functionalities; handle their initialization
+ * as appropriate in smbk5pwd_modules_init().
+ */
+static slap_verbmasks smbk5pwd_modules[] = {
+ { BER_BVC( "krb5" ), SMBK5PWD_F_KRB5 },
+ { BER_BVC( "samba" ), SMBK5PWD_F_SAMBA },
+ { BER_BVC( "shadow" ), SMBK5PWD_F_SHADOW },
+ { BER_BVNULL, -1 }
+};
+
+static int
+smbk5pwd_cf_func( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+
+ int rc = 0;
+ smbk5pwd_t *pi = on->on_bi.bi_private;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case PC_SMB_MUST_CHANGE:
+#ifdef DO_SAMBA
+ c->value_int = pi->smb_must_change;
+#else /* ! DO_SAMBA */
+ c->value_int = 0;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_CAN_CHANGE:
+#ifdef DO_SAMBA
+ c->value_int = pi->smb_can_change;
+#else /* ! DO_SAMBA */
+ c->value_int = 0;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_ENABLE:
+ c->rvalue_vals = NULL;
+ if ( pi->mode ) {
+ mask_to_verbs( smbk5pwd_modules, pi->mode, &c->rvalue_vals );
+ if ( c->rvalue_vals == NULL ) {
+ rc = 1;
+ }
+ }
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case PC_SMB_MUST_CHANGE:
+ break;
+
+ case PC_SMB_CAN_CHANGE:
+ break;
+
+ case PC_SMB_ENABLE:
+ if ( !c->line ) {
+ pi->mode = 0;
+
+ } else {
+ int i;
+
+ i = verb_to_mask( c->line, smbk5pwd_modules );
+ pi->mode &= ~smbk5pwd_modules[i].mask;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case PC_SMB_MUST_CHANGE:
+#ifdef DO_SAMBA
+ if ( c->value_int < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> invalid negative value \"%d\".",
+ c->log, c->argv[ 0 ], c->value_int );
+ return 1;
+ }
+ pi->smb_must_change = c->value_int;
+#else /* ! DO_SAMBA */
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> only meaningful "
+ "when compiled with -DDO_SAMBA.\n",
+ c->log, c->argv[ 0 ] );
+ return 1;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_CAN_CHANGE:
+#ifdef DO_SAMBA
+ if ( c->value_int < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> invalid negative value \"%d\".",
+ c->log, c->argv[ 0 ], c->value_int );
+ return 1;
+ }
+ pi->smb_can_change = c->value_int;
+#else /* ! DO_SAMBA */
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> only meaningful "
+ "when compiled with -DDO_SAMBA.\n",
+ c->log, c->argv[ 0 ] );
+ return 1;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_ENABLE: {
+ slap_mask_t mode = pi->mode, m = 0;
+
+ rc = verbs_to_mask( c->argc, c->argv, smbk5pwd_modules, &m );
+ if ( rc > 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> unknown module \"%s\".\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ return 1;
+ }
+
+ /* we can hijack the smbk5pwd_t structure because
+ * from within the configuration, this is the only
+ * active thread. */
+ pi->mode |= m;
+
+#ifndef DO_KRB5
+ if ( SMBK5PWD_DO_KRB5( pi ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> module \"%s\" only allowed when compiled with -DDO_KRB5.\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ pi->mode = mode;
+ return 1;
+ }
+#endif /* ! DO_KRB5 */
+
+#ifndef DO_SAMBA
+ if ( SMBK5PWD_DO_SAMBA( pi ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> module \"%s\" only allowed when compiled with -DDO_SAMBA.\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ pi->mode = mode;
+ return 1;
+ }
+#endif /* ! DO_SAMBA */
+
+#ifndef DO_SHADOW
+ if ( SMBK5PWD_DO_SHADOW( pi ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> module \"%s\" only allowed when compiled with -DDO_SHADOW.\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ pi->mode = mode;
+ return 1;
+ }
+#endif /* ! DO_SHADOW */
+
+ /* Re-initialize the module, because
+ * the configuration might have changed */
+ rc = smbk5pwd_modules_init( pi );
+ if ( rc ) {
+ pi->mode = mode;
+ return 1;
+ }
+
+ } break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+ return rc;
+}
+
+static int
+smbk5pwd_modules_init( smbk5pwd_t *pi )
+{
+ static struct {
+ const char *name;
+ AttributeDescription **adp;
+ }
+#ifdef DO_KRB5
+ krb5_ad[] = {
+ { "krb5Key", &ad_krb5Key },
+ { "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber },
+ { "krb5PrincipalName", &ad_krb5PrincipalName },
+ { "krb5ValidEnd", &ad_krb5ValidEnd },
+ { NULL }
+ },
+#endif /* DO_KRB5 */
+#ifdef DO_SAMBA
+ samba_ad[] = {
+ { "sambaNTPassword", &ad_sambaNTPassword },
+ { "sambaPwdLastSet", &ad_sambaPwdLastSet },
+ { "sambaPwdMustChange", &ad_sambaPwdMustChange },
+ { "sambaPwdCanChange", &ad_sambaPwdCanChange },
+ { NULL }
+ },
+#endif /* DO_SAMBA */
+#ifdef DO_SHADOW
+ shadow_ad[] = {
+ { "shadowLastChange", &ad_shadowLastChange },
+ { NULL }
+ },
+#endif /* DO_SHADOW */
+ dummy_ad;
+
+ /* this is to silence the unused var warning */
+ (void) dummy_ad;
+
+#ifdef DO_KRB5
+ if ( SMBK5PWD_DO_KRB5( pi ) && oc_krb5KDCEntry == NULL ) {
+ krb5_error_code ret;
+ extern HDB *_kadm5_s_get_db(void *);
+
+ int i, rc;
+
+ /* Make sure all of our necessary schema items are loaded */
+ oc_krb5KDCEntry = oc_find( "krb5KDCEntry" );
+ if ( !oc_krb5KDCEntry ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"krb5KDCEntry\" objectClass.\n" );
+ return -1;
+ }
+
+ for ( i = 0; krb5_ad[ i ].name != NULL; i++ ) {
+ const char *text;
+
+ *(krb5_ad[ i ].adp) = NULL;
+
+ rc = slap_str2ad( krb5_ad[ i ].name, krb5_ad[ i ].adp, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"%s\" attributeType: %s (%d).\n",
+ krb5_ad[ i ].name, text, rc );
+ oc_krb5KDCEntry = NULL;
+ return rc;
+ }
+ }
+
+ /* Initialize Kerberos context */
+ ret = krb5_init_context(&context);
+ if (ret) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to initialize krb5 context (%d).\n",
+ ret );
+ oc_krb5KDCEntry = NULL;
+ return -1;
+ }
+
+ ret = kadm5_s_init_with_password_ctx( context,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0, &kadm_context );
+ if (ret) {
+ char *err_str, *err_msg = "<unknown error>";
+ err_str = krb5_get_error_string( context );
+ if (!err_str)
+ err_msg = (char *)krb5_get_err_text( context, ret );
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to initialize krb5 admin context: %s (%d).\n",
+ err_str ? err_str : err_msg, ret );
+ if (err_str)
+ krb5_free_error_string( context, err_str );
+ krb5_free_context( context );
+ oc_krb5KDCEntry = NULL;
+ return -1;
+ }
+
+ db = _kadm5_s_get_db( kadm_context );
+ }
+#endif /* DO_KRB5 */
+
+#ifdef DO_SAMBA
+ if ( SMBK5PWD_DO_SAMBA( pi ) && oc_sambaSamAccount == NULL ) {
+ int i, rc;
+
+ oc_sambaSamAccount = oc_find( "sambaSamAccount" );
+ if ( !oc_sambaSamAccount ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"sambaSamAccount\" objectClass.\n" );
+ return -1;
+ }
+
+ for ( i = 0; samba_ad[ i ].name != NULL; i++ ) {
+ const char *text;
+
+ *(samba_ad[ i ].adp) = NULL;
+
+ rc = slap_str2ad( samba_ad[ i ].name, samba_ad[ i ].adp, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"%s\" attributeType: %s (%d).\n",
+ samba_ad[ i ].name, text, rc );
+ oc_sambaSamAccount = NULL;
+ return rc;
+ }
+ }
+ }
+#endif /* DO_SAMBA */
+
+#ifdef DO_SHADOW
+ if ( SMBK5PWD_DO_SHADOW( pi ) && oc_shadowAccount == NULL ) {
+ int i, rc;
+
+ oc_shadowAccount = oc_find( "shadowAccount" );
+ if ( !oc_shadowAccount ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"shadowAccount\" objectClass.\n" );
+ return -1;
+ }
+
+ for ( i = 0; shadow_ad[ i ].name != NULL; i++ ) {
+ const char *text;
+
+ *(shadow_ad[ i ].adp) = NULL;
+
+ rc = slap_str2ad( shadow_ad[ i ].name, shadow_ad[ i ].adp, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"%s\" attributeType: %s (%d).\n",
+ shadow_ad[ i ].name, text, rc );
+ oc_shadowAccount = NULL;
+ return rc;
+ }
+ }
+ }
+#endif /* DO_SHADOW */
+
+ return 0;
+}
+
+static int
+smbk5pwd_db_init(BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ smbk5pwd_t *pi;
+
+ pi = ch_calloc( 1, sizeof( smbk5pwd_t ) );
+ if ( pi == NULL ) {
+ return 1;
+ }
+ on->on_bi.bi_private = (void *)pi;
+
+ return 0;
+}
+
+static int
+smbk5pwd_db_open(BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ smbk5pwd_t *pi = (smbk5pwd_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ if ( pi->mode == 0 ) {
+ pi->mode = SMBK5PWD_F_ALL;
+ }
+
+ rc = smbk5pwd_modules_init( pi );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+smbk5pwd_db_destroy(BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ smbk5pwd_t *pi = (smbk5pwd_t *)on->on_bi.bi_private;
+
+ if ( pi ) {
+ ch_free( pi );
+ }
+
+ return 0;
+}
+
+int
+smbk5pwd_initialize(void)
+{
+ int rc;
+
+ smbk5pwd.on_bi.bi_type = "smbk5pwd";
+
+ smbk5pwd.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ smbk5pwd.on_bi.bi_db_init = smbk5pwd_db_init;
+ smbk5pwd.on_bi.bi_db_open = smbk5pwd_db_open;
+ smbk5pwd.on_bi.bi_db_destroy = smbk5pwd_db_destroy;
+
+ smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd;
+
+#ifdef DO_KRB5
+ smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind;
+
+ lutil_passwd_add( (struct berval *)&k5key_scheme, k5key_chk, k5key_hash );
+#endif
+
+ smbk5pwd.on_bi.bi_cf_ocs = smbk5pwd_cfocs;
+
+ rc = config_register_schema( smbk5pwd_cfats, smbk5pwd_cfocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &smbk5pwd );
+}
+
+#if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return smbk5pwd_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_SMBK5PWD) */
diff --git a/contrib/slapd-modules/trace/Makefile b/contrib/slapd-modules/trace/Makefile
new file mode 100644
index 0000000..1bf8a55
--- /dev/null
+++ b/contrib/slapd-modules/trace/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_TRACE=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = trace.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+trace.la: trace.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/trace/trace.c b/contrib/slapd-modules/trace/trace.c
new file mode 100644
index 0000000..1e61025
--- /dev/null
+++ b/contrib/slapd-modules/trace/trace.c
@@ -0,0 +1,256 @@
+/* trace.c - traces overlay invocation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_TRACE
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+static int
+trace_op2str( Operation *op, char **op_strp )
+{
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ *op_strp = "BIND";
+ break;
+
+ case LDAP_REQ_UNBIND:
+ *op_strp = "UNBIND";
+ break;
+
+ case LDAP_REQ_SEARCH:
+ *op_strp = "SEARCH";
+ break;
+
+ case LDAP_REQ_MODIFY:
+ *op_strp = "MODIFY";
+ break;
+
+ case LDAP_REQ_ADD:
+ *op_strp = "ADD";
+ break;
+
+ case LDAP_REQ_DELETE:
+ *op_strp = "DELETE";
+ break;
+
+ case LDAP_REQ_MODRDN:
+ *op_strp = "MODRDN";
+ break;
+
+ case LDAP_REQ_COMPARE:
+ *op_strp = "COMPARE";
+ break;
+
+ case LDAP_REQ_ABANDON:
+ *op_strp = "ABANDON";
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ *op_strp = "EXTENDED";
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+static int
+trace_op_func( Operation *op, SlapReply *rs )
+{
+ char *op_str = NULL;
+
+ (void)trace_op2str( op, &op_str );
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_EXTENDED:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=EXTENDED dn=\"%s\" reqoid=%s\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val,
+ BER_BVISNULL( &op->ore_reqoid ) ? "" : op->ore_reqoid.bv_val );
+ break;
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=%s dn=\"%s\"\n",
+ op->o_log_prefix, op_str,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val );
+ break;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+trace_response( Operation *op, SlapReply *rs )
+{
+ char *op_str = NULL;
+
+ (void)trace_op2str( op, &op_str );
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_EXTENDED:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=EXTENDED RESPONSE dn=\"%s\" reqoid=%s rspoid=%s err=%d\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val,
+ BER_BVISNULL( &op->ore_reqoid ) ? "" : op->ore_reqoid.bv_val,
+ rs->sr_rspoid == NULL ? "" : rs->sr_rspoid,
+ rs->sr_err );
+ break;
+
+ case LDAP_REQ_SEARCH:
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=SEARCH ENTRY dn=\"%s\"\n",
+ op->o_log_prefix,
+ rs->sr_entry->e_name.bv_val );
+ goto done;
+
+ case REP_SEARCHREF:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=SEARCH REFERENCE ref=\"%s\"\n",
+ op->o_log_prefix,
+ rs->sr_ref[ 0 ].bv_val );
+ goto done;
+
+ case REP_RESULT:
+ break;
+
+ default:
+ assert( 0 );
+ }
+ /* fallthru */
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=%s RESPONSE dn=\"%s\" err=%d\n",
+ op->o_log_prefix,
+ op_str,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val,
+ rs->sr_err );
+ break;
+ }
+
+done:;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+trace_db_init( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_INIT\n" );
+
+ return 0;
+}
+
+static int
+trace_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_CONFIG argc=%d argv[0]=\"%s\"\n",
+ argc, argv[ 0 ] );
+
+ return 0;
+}
+
+static int
+trace_db_open( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_OPEN\n" );
+
+ return 0;
+}
+
+static int
+trace_db_close( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_CLOSE\n" );
+
+ return 0;
+}
+
+static int
+trace_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_DESTROY\n" );
+
+ return 0;
+}
+
+static slap_overinst trace;
+
+int
+trace_initialize()
+{
+ trace.on_bi.bi_type = "trace";
+
+ trace.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ trace.on_bi.bi_db_init = trace_db_init;
+ trace.on_bi.bi_db_open = trace_db_open;
+ trace.on_bi.bi_db_config = trace_db_config;
+ trace.on_bi.bi_db_close = trace_db_close;
+ trace.on_bi.bi_db_destroy = trace_db_destroy;
+
+ trace.on_bi.bi_op_add = trace_op_func;
+ trace.on_bi.bi_op_bind = trace_op_func;
+ trace.on_bi.bi_op_unbind = trace_op_func;
+ trace.on_bi.bi_op_compare = trace_op_func;
+ trace.on_bi.bi_op_delete = trace_op_func;
+ trace.on_bi.bi_op_modify = trace_op_func;
+ trace.on_bi.bi_op_modrdn = trace_op_func;
+ trace.on_bi.bi_op_search = trace_op_func;
+ trace.on_bi.bi_op_abandon = trace_op_func;
+ trace.on_bi.bi_extended = trace_op_func;
+
+ trace.on_response = trace_response;
+
+ return overlay_register( &trace );
+}
+
+#if SLAPD_OVER_TRACE == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return trace_initialize();
+}
+#endif /* SLAPD_OVER_TRACE == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_TRACE) */
diff --git a/contrib/slapd-modules/usn/Makefile b/contrib/slapd-modules/usn/Makefile
new file mode 100644
index 0000000..d7af04b
--- /dev/null
+++ b/contrib/slapd-modules/usn/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_USN=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = usn.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+usn.la: usn.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/usn/README b/contrib/slapd-modules/usn/README
new file mode 100644
index 0000000..3bfb096
--- /dev/null
+++ b/contrib/slapd-modules/usn/README
@@ -0,0 +1,44 @@
+This directory contains a slapd overlay, usn, that extends slapd
+to maintain the usnCreated and usnChanged operational attributes
+normally used by Microsoft ActiveDirectory.
+
+To use the overlay, add:
+
+ moduleload <path to>usn.so
+ ...
+
+ database mdb
+ ...
+ overlay usn
+
+to your slapd configuration file. The schema definitions for the
+two USN attributes are hardcoded in this overlay.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+ gcc -c -I ../../include/ -I ../../servers/slapd -DSLAPD_OVER_USN=SLAPD_MOD_DYNAMIC usn.c
+ gcc -shared -o usn.so usn.o
+
+This overlay is only set up to be built as a dynamically loaded module.
+On most platforms, in order for the module to be usable, all of the
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlay statically, you will have to move it into the
+slapd/overlays directory and edit the Makefile and overlays.c to reference
+it. You will also have to define SLAPD_OVER_USN to SLAPD_MOD_STATIC,
+and add the relevant libraries to the main slapd link command.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2007-2022 The OpenLDAP Foundation.
+Portions Copyright 2007 Howard Chu, Symas Corp. 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>.
+
diff --git a/contrib/slapd-modules/usn/usn.c b/contrib/slapd-modules/usn/usn.c
new file mode 100644
index 0000000..abd6d13
--- /dev/null
+++ b/contrib/slapd-modules/usn/usn.c
@@ -0,0 +1,330 @@
+/* usn.c - Maintain Microsoft-style Update Sequence Numbers */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_USN
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+/* This overlay intercepts write operations and adds a Microsoft-style
+ * USN to the target entry.
+ */
+
+typedef struct usn_info {
+ int ui_current;
+ ldap_pvt_thread_mutex_t ui_mutex;
+} usn_info_t;
+
+static AttributeDescription *ad_usnCreated, *ad_usnChanged;
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.2.840.113556.1.2.19 "
+ "NAME 'uSNCreated' "
+ "SYNTAX '1.2.840.113556.1.4.906' "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION )",
+ &ad_usnCreated },
+ { "( 1.2.840.113556.1.2.120 "
+ "NAME 'uSNChanged' "
+ "SYNTAX '1.2.840.113556.1.4.906' "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION )",
+ &ad_usnChanged },
+ { NULL }
+};
+
+static int
+usn_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ usn_info_t *ui = on->on_bi.bi_private;
+ int my_usn;
+ char intbuf[64];
+ struct berval bv[2];
+
+ ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
+ ui->ui_current++;
+ my_usn = ui->ui_current;
+ ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
+
+ BER_BVZERO(&bv[1]);
+ bv[0].bv_val = intbuf;
+ bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
+ switch(op->o_tag) {
+ case LDAP_REQ_ADD:
+ attr_merge( op->ora_e, ad_usnCreated, bv, NULL );
+ attr_merge( op->ora_e, ad_usnChanged, bv, NULL );
+ break;
+ case LDAP_REQ_DELETE:
+ /* Probably need to update root usnLastObjRem */
+ break;
+ default: {
+ /* Modify, ModDN */
+ Modifications *ml, *mod = ch_calloc( sizeof( Modifications ), 1 );
+ for ( ml = op->orm_modlist; ml && ml->sml_next; ml = ml->sml_next );
+ ml->sml_next = mod;
+ mod->sml_desc = ad_usnChanged;
+ mod->sml_numvals = 1;
+ value_add_one( &mod->sml_values, &bv[0] );
+ mod->sml_nvalues = NULL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+ mod->sml_next = NULL;
+ break;
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+usn_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private;
+
+ if ( rs->sr_entry &&
+ dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) {
+
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( ad_usnChanged, rs->sr_attrs )) {
+ Attribute *a, **ap = NULL;
+ char intbuf[64];
+ struct berval bv;
+ int my_usn;
+
+ for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
+ if ( a->a_desc == ad_usnChanged )
+ break;
+ }
+
+ if ( !a ) {
+ for ( ap = &rs->sr_operational_attrs; *ap;
+ ap=&(*ap)->a_next );
+
+ a = attr_alloc( ad_usnChanged );
+ *ap = a;
+ }
+
+ if ( !ap ) {
+ if ( rs_entry2modifiable( op,rs, on )) {
+ a = attr_find( rs->sr_entry->e_attrs,
+ ad_usnChanged );
+ }
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_numvals = 0;
+ }
+ ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
+ my_usn = ui->ui_current;
+ ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
+ bv.bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
+ bv.bv_val = intbuf;
+ attr_valadd( a, &bv, NULL, 1 );
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/* Read the old USN from the underlying DB. This code is
+ * stolen from the syncprov overlay.
+ */
+static int
+usn_db_open(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+ void *thrctx = NULL;
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+
+ rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL,
+ slap_schema.si_ad_contextCSN, 0, &e, on );
+
+ if ( e ) {
+ a = attr_find( e->e_attrs, ad_usnChanged );
+ if ( a ) {
+ ui->ui_current = atoi( a->a_vals[0].bv_val );
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+ return 0;
+}
+
+static int
+usn_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ usn_info_t *ui;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "usn must be instantiated within a database.\n" );
+ return 1;
+ }
+
+ ui = ch_calloc(1, sizeof(usn_info_t));
+ ldap_pvt_thread_mutex_init( &ui->ui_mutex );
+ on->on_bi.bi_private = ui;
+ return 0;
+}
+
+static int
+usn_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ usn_info_t *ui = on->on_bi.bi_private;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ SlapReply rs = {REP_RESULT};
+ void *thrctx;
+
+ Modifications mod;
+ slap_callback cb = {0};
+ char intbuf[64];
+ struct berval bv[2];
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ BER_BVZERO( &bv[1] );
+ bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", ui->ui_current );
+ bv[0].bv_val = intbuf;
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ mod.sml_nvalues = NULL;
+ mod.sml_desc = ad_usnChanged;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = 0;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+ op->o_req_dn = op->o_bd->be_suffix[0];
+ op->o_req_ndn = op->o_bd->be_nsuffix[0];
+ op->o_bd->bd_info = on->on_info->oi_orig;
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ op->o_no_schema_check = 1;
+ op->o_bd->be_modify( op, &rs );
+ if ( mod.sml_next != NULL ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+ return 0;
+}
+
+static int
+usn_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ usn_info_t *ui = on->on_bi.bi_private;
+
+ ldap_pvt_thread_mutex_destroy( &ui->ui_mutex );
+ ch_free( ui );
+ on->on_bi.bi_private = NULL;
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst usn;
+
+int
+usn_init( void )
+{
+ int i, code;
+
+ memset( &usn, 0, sizeof( slap_overinst ) );
+ usn.on_bi.bi_type = "usn";
+ usn.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ usn.on_bi.bi_db_init = usn_db_init;
+ usn.on_bi.bi_db_destroy = usn_db_destroy;
+ usn.on_bi.bi_db_open = usn_db_open;
+ usn.on_bi.bi_db_close = usn_db_close;
+
+ usn.on_bi.bi_op_modify = usn_func;
+ usn.on_bi.bi_op_modrdn = usn_func;
+ usn.on_bi.bi_op_add = usn_func;
+ usn.on_bi.bi_op_delete = usn_func;
+ usn.on_bi.bi_operational = usn_operational;
+
+ for ( i = 0; as[i].desc; i++ ) {
+ code = register_at( as[i].desc, as[i].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "usn_init: register_at #%d failed\n", i );
+ return code;
+ }
+ }
+ return overlay_register( &usn );
+}
+
+#if SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return usn_init();
+}
+#endif /* SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_USN) */
diff --git a/contrib/slapd-modules/variant/Makefile b/contrib/slapd-modules/variant/Makefile
new file mode 100644
index 0000000..07effed
--- /dev/null
+++ b/contrib/slapd-modules/variant/Makefile
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2017 Ondřej Kuzník, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_VARIANT=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = variant.la
+MANPAGES = slapo-variant.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+variant.la: variant.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/variant/slapo-variant.5 b/contrib/slapd-modules/variant/slapo-variant.5
new file mode 100644
index 0000000..a480744
--- /dev/null
+++ b/contrib/slapd-modules/variant/slapo-variant.5
@@ -0,0 +1,472 @@
+.TH SLAPO-VARIANT 5 "RELEASEDATE" "OpenLDAP"
+.\" Copyright 2016-2017 Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See LICENSE.
+.SH NAME
+slapo\-variant \- share values between entries
+.SH SYNOPSIS
+olcOverlay=variant
+.SH DESCRIPTION
+The
+.B variant
+overlay to
+.BR slapd (8)
+allows attributes/values to be shared between several entries. In some ways
+this is similar to
+.BR slapo-collect (5)
+with the exception that the source and target attributes can be different.
+.LP
+The overlay operates on configured
+.B variant
+entries which can have several
+.B attributes
+each configured to borrow values from an attribute in the
+.B alternate
+entry.
+.LP
+Two types of
+.B variant
+entries can be configured,
+.B regular
+and
+.BR regex ,
+where the latter are configured with a regular expression and patterns to
+locate each alternate entry, with access to the variant DN and first nine
+submatches captured by the regular expression.
+.LP
+For most purposes (see
+.BR LIMITATIONS ,
+especially for
+.B regex
+variants), the resulting entry is completely transparent to the operations
+performed on it, e.g. a modify operation on the
+.B variant
+attribute gets transformed
+into an operation on the
+.B alternate
+entry+attribute. As such, the usual ACL rules apply, appropriate
+access to both the
+.B variant
+and
+.B alternate
+entry is checked.
+.LP
+As a special case,
+.B Add
+and
+.B Delete
+operations will not affect the
+.B alternate
+entries. Should an attempt be made to add a configured
+.B variant
+entry with the
+.B variant
+attributes already populated, the operation will be rejected with a
+.B Constraint
+.BR Violation .
+
+.SH CONFIGURATION LAYOUT
+
+The overlay has to be instantiated under a database adding an entry of
+.B olcOverlay=variant
+with objectClass of
+.BR olcVariantConfig .
+
+The overlay configuration subtree consists of the following levels:
+.RS
+.TP
+.B objectClass=olcVariantConfig
+Main overlay configuration. Created directly under the database
+configuration entry.
+.TP
+.B objectClass=olcVariantVariant
+Specifies a
+.B regular variant
+entry and must be a child of an entry with
+.BR objectClass=olcVariantConfig .
+There may be as many such entries as necessary provided they all specify a
+different DN in the
+.BR olcVariantEntry
+attribute.
+.TP
+.B objectClass=olcVariantAttribute
+Specifies a
+.B regular variant
+attribute together with information where the
+.B alternate
+attribute is stored. Must be a child of an entry with
+.BR objectClass=olcVariantVariant .
+There may be as many such entries as necessary provided they all specify a
+different attribute in
+.BR olcVariantVariantAttribute .
+.TP
+.B objectClass=olcVariantRegex
+Specifies a
+.B regex variant
+entry and must be a child of an entry with
+.BR objectClass=olcVariantConfig .
+There may be as many such entries as necessary provided they all specify a
+different DN in the
+.BR olcVariantEntryRegex
+attribute.
+.TP
+.B objectClass=olcVariantAttributePattern
+Specifies a
+.B regex variant
+attribute together with information where the
+.B alternate
+attribute is stored. Must be a child of an entry with
+.BR objectClass=olcVariantRegex .
+There may be as many such entries as necessary provided they all specify a
+different attribute in
+.BR olcVariantVariantAttribute .
+.RE
+
+In the case of
+.BR slapd.conf (5),
+the variant definition is delimited by the keyword
+.B variantDN
+followed by an arbitrary number of
+.B variantSpec
+providing the attribute definitions following it. Each new
+.B variantDN
+line starts configuring a new variant.
+
+.SH OVERLAY CONFIGURATION ENTRY
+
+The top entry
+.RB ( olcVariantConfig )
+has the following options available:
+
+.RS
+.TP
+.B olcVariantPassReplication: TRUE | FALSE
+If set to
+.BR TRUE ,
+.B search
+operations with the
+.B SyncReplication
+control will be passed unchanged so that replication can be unaffected.
+Defaults to
+.B FALSE
+while unset. The
+.BR slapd.conf (5)
+equivalent is
+.BR passReplication .
+.RE
+
+.SH VARIANT CONFIGURATION ENTRY
+
+The
+.B regular variant entry
+configuration
+.RB ( olcVariantVariant )
+has the following options available:
+
+.RS
+.TP
+.B olcVariantEntry: <dn>
+Mandatory attribute, indicates that the named entry is to be treated as a
+.B variant
+entry. The
+.BR slapd.conf (5)
+equivalent is
+.BR variantDN .
+.TP
+.B name: <reference>
+Name of the entry for reference, usually the attribute present in the
+configuration entry's RDN. There is no
+.BR slapd.conf (5)
+equivalent as this has no effect on the overlay operation.
+.RE
+
+Similarly, the
+.B regex variant entry
+configuration
+.RB ( olcVariantRegex )
+has these options available:
+
+.RS
+.TP
+.B olcVariantRegex: <regex>
+Mandatory attribute, indicates that the entries whose normalised DN matches is
+to be treated as a
+.B regex variant
+entry. The (POSIX.2) regex can use submatches to capture parts of the DN for
+later use in locating the
+.B alternative
+.BR entry .
+The
+.BR slapd.conf (5)
+equivalent is
+.BR variantRegex .
+.TP
+.B name: <reference>
+Name of the entry for reference, usually the attribute present in the
+configuration entry's RDN. There is no
+.BR slapd.conf (5)
+equivalent as this has no effect on the overlay operation.
+.RE
+
+.SH CONFIGURATION PRECEDENCE
+
+While several
+.B regex variants
+can match the same entry, only one can apply at a time. The list of the
+.B regular variants
+is checked first. Should none match, the list of
+.B regex variants
+is checked in the order they have been configured using only the first one that
+matches.
+
+.SH VARIANT ATTRIBUTE CONFIGURATION ENTRY
+
+The
+.B regular variant
+attribute configuration
+.RB ( olcVariantAttribute )
+and
+.B regex variant
+attribute configuration
+.RB ( olcVariantAttributePattern )
+have the following options available:
+
+.RS
+.TP
+.B name: <reference>
+Name of the attribute configuration for reference and/or documentation, if
+present, usually found in the configuration entry's RDN. There is no
+.BR slapd.conf (5)
+equivalent as this has no effect on the overlay operation.
+.TP
+.B olcVariantVariantAttribute: <attr>
+Mandatory attribute, indicates that the named attribute is not present in
+the
+.B variant
+entry but is to be retrieved from the
+.B alternate
+entry.
+.TP
+.B olcVariantAlternativeAttribute: <attr>
+Mandatory attribute, indicates that the values of the named attribute is to
+be retrieved from the
+.B alternate
+entry for use as the values of the
+.B variant
+attribute. The syntaxes of the corresponding
+.B variant
+and
+.B alternate
+attributes have to match or the configuration will be rejected.
+.TP
+.B olcVariantAlternativeEntry: <dn>
+Attribute mandatory for
+.B regular
+.BR variants ,
+indicates the
+.B alternate
+entry to use when retrieving the attribute from.
+.TP
+.B olcVariantAlternativeEntryPattern: <pattern>
+Attribute mandatory for
+.B regex
+.BR variants ,
+indicates the
+.B alternate
+entry to use when retrieving the attribute from. Substitution patterns
+.RB ( $n )
+can be used to insert parts of the variant entry's DN.
+.B $0
+will place the entire variant DN,
+.B $1
+to
+.B $9
+can be used to place respective capture patterns from the
+.B variant
+entry.
+.TP
+.B variantSpec <attr> <attr2> <dn>
+.BR slapd.conf (5)
+only. The equivalent to options above, where
+.B <attr>
+represents the
+.BR olcVariantVariantAttribute ,
+.B <attr2>
+represents the
+.B olcVariantAlternativeAttribute
+and
+.B <dn>
+has the same meaning as the content of
+.BR olcVariantAlternativeEntry .
+Has to follow a
+.B variantDN
+line in the overlay's configuration.
+.TP
+.B variantRegexSpec <attr> <attr2> <pattern>
+.BR slapd.conf (5)
+only. The equivalent to options above, where
+.B <attr>
+represents the
+.BR olcVariantVariantAttribute ,
+.B <attr2>
+represents the
+.B olcVariantAlternativeAttribute
+and
+.B <pattern>
+has the same meaning as the content of
+.BR olcVariantAlternativeEntryPattern .
+Has to follow a
+.B variantRegex
+line in the overlay's configuration.
+.RE
+
+.SH EXAMPLE
+
+The following is an example of a configured overlay, substitute
+.B $DATABASE
+for the DN of the database it is attached to and
+.B {x}
+with the desired position of the overlay in the overlay stack.
+
+.nf
+dn: olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantConfig
+olcOverlay: variant
+# Let replication requests pass through unmodified
+olcVariantPassReplication: TRUE
+
+# when an operation considers dc=example,dc=com
+dn: name=example,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantVariant
+olcVariantEntry: dc=example,dc=com
+
+# share the Headquarters' address as the company address
+dn: olcVariantVariantAttribute=postaladdress,name={0}example,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantVariantAttribute
+olcVariantVariantAttribute: postaladdress
+olcVariantAlternativeAttribute: postaladdress
+olcVariantAlternativeEntry: ou=Headquarters,dc=example,dc=com
+
+# populate telephonenumber from CEO's home phone
+dn: name=Take phone from CEO entry,name={0}example,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantVariantAttribute
+olcVariantVariantAttribute: telephonenumber
+olcVariantAlternativeAttribute: homephone
+olcVariantAlternativeEntry: cn=John Doe,ou=People,dc=example,dc=com
+
+# Match all entries with example in the DN
+#
+# It will not match dc=example,dc=com as that's already configured as a regular
+# variant
+dn: name=example 2,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantRegex
+olcVariantEntryRegex: .*example[^,]*,(.*)
+
+dn: olcVariantVariantAttribute=location,name={1}example 2,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantAttributePattern
+olcVariantVariantAttribute: location
+olcVariantAlternativeAttribute: location
+olcVariantAlternativeEntryPattern: ou=object with location,$1
+.fi
+
+The
+.BR slapd.conf (5)
+equivalent of the above follows (note that the converted
+.B cn=config
+will differ in the first variant attribute configuration entry):
+
+.nf
+overlay variant
+passReplication TRUE
+
+variantDN dc=example,dc=com
+variantSpec telephonenumber homephone "cn=John Doe,ou=People,dc=example,dc=com"
+variantSpec postaladdress postaladdress ou=Headquarters,dc=example,dc=com
+
+variantRegex .*example[^,]*,(.*)
+variantRegexSpec location location "ou=object with location,$1"
+.fi
+
+.SH REPLICATION
+
+There are two ways that a database with
+.BR slapo-variant (5)
+might be replicated, either replicating the data as stored in the database,
+or as seen by the clients interacting with the server.
+
+The former can be achieved by setting the overlay option
+.B olcVariantPassReplication
+on the provider and configuring
+.BR slapo-syncprov (5)
+to appear before (with a lower index than)
+.BR slapo-variant (5).
+This is the preferred way and the only to work with
+.B regex variants
+or support multi-provider replication,
+but care must be taken to configure
+.BR slapo-variant (5)
+correctly on each replica.
+
+The latter is mostly possible by keeping the option
+.B olcVariantPassReplication
+set to
+.B FALSE
+on the provider and configuring
+.BR slapo-syncprov (5)
+to appear after (with a higher index than)
+.BR slapo-variant (5).
+However, it will only really work for replication set-ups that do not
+utilise
+.B regex
+.BR variants ,
+delta-replication nor the refresh and persist mode and is therefore
+discouraged.
+
+.SH LIMITATIONS
+For
+.B regex
+.BR variants ,
+the
+.B Search
+operation will only apply if the search scope is set to
+.BR base .
+
+The
+.B ModRDN
+operation is not currently handled and will always modify only the entry in
+question, not the configured
+.B alternate
+entry.
+
+The
+.B Modify
+operation is not atomic with respect to the alternate entries. Currently,
+the overlay processes the operations on the entry, sends the result message
+and, if successful, starts modifying the
+.B alternate
+entries accordingly.
+There is currently no support to indicate whether modifications to the
+.B alternate
+entries have been successful or whether they have finished.
+
+The only control explicitly handled is the
+.B SyncReplication
+control if enabled through the
+.B olcVariantPassReplication
+setting, adding any controls to an operation that is handled by the overlay
+might lead to unexpected behaviour and is therefore discouraged.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd-config (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR regex (7),
+.BR slapd (8)
+.SH ACKNOWLEDGEMENTS
+This module was developed in 2016-2017 by Ondřej Kuzník for Symas Corp.
diff --git a/contrib/slapd-modules/variant/tests/Rules.mk b/contrib/slapd-modules/variant/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/variant/tests/data/additional-config.ldif b/contrib/slapd-modules/variant/tests/data/additional-config.ldif
new file mode 100644
index 0000000..6a286fe
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/additional-config.ldif
@@ -0,0 +1,23 @@
+dn: name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: name=attribute 1,name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantVariantAttribute: cn
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: dc=example,dc=com
+
+dn: name=attribute 2,name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantVariantAttribute: pager
+olcVariantAlternativeAttribute: telephonenumber
+olcVariantAlternativeEntry: dc=example,dc=com
+
+dn: name={0}attribute 1,name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantVariantAttribute
+olcVariantVariantAttribute: description
diff --git a/contrib/slapd-modules/variant/tests/data/config.ldif b/contrib/slapd-modules/variant/tests/data/config.ldif
new file mode 100644
index 0000000..6e323b9
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/config.ldif
@@ -0,0 +1,89 @@
+dn: olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectclass: olcVariantConfig
+
+dn: olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantPassReplication
+olcVariantPassReplication: TRUE
+
+dn: name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: ou=People,dc=example,dc=com
+
+# a basic variant
+dn: olcVariantVariantAttribute=description,name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: dc=example,dc=com
+
+# a nonexistent alternate
+dn: olcVariantVariantAttribute=seealso,name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: seealso
+olcVariantAlternativeEntry: ou=Societies,dc=example,dc=com
+
+dn: name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: ou=Groups,dc=example,dc=com
+
+# recursive retrieval is not done
+dn: olcVariantVariantAttribute=description,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: ou=People,dc=example,dc=com
+
+# a variant taking data from a different attribute (after the changes below)
+dn: olcVariantVariantAttribute=st,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: st
+olcVariantAlternativeEntry: cn=Manager,dc=example,dc=com
+
+# configuration changes
+dn: olcVariantVariantAttribute={1}st,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantAlternativeAttribute
+olcVariantAlternativeAttribute: ou
+-
+replace: olcVariantAlternativeEntry
+olcVariantAlternativeEntry: ou=Alumni Association,ou=People,dc=example,dc=com
+-
+
+# a regex variant
+dn: name={2}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantRegex
+olcVariantEntryRegex: (.*),(ou=.*technology.*)(,)dc=example,dc=com
+
+dn: olcVariantVariantAttribute=ou,name={2}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttributePattern
+olcVariantAlternativeAttribute: ou
+olcVariantAlternativeEntryPattern: $2$3dc=example$3dc=com
+
+# Duplicate description into title
+dn: olcVariantVariantAttribute=title,name={2}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttributePattern
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntryPattern: $0
+
+# everything
+dn: name={3}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantRegex
+olcVariantEntryRegex: .*
+
+dn: olcVariantVariantAttribute=l,name={3}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttributePattern
+olcVariantAlternativeAttribute: l
+olcVariantAlternativeEntryPattern: dc=example,dc=com
+
diff --git a/contrib/slapd-modules/variant/tests/data/hidden.ldif b/contrib/slapd-modules/variant/tests/data/hidden.ldif
new file mode 100644
index 0000000..d219746
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/hidden.ldif
@@ -0,0 +1,4 @@
+dn: ou=Groups,dc=example,dc=com
+changetype: modify
+add: description
+description: This is hidden by the overlay config
diff --git a/contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif b/contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif
new file mode 100644
index 0000000..880e035
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif
@@ -0,0 +1,4 @@
+dn: name=variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: ou=Groups,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif b/contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif
new file mode 100644
index 0000000..0fb8b2b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif
@@ -0,0 +1,4 @@
+dn: name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantEntry
+olcVariantEntry: ou=Groups,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif b/contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif
new file mode 100644
index 0000000..8447018
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif
@@ -0,0 +1,6 @@
+dn: olcVariantAlternativeAttribute=description,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantVariantAttribute: description
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: ou=People,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif b/contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif
new file mode 100644
index 0000000..dfbde5b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif
@@ -0,0 +1,4 @@
+dn: olcVariantVariantAttribute={1}st,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantAlternativeAttribute
+olcVariantAlternativeAttribute: userPassword
diff --git a/contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif b/contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif
new file mode 100644
index 0000000..21b5b14
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif
@@ -0,0 +1,16 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+postaladdress: ITD $ 535 W. William St $ Anytown, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+testTime: 20050304001801.234Z
diff --git a/contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif b/contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif
new file mode 100644
index 0000000..8f0f439
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif
@@ -0,0 +1,7 @@
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+title: Chief Investigator, ITD
diff --git a/contrib/slapd-modules/variant/tests/data/test003-out.ldif b/contrib/slapd-modules/variant/tests/data/test003-out.ldif
new file mode 100644
index 0000000..1c3ca5d
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test003-out.ldif
@@ -0,0 +1,124 @@
+# Test 1, list two unrelated entries
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+
+# Test 2, list some of the variant entries, checking that attributes have been populated
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+description: The Example, Inc. at Anytown
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+
+# Return $BASEDN, location is rewritten to end
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+l: Anytown, Michigan
+
+
+# Make sure only the first regex applies
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+title: Hiker, biker
+ou: Information Technology Division
+
+
+# Exercise the last regex
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+l: Anytown, Michigan
+
+
+# Test 3, check filters pick up the new data
+dn: ou=Groups,dc=example,dc=com
+st: Alumni Association
+
diff --git a/contrib/slapd-modules/variant/tests/data/test005-changes.ldif b/contrib/slapd-modules/variant/tests/data/test005-changes.ldif
new file mode 100644
index 0000000..767f48a
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-changes.ldif
@@ -0,0 +1,35 @@
+dn: ou=People,dc=example,dc=com
+changetype: modify
+add: description
+description: Everyone's heard of them
+-
+increment: uidNumber
+uidNumber: 1
+-
+
+dn: ou=Groups,dc=example,dc=com
+changetype: modify
+add: st
+st: Alabama
+-
+
+# check regex
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+changetype: modify
+replace: description
+description: A mouthful
+-
+add: ou
+ou: The IT Crowd
+-
+
+# have the two mods merge
+dn: dc=example,dc=com
+changetype: modify
+add: l
+l: Locally
+-
+replace: st
+st: Antarctica
+-
diff --git a/contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif b/contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif
new file mode 100644
index 0000000..ce9c007
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif
@@ -0,0 +1,4 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+replace: description
+description: Ghost
diff --git a/contrib/slapd-modules/variant/tests/data/test005-out.ldif b/contrib/slapd-modules/variant/tests/data/test005-out.ldif
new file mode 100644
index 0000000..67e441b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-out.ldif
@@ -0,0 +1,206 @@
+# Test1: list entries that should have been changed by ldapmodify
+dn: dc=example,dc=com
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+l: Locally
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+description: Everyone's heard of them
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+st: Antarctica
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 1
+gidNumber: 0
+description: The Example, Inc. at Anytown
+description: Everyone's heard of them
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+ou: Alabama
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: alumni association
+st: alabama
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+ou: The IT Crowd
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+title: Hiker, biker
+ou: Information Technology Division
+ou: The IT Crowd
+
diff --git a/contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif b/contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif
new file mode 100644
index 0000000..54fd3a5
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif
@@ -0,0 +1,4 @@
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: seealso
+seealso: dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test006-config.ldif b/contrib/slapd-modules/variant/tests/data/test006-config.ldif
new file mode 100644
index 0000000..c668134
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test006-config.ldif
@@ -0,0 +1,61 @@
+dn: name={4}Mark,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={4}Mark,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: cn
+olcVariantAlternativeEntry: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: name={5}Elliot,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=title,name={5}Elliot,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: cn
+olcVariantAlternativeEntry: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={5}Elliot,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+
+dn: name={6}Doe,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: sn=Doe,ou=Add & Delete,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=title,name={6}Doe,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: cn
+olcVariantAlternativeEntry: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={6}Doe,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+
+dn: name={7}Group,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: cn=Group,ou=Add & Delete,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=seeAlso,name={7}Group,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: member
+olcVariantAlternativeEntry: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={7}Group,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test006-out.ldif b/contrib/slapd-modules/variant/tests/data/test006-out.ldif
new file mode 100644
index 0000000..03910c0
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test006-out.ldif
@@ -0,0 +1,151 @@
+# reading Mark Elliot as anonymous
+
+# reading the same as various users
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+description: Mark A Elliot
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+description: Mark Elliot
+
+
+# Add & Delete subtree contents as seen by Babs
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John
+uid: jd
+sn: Doe
+title: John Doe
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark
+uid: me
+sn: Elliot
+title: Mark A Elliot
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+objectClass: groupOfNames
+member: dc=example,dc=com
+cn: group
+description: All Alumni Assoc Staff
+seeAlso: cn=Manager,dc=example,dc=com
+seeAlso: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+
+# Add & Delete subtree contents as seen by Bjorn
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John
+uid: jd
+sn: Doe
+title: Jonathon Doe
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark
+uid: me
+sn: Elliot
+title: Mark Elliot
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+objectClass: groupOfNames
+member: dc=example,dc=com
+cn: group
+description: All Alumni Assoc Staff
+seeAlso: cn=Manager,dc=example,dc=com
+seeAlso: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+
+# Final state of ou=Add & Delete,dc=example,dc=com as seen by the Manager
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+objectClass: inetOrgPerson
+sn: Jensen
+cn: Added by Bjorn
+description: added by jaj (should succeed)
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John
+uid: jd
+sn: Doe
+description: added by jaj (should succeed)
+title: John Doe
+title: Jonathon Doe
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark
+uid: me
+sn: Elliot
+description: added by jaj (should succeed)
+title: Mark Elliot
+title: Mark A Elliot
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+objectClass: groupOfNames
+member: dc=example,dc=com
+cn: group
+description: All Alumni Assoc Staff
+description: another one added by bjorn (should succeed)
+seeAlso: cn=Manager,dc=example,dc=com
+seeAlso: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=examp
+ le,dc=com
+seeAlso: cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,dc=exa
+ mple,dc=com
+
diff --git a/contrib/slapd-modules/variant/tests/data/test007-out.ldif b/contrib/slapd-modules/variant/tests/data/test007-out.ldif
new file mode 100644
index 0000000..cf1aac8
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test007-out.ldif
@@ -0,0 +1,6 @@
+# Testing searches against attribute supertypes...
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+
diff --git a/contrib/slapd-modules/variant/tests/data/test010-out.ldif b/contrib/slapd-modules/variant/tests/data/test010-out.ldif
new file mode 100644
index 0000000..28603e1
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test010-out.ldif
@@ -0,0 +1,52 @@
+# Test 1, trigger sizelimit without overlay interference
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+Size limit exceeded (4)
+
+# Test 2, check sizelimit is not triggered when it matches the number of entries returned
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+description: The Example, Inc. at Anytown
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+# Test 3, check sizelimit will stop at the right time
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+Size limit exceeded (4)
+
diff --git a/contrib/slapd-modules/variant/tests/data/test011-out.ldif b/contrib/slapd-modules/variant/tests/data/test011-out.ldif
new file mode 100644
index 0000000..07604f8
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test011-out.ldif
@@ -0,0 +1,10 @@
+# ldapsearch does not return anything tangible in the output if it enounters a referral
+
+# Asking for the referral will return LDAP_REFERRAL
+Referral (10)
+Matched DN: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+Referral: ldap://hostB/cn=Gern%20Jensen,ou=Information%20Technology%20Division,ou=People,dc=example,dc=com??sub
+# Asking for anything under a referral will do the same
+Referral (10)
+Matched DN: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+Referral: ldap://hostB/cn=child,cn=Gern%20Jensen,ou=Information%20Technology%20Division,ou=People,dc=example,dc=com??sub
diff --git a/contrib/slapd-modules/variant/tests/data/test012-data.ldif b/contrib/slapd-modules/variant/tests/data/test012-data.ldif
new file mode 100644
index 0000000..8b8d8b3
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test012-data.ldif
@@ -0,0 +1,13 @@
+dn: dc=demonstration,dc=com
+changetype: add
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+o: demo
+associateddomain: demonstration.com
+
+dn: ou=Societies,dc=demonstration,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Societies
+seealso: dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test012-out.ldif b/contrib/slapd-modules/variant/tests/data/test012-out.ldif
new file mode 100644
index 0000000..bd31fa0
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test012-out.ldif
@@ -0,0 +1,9 @@
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+seealso: dc=example,dc=com
+description: The Example, Inc. at Anytown
+
diff --git a/contrib/slapd-modules/variant/tests/data/variant.conf b/contrib/slapd-modules/variant/tests/data/variant.conf
new file mode 100644
index 0000000..dba6c46
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/variant.conf
@@ -0,0 +1,17 @@
+overlay variant
+passReplication TRUE
+
+variantDN ou=People,dc=example,dc=com
+variantSpec seealso seealso ou=Societies,dc=example,dc=com
+variantSpec description description dc=example,dc=com
+
+variantRegex "(.*),(ou=.*technology.*)(,)dc=example,dc=com"
+variantRegexSpec title description $0
+variantRegexSpec ou ou "$2$3dc=example$3dc=com"
+
+variantDN ou=Groups,dc=example,dc=com
+variantSpec st ou "ou=Alumni Association,ou=People,dc=example,dc=com"
+variantSpec description description ou=People,dc=example,dc=com
+
+variantRegex .*
+variantRegexSpec l l dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/run b/contrib/slapd-modules/variant/tests/run
new file mode 100755
index 0000000..6a38431
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/run
@@ -0,0 +1,229 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+TOPSRCDIR="${SRCDIR-$LDAP_SRC}"
+SRCDIR="${TOPSRCDIR}/tests"
+eval `grep EGREP_CMD= ${LDAP_BUILD}/tests/run`
+eval `$EGREP_CMD -e '^LN_S=' ${LDAP_BUILD}/tests/run`
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+. "${SRCDIR}/scripts/defines.sh"
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+eval `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run`
+export `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run | sed 's/=.*//'`
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+ ndb) INDEXDB=indexdb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA \
+ SRCDIR
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${TOPDIR}/tests/scripts"
+
+export SCRIPTDIR
+
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+if test ! -r ./data; then
+ ${LN_S} ${TOPDIR}/tests/data ./
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ $SCRIPT $*
+ RC=$?
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/contrib/slapd-modules/variant/tests/scripts/all b/contrib/slapd-modules/variant/tests/scripts/all
new file mode 100755
index 0000000..d6d6dc7
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/all
@@ -0,0 +1,102 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 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>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+ if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/variant/tests/scripts/common.sh b/contrib/slapd-modules/variant/tests/scripts/common.sh
new file mode 100755
index 0000000..3b155ad
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/common.sh
@@ -0,0 +1,115 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016-2017 by Ondřej Kuzník for Symas Corp.
+
+OVERLAY_CONFIG=${OVERLAY_CONFIG-data/config.ldif}
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1 for configuration..."
+$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 $SLEEP0
+
+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 ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Making a modification that will be hidden by the test config..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/hidden.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading test variant configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
diff --git a/contrib/slapd-modules/variant/tests/scripts/test001-config b/contrib/slapd-modules/variant/tests/scripts/test001-config
new file mode 100755
index 0000000..7a5559f
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test001-config
@@ -0,0 +1,209 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying invalid changes to config (should fail)..."
+for CHANGE in data/test001-*.ldif; do
+ echo "... $CHANGE"
+ . $CONFFILTER $BACKEND $MONITORDB < $CHANGE | \
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 80)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+# We run this search after the changes above and before restart so we can also
+# check the reconfiguration attempts actually had no side effects
+echo "Saving search output before server restart..."
+echo "# search output from dynamically configured server..." >> $SERVER3OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -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 $SLEEP0
+
+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 ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Testing slapd.conf support..."
+mkdir $TESTDIR/conftest $DBDIR2
+. $CONFFILTER $BACKEND $MONITORDB < $CONFTWO \
+ | sed -e '/^argsfile.*/a\
+moduleload ../variant.la' \
+ -e '/database.*monitor/i\
+include data/variant.conf' \
+ > $CONF2
+echo "database config" >>$CONF2
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF2
+
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+$SLAPD -Tt -f $CONF2 -F $TESTDIR/conftest -d $LVL >> $LOG2 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slaptest failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -F $TESTDIR/conftest -h $URI2 -d $LVL >> $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Gathering overlay configuration from both servers..."
+echo "# overlay configuration from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# overlay configuration from server configured from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -D cn=config -H $URI2 -y $CONFIGPWF \
+ -b "olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# We've already filtered out the ordering markers, now sort the entries
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s a < $SERVER2OUT > $SERVER2FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s a < $SERVER1OUT > $SERVER1FLT
+echo "Comparing filter output..."
+$CMP $SERVER2FLT $SERVER1FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SERVER1OUT $SERVER2OUT
+
+echo "Comparing search output on both servers..."
+echo "# search output from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# search output from server configured from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+$LDIFFILTER -s e < $SERVER2OUT > $SERVER2FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Comparing filter output..."
+$CMP $SERVER3FLT $SERVER1FLT > $CMPOUT && \
+$CMP $SERVER3FLT $SERVER2FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test002-add-delete b/contrib/slapd-modules/variant/tests/scripts/test002-add-delete
new file mode 100755
index 0000000..bd316b2
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test002-add-delete
@@ -0,0 +1,113 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Adding entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-01-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring entry as variant..."
+. $CONFFILTER $BACKEND $MONITORDB < data/additional-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Removing entry..."
+$LDAPDELETE -D $MANAGERDN -H $URI1 -w $PASSWD \
+ "cn=Gern Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding entry again (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-01-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Adding a regex entry (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-02-regex.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Adding entry with offending attributes removed..."
+grep -v '^description:' data/test002-01-entry.ldif | \
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test003-search b/contrib/slapd-modules/variant/tests/scripts/test003-search
new file mode 100755
index 0000000..2284ab7
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test003-search
@@ -0,0 +1,113 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -s base -H $URI1 \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ '(ou=Information Technology Division)' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "cn=ITD Staff,ou=Groups,$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(st=Alumni Association)" st \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test004-compare b/contrib/slapd-modules/variant/tests/scripts/test004-compare
new file mode 100755
index 0000000..c87d347
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test004-compare
@@ -0,0 +1,63 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing a regular entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "cn:Mark Elliot" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a variant entry..."
+$LDAPCOMPARE -H $URI1 \
+ "ou=People,$BASEDN" \
+ "description:The Example, Inc. at Anytown" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a regex entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Barbara Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ "ou:Information Technology Division" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test005-modify b/contrib/slapd-modules/variant/tests/scripts/test005-modify
new file mode 100755
index 0000000..4cbf289
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test005-modify
@@ -0,0 +1,120 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# for now, overlay returns success just after the modifications to the main
+# entry succeed, ignoring the rest should they fail
+echo "Modifying a nonexistent variant of an existing entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-variant-missing.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring nonexistent entry as variant..."
+. $CONFFILTER $BACKEND $MONITORDB < data/additional-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying an existing variant of above missing entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-modify-missing.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+32)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(description=*heard*)(st=*)(ou=alabama)(ou=*IT*))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >>$SEARCHOUT
+$LDAPSEARCH -H $URI1 -s base \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test006-acl b/contrib/slapd-modules/variant/tests/scripts/test006-acl
new file mode 100755
index 0000000..6b34fb8
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test006-acl
@@ -0,0 +1,323 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+case "$BACKEND" in ldif | null)
+ echo "$BACKEND backend does not support access controls, test skipped"
+ exit 0
+esac
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+CONF=$ACLCONF
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying test-specific configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test006-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectclass: groupOfNames
+member: dc=example,dc=com
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: John
+uid: jd
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Mark
+uid: me
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing search ACL processing..."
+
+echo "# Try to read an entry inside the Alumni Association container.
+# It should give us noSuchObject if we're not bound..." \
+>> $SEARCHOUT
+# FIXME: temporarily remove the "No such object" message to make
+# the test succeed even if SLAP_ACL_HONOR_DISCLOSE is not #define'd
+$LDAPSEARCH -b "$MELLIOTDN" -H $URI1 "(objectclass=*)" \
+ 2>&1 | grep -v "No such object" >> $SEARCHOUT
+
+echo >>$SEARCHOUT
+echo "# ... and should return appropriate attributes if we're bound as anyone
+# under Example." \
+>> $SEARCHOUT
+$LDAPSEARCH -b "$MELLIOTDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+$LDAPSEARCH -b "$MELLIOTDN" -H $URI1 \
+ -D "$BJORNSDN" -w bjorn "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+echo >>$SEARCHOUT
+echo "# Add & Delete subtree contents as seen by Babs" >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Add & Delete,dc=example,dc=com" -H $URI1 \
+ -D "$BABSDN" -w bjensen "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+echo >>$SEARCHOUT
+echo "# Add & Delete subtree contents as seen by Bjorn" >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Add & Delete,dc=example,dc=com" -H $URI1 \
+ -D "$BJORNSDN" -w bjorn "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+echo "Testing modifications..."
+echo "... ACL on the alternative entry"
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: seealso
+seealso: $BJORNSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Alumni Assoc Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: description
+description: added by bjensen (should fail)
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: seealso
+seealso: $BABSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Alumni Assoc Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: description
+description: added by bjorn (removed later)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+delete: description
+description: added by bjorn (removed later)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+sn: Jensen
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: another one added by bjorn (should succeed)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "... ACL on the variant entry"
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: added by bjensen (should fail)
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: added by bjorn (will be removed)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+replace: description
+description: added by bjensen (should fail)
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+delete: description
+description: added by bjorn (will be removed)
+-
+add: description
+description: added by jaj (should succeed)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep $SLEEP0
+
+echo >>$SEARCHOUT
+echo "Using ldapsearch to retrieve all the entries..."
+echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=Add & Delete,dc=example,dc=com" \
+ -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'objectClass=*' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test006-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test007-subtypes b/contrib/slapd-modules/variant/tests/scripts/test007-subtypes
new file mode 100755
index 0000000..177fc33
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test007-subtypes
@@ -0,0 +1,67 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing supertype of a variant attribute..."
+$LDAPCOMPARE -H $URI1 \
+ "ou=Groups,$BASEDN" \
+ "name:Alumni Association" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing searches against attribute supertypes..."
+echo "# Testing searches against attribute supertypes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(&(name=groups)(name=Alumni Association))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test007-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test008-variant-replication b/contrib/slapd-modules/variant/tests/scripts/test008-variant-replication
new file mode 100755
index 0000000..63e2d7e
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test008-variant-replication
@@ -0,0 +1,194 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcVariantPassReplication
+olcVariantPassReplication: FALSE
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring syncprov on the provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+mkdir $DBDIR4
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# regex variants do not replicate correctly and this is documented
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# regex variants do not replicate correctly and this is documented
+echo >> $SEARCHOUT
+$LDAPSEARCH -s base -H $URI1 \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ '(ou=Information Technology Division)' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# regex variants do not replicate correctly and this is documented
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "cn=ITD Staff,ou=Groups,$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(st=Alumni Association)" st \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test009-ignored-replication b/contrib/slapd-modules/variant/tests/scripts/test009-ignored-replication
new file mode 100755
index 0000000..aefbfa9
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test009-ignored-replication
@@ -0,0 +1,227 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring syncprov on the provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+mkdir $DBDIR4 $TESTDIR/confdir-consumer
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+
+echo "database config" >>$CONF4
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF4
+
+$SLAPD -f $CONF4 -F $TESTDIR/confdir-consumer -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+echo "Setting up variant overlay on consumer..."
+$LDAPSEARCH -D cn=config -H $URI4 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -s base -H $URI4 \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ '(ou=Information Technology Division)' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "cn=ITD Staff,ou=Groups,$BASEDN" -s base -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(st=Alumni Association)" st \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test010-limits b/contrib/slapd-modules/variant/tests/scripts/test010-limits
new file mode 100755
index 0000000..5828922
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test010-limits
@@ -0,0 +1,99 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ -z 1 "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "sizelimit reached ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing searches listing variants where limits just fit..."
+echo "# Testing searches listing variants where limits just fit..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI1 \
+ -z 3 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on variants going over the specified limit..."
+echo "# Testing searches filtering on variants going over the specified limit..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ -z 1 "(name=Alumni Association)" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "sizelimit reached ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test010-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test011-referral b/contrib/slapd-modules/variant/tests/scripts/test011-referral
new file mode 100755
index 0000000..37d6d8c
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test011-referral
@@ -0,0 +1,169 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+TESTDN="cn=Gern Jensen,ou=Information Technology Division,ou=People,$BASEDN"
+
+echo "Adding referral..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: $TESTDN
+changetype: add
+objectclass: referral
+objectclass: extensibleObject
+ref: ldap://hostB HostB
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring referral as variant..."
+. $CONFFILTER $BACKEND $MONITORDB < data/additional-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving a referral variant..."
+echo "# Retrieving a referral variant..." >> $SEARCHOUT
+$LDAPSEARCH -LLL -b "$BASEDN" -H $URI1 \
+ '(cn=Gern Jensen)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch: unexpected result ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving a referral variant (returns a referral)..."
+echo "# Retrieving a referral variant (returns a referral)..." >> $SEARCHOUT
+$LDAPSEARCH -b "$TESTDN" -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying a referral variant (returns a referral)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: $TESTDN
+changetype: modify
+delete: description
+EOMOD
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapmodify: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Comparing a referral variant (returns a referral)..."
+$LDAPCOMPARE -H $URI1 "$TESTDN" \
+ "description:The Example, Inc. at Anytown" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 10; then
+ echo "ldapcompare: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Reconfiguring variant underneath a referral..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: name={4}test002,olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcVariantEntry
+olcVariantEntry: cn=child,$TESTDN
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving a variant under a referral (returns a referral)..."
+echo "# Retrieving a variant under a referral (returns a referral)..." >> $SEARCHOUT
+$LDAPSEARCH -b "cn=child,$TESTDN" -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying a variant under a referral (returns a referral)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=child,$TESTDN
+changetype: modify
+delete: description
+EOMOD
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapmodify: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Comparing a variant under a referral (returns a referral)..."
+$LDAPCOMPARE -H $URI1 "cn=child,$TESTDN" \
+ "description:The Example, Inc. at Anytown" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 10; then
+ echo "ldapcompare: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test011-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test012-crossdb b/contrib/slapd-modules/variant/tests/scripts/test012-crossdb
new file mode 100755
index 0000000..8854a1b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test012-crossdb
@@ -0,0 +1,90 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Setting up another database and variant using an alternate there..."
+mkdir $DBDIR2
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ <<EOMOD >> $TESTOUT 2>&1
+dn: olcDatabase=ldif,cn=config
+changetype: add
+objectclass: olcLdifConfig
+olcSuffix: dc=demonstration,dc=com
+olcDbDirectory: $DBDIR2
+olcRootDn: $MANAGERDN
+
+dn: olcVariantVariantAttribute={1}seealso,name={0}variant,olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcVariantAlternativeEntry
+olcVariantAlternativeEntry: ou=Societies,dc=demonstration,dc=com
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding alternate entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test012-data.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking the variant gets resolved correctly..."
+echo "# Testing a search against a variant using another DB..." >> $SEARCHOUT
+#$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+# "seealso=dc=example,dc=com" \
+$LDAPSEARCH -b "ou=People,$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test012-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/variant.c b/contrib/slapd-modules/variant/variant.c
new file mode 100644
index 0000000..edf4832
--- /dev/null
+++ b/contrib/slapd-modules/variant/variant.c
@@ -0,0 +1,1424 @@
+/* variant.c - variant overlay */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2021 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed in 2016-2017 by Ondřej Kuzník for Symas Corp.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_VARIANT
+
+#include "slap.h"
+#include "slap-config.h"
+#include "ldap_queue.h"
+
+typedef enum variant_type_t {
+ VARIANT_INFO_PLAIN = 1 << 0,
+ VARIANT_INFO_REGEX = 1 << 1,
+
+ VARIANT_INFO_ALL = ~0
+} variant_type_t;
+
+typedef struct variant_info_t {
+ int passReplication;
+ LDAP_STAILQ_HEAD(variant_list, variantEntry_info) variants, regex_variants;
+} variant_info_t;
+
+typedef struct variantEntry_info {
+ variant_info_t *ov;
+ struct berval dn;
+ variant_type_t type;
+ regex_t *regex;
+ LDAP_SLIST_HEAD(attribute_list, variantAttr_info) attributes;
+ LDAP_STAILQ_ENTRY(variantEntry_info) next;
+} variantEntry_info;
+
+typedef struct variantAttr_info {
+ variantEntry_info *variant;
+ struct berval dn;
+ AttributeDescription *attr, *alternative;
+ LDAP_SLIST_ENTRY(variantAttr_info) next;
+} variantAttr_info;
+
+static int
+variant_build_dn(
+ Operation *op,
+ variantAttr_info *vai,
+ int nmatch,
+ regmatch_t *pmatch,
+ struct berval *out )
+{
+ struct berval dn, *ndn = &op->o_req_ndn;
+ char *dest, *p, *prev, *end = vai->dn.bv_val + vai->dn.bv_len;
+ size_t len = vai->dn.bv_len;
+ int rc;
+
+ p = vai->dn.bv_val;
+ while ( (p = memchr( p, '$', end - p )) != NULL ) {
+ len -= 1;
+ p += 1;
+
+ if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
+ int i = *p - '0';
+
+ len += ( pmatch[i].rm_eo - pmatch[i].rm_so );
+ } else if ( *p != '$' ) {
+ /* Should have been checked at configuration time */
+ assert(0);
+ }
+ len -= 1;
+ p += 1;
+ }
+
+ dest = dn.bv_val = ch_realloc( out->bv_val, len + 1 );
+ dn.bv_len = len;
+
+ prev = vai->dn.bv_val;
+ while ( (p = memchr( prev, '$', end - prev )) != NULL ) {
+ len = p - prev;
+ AC_MEMCPY( dest, prev, len );
+ dest += len;
+ p += 1;
+
+ if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
+ int i = *p - '0';
+ len = pmatch[i].rm_eo - pmatch[i].rm_so;
+
+ AC_MEMCPY( dest, ndn->bv_val + pmatch[i].rm_so, len );
+ dest += len;
+ } else if ( *p == '$' ) {
+ *dest++ = *p;
+ }
+ prev = p + 1;
+ }
+ len = end - prev;
+ AC_MEMCPY( dest, prev, len );
+ dest += len;
+ *dest = '\0';
+
+ rc = dnNormalize( 0, NULL, NULL, &dn, out, NULL );
+ ch_free( dn.bv_val );
+
+ return rc;
+}
+
+static int
+variant_build_entry(
+ Operation *op,
+ variantEntry_info *vei,
+ struct berval *dn,
+ Entry **ep,
+ int nmatch,
+ regmatch_t *pmatch )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ BackendDB *be_orig = op->o_bd, *db;
+ struct berval ndn = BER_BVNULL;
+ variantAttr_info *vai;
+ Attribute *a;
+ BerVarray nvals;
+ Entry *e;
+ unsigned int i;
+ int rc;
+
+ assert( ep );
+ assert( !*ep );
+
+ rc = overlay_entry_get_ov( op, dn, NULL, NULL, 0, &e, on );
+ if ( rc == LDAP_SUCCESS && is_entry_referral( e ) ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ rc = LDAP_REFERRAL;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ *ep = entry_dup( e );
+ overlay_entry_release_ov( op, e, 0, on );
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ rc = variant_build_dn( op, vai, nmatch, pmatch, &ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ } else {
+ ndn = vai->dn;
+ }
+
+ (void)attr_delete( &(*ep)->e_attrs, vai->attr );
+ op->o_bd = be_orig;
+
+ /* only select backend if not served by ours, would retrace all
+ * overlays again */
+ db = select_backend( &ndn, 0 );
+ if ( db && db != be_orig->bd_self ) {
+ op->o_bd = db;
+ rc = be_entry_get_rw( op, &ndn, NULL, vai->alternative, 0, &e );
+ } else {
+ rc = overlay_entry_get_ov(
+ op, &ndn, NULL, vai->alternative, 0, &e, on );
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+ case LDAP_INSUFFICIENT_ACCESS:
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ case LDAP_NO_SUCH_OBJECT:
+ rc = LDAP_SUCCESS;
+ continue;
+ break;
+ default:
+ goto done;
+ break;
+ }
+
+ a = attr_find( e->e_attrs, vai->alternative );
+
+ /* back-ldif doesn't check the attribute exists in the entry before
+ * returning it */
+ if ( a ) {
+ if ( a->a_nvals ) {
+ nvals = a->a_nvals;
+ } else {
+ nvals = a->a_vals;
+ }
+
+ for ( i = 0; i < a->a_numvals; i++ ) {
+ if ( backend_access( op, e, &ndn, vai->alternative, &nvals[i],
+ ACL_READ, NULL ) != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ rc = attr_merge_one( *ep, vai->attr, &a->a_vals[i], &nvals[i] );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+ }
+
+ if ( db && db != be_orig->bd_self ) {
+ be_entry_release_rw( op, e, 0 );
+ } else {
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:
+ op->o_bd = be_orig;
+ if ( rc != LDAP_SUCCESS && *ep ) {
+ entry_free( *ep );
+ *ep = NULL;
+ }
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ ch_free( ndn.bv_val );
+ }
+
+ return rc;
+}
+
+static int
+variant_find_config(
+ Operation *op,
+ variant_info_t *ov,
+ struct berval *ndn,
+ int which,
+ variantEntry_info **veip,
+ size_t nmatch,
+ regmatch_t *pmatch )
+{
+ variantEntry_info *vei;
+
+ assert( veip );
+
+ if ( which & VARIANT_INFO_PLAIN ) {
+ int diff;
+
+ LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
+ dnMatch( &diff, 0, NULL, NULL, ndn, &vei->dn );
+ if ( diff ) continue;
+
+ *veip = vei;
+ return LDAP_SUCCESS;
+ }
+ }
+
+ if ( which & VARIANT_INFO_REGEX ) {
+ LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
+ if ( regexec( vei->regex, ndn->bv_val, nmatch, pmatch, 0 ) ) {
+ continue;
+ }
+
+ *veip = vei;
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+variant_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ int rc;
+
+ /* Replication always uses the rootdn */
+ if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
+ be_isroot( op ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
+ "dn=%s\n", op->o_req_ndn.bv_val );
+
+ rc = variant_find_config(
+ op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, 0, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ variantAttr_info *vai;
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ Attribute *a;
+ for ( a = op->ora_e->e_attrs; a; a = a->a_next ) {
+ if ( a->a_desc == vai->attr ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ send_ldap_error( op, rs, rc,
+ "variant: trying to add variant attributes" );
+ goto done;
+ }
+ }
+ }
+ }
+ rc = SLAP_CB_CONTINUE;
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
+ "finished with %d\n",
+ rc );
+ return rc;
+}
+
+static int
+variant_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ regmatch_t pmatch[10];
+ int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
+ "dn=%s\n", op->o_req_ndn.bv_val );
+
+ rc = variant_find_config(
+ op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
+ if ( rc == LDAP_SUCCESS ) {
+ Entry *e = NULL;
+
+ rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
+ /* in case of error, just let the backend deal with the mod and the
+ * client should get a meaningful error back */
+ if ( rc != LDAP_SUCCESS ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = slap_compare_entry( op, e, op->orc_ava );
+
+ entry_free( e );
+ e = NULL;
+ }
+ }
+
+ if ( rc != SLAP_CB_CONTINUE ) {
+ rs->sr_err = rc;
+ send_ldap_result( op, rs );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
+ "finished with %d\n", rc );
+ return rc;
+}
+
+static int
+variant_cmp_op( const void *l, const void *r )
+{
+ const Operation *left = l, *right = r;
+ int diff;
+
+ dnMatch( &diff, 0, NULL, NULL, (struct berval *)&left->o_req_ndn,
+ (void *)&right->o_req_ndn );
+
+ return diff;
+}
+
+static int
+variant_run_mod( void *nop, void *arg )
+{
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { 0 };
+ Operation *op = nop;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int *rc = arg;
+
+ cb.sc_response = slap_null_cb;
+ op->o_callback = &cb;
+
+ Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
+ "running mod on dn=%s\n",
+ op->o_req_ndn.bv_val );
+ *rc = on->on_info->oi_orig->bi_op_modify( op, &nrs );
+ Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
+ "finished with %d\n", *rc );
+
+ return ( *rc != LDAP_SUCCESS );
+}
+
+/** Move the Modifications back to the original Op so that they can be disposed
+ * of by the original creator
+ */
+static int
+variant_reassign_mods( void *nop, void *arg )
+{
+ Operation *op = nop, *orig_op = arg;
+ Modifications *mod;
+
+ assert( op->orm_modlist );
+
+ for ( mod = op->orm_modlist; mod->sml_next; mod = mod->sml_next )
+ /* get the tail mod */;
+
+ mod->sml_next = orig_op->orm_modlist;
+ orig_op->orm_modlist = op->orm_modlist;
+
+ return LDAP_SUCCESS;
+}
+
+void
+variant_free_op( void *op )
+{
+ ch_free( ((Operation *)op)->o_req_ndn.bv_val );
+ ch_free( op );
+}
+
+static int
+variant_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ variantAttr_info *vai;
+ Avlnode *ops = NULL;
+ Entry *e = NULL;
+ Modifications *mod, *nextmod;
+ regmatch_t pmatch[10];
+ int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
+
+ /* Replication always uses the rootdn */
+ if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
+ be_isroot( op ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "dn=%s\n", op->o_req_ndn.bv_val );
+
+ rc = variant_find_config(
+ op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "not a variant\n" );
+ rc = SLAP_CB_CONTINUE;
+ goto done;
+ }
+
+ rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
+ /* in case of error, just let the backend deal with the mod and the client
+ * should get a meaningful error back */
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "failed to retrieve entry\n" );
+ rc = SLAP_CB_CONTINUE;
+ goto done;
+ }
+
+ rc = acl_check_modlist( op, e, op->orm_modlist );
+ entry_free( e );
+
+ if ( !rc ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ send_ldap_error( op, rs, rc, "" );
+ return rc;
+ }
+
+ for ( mod = op->orm_modlist; mod; mod = nextmod ) {
+ Operation needle = { .o_req_ndn = BER_BVNULL }, *nop;
+
+ nextmod = mod->sml_next;
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ if ( vai->attr == mod->sml_desc ) {
+ break;
+ }
+ }
+
+ if ( vai ) {
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ rc = variant_build_dn(
+ op, vai, nmatch, pmatch, &needle.o_req_ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+ } else {
+ needle.o_req_ndn = vai->dn;
+ }
+
+ nop = ldap_avl_find( ops, &needle, variant_cmp_op );
+ if ( nop == NULL ) {
+ nop = ch_calloc( 1, sizeof(Operation) );
+ *nop = *op;
+
+ ber_dupbv( &nop->o_req_ndn, &needle.o_req_ndn );
+ nop->o_req_dn = nop->o_req_ndn;
+ nop->orm_modlist = NULL;
+
+ rc = ldap_avl_insert( &ops, nop, variant_cmp_op, ldap_avl_dup_error );
+ assert( rc == 0 );
+ }
+ mod->sml_desc = vai->alternative;
+
+ op->orm_modlist = nextmod;
+ mod->sml_next = nop->orm_modlist;
+ nop->orm_modlist = mod;
+
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ ch_free( needle.o_req_ndn.bv_val );
+ }
+ }
+ }
+
+ if ( !ops ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "no variant attributes in mod\n" );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /*
+ * First run original Operation
+ * This will take care of making sure the entry exists as well.
+ *
+ * FIXME?
+ * Since we cannot make the subsequent Ops atomic wrt. this one, we just
+ * let it send the response as well. After all, the changes on the main DN
+ * have finished by then
+ */
+ rc = on->on_info->oi_orig->bi_op_modify( op, rs );
+ if ( rc == LDAP_SUCCESS ) {
+ /* FIXME: if a mod fails, should we attempt to apply the rest? */
+ ldap_avl_apply( ops, variant_run_mod, &rc, -1, AVL_INORDER );
+ }
+
+ ldap_avl_apply( ops, variant_reassign_mods, op, -1, AVL_INORDER );
+ ldap_avl_free( ops, variant_free_op );
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "finished with %d\n", rc );
+ return rc;
+}
+
+static int
+variant_search_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = op->o_callback->sc_private;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ int rc;
+
+ if ( rs->sr_type == REP_RESULT ) {
+ ch_free( op->o_callback );
+ op->o_callback = NULL;
+ }
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = variant_find_config(
+ op, ov, &rs->sr_entry->e_nname, VARIANT_INFO_PLAIN, &vei, 0, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_nentries--;
+ return rc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+variant_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ slap_callback *cb;
+ Entry *e = NULL;
+ regmatch_t pmatch[10];
+ int variantInScope = 0, rc = SLAP_CB_CONTINUE,
+ nmatch = sizeof(pmatch) / sizeof(regmatch_t);
+
+ if ( ov->passReplication && ( op->o_sync > SLAP_CONTROL_IGNORED ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "dn=%s, scope=%d\n",
+ op->o_req_ndn.bv_val, op->ors_scope );
+
+ LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
+ if ( !dnIsSuffixScope( &vei->dn, &op->o_req_ndn, op->ors_scope ) )
+ continue;
+
+ variantInScope = 1;
+
+ rc = variant_build_entry( op, vei, &vei->dn, &e, 0, NULL );
+ if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
+ rc = SLAP_CB_CONTINUE;
+ continue;
+ } else if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "failed to retrieve entry: dn=%s\n",
+ vei->dn.bv_val );
+ goto done;
+ }
+
+ if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "entry matched: dn=%s\n",
+ vei->dn.bv_val );
+ rs->sr_entry = e;
+ rs->sr_attrs = op->ors_attrs;
+ rc = send_search_entry( op, rs );
+ }
+ entry_free( e );
+ e = NULL;
+ }
+
+ /* Three options:
+ * - the entry has been handled above, in that case vei->type is VARIANT_INFO_PLAIN
+ * - the entry matches a regex, use the first one and we're finished
+ * - no configuration matches entry - do nothing
+ */
+ if ( op->ors_scope == LDAP_SCOPE_BASE &&
+ variant_find_config( op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei,
+ nmatch, pmatch ) == LDAP_SUCCESS &&
+ vei->type == VARIANT_INFO_REGEX ) {
+ rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
+ if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
+ rc = SLAP_CB_CONTINUE;
+ } else if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "failed to retrieve entry: dn=%s\n",
+ vei->dn.bv_val );
+ goto done;
+ } else {
+ if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "entry matched: dn=%s\n",
+ vei->dn.bv_val );
+ rs->sr_entry = e;
+ rs->sr_attrs = op->ors_attrs;
+ rc = send_search_entry( op, rs );
+ }
+ entry_free( e );
+ e = NULL;
+ goto done;
+ }
+ }
+ rc = SLAP_CB_CONTINUE;
+
+ if ( variantInScope ) {
+ cb = ch_calloc( 1, sizeof(slap_callback) );
+ cb->sc_private = on;
+ cb->sc_response = variant_search_response;
+ cb->sc_next = op->o_callback;
+
+ op->o_callback = cb;
+ }
+
+done:
+ if ( rc != SLAP_CB_CONTINUE ) {
+ rs->sr_err = (rc == LDAP_SUCCESS) ? rc : LDAP_OTHER;
+ send_ldap_result( op, rs );
+ }
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "finished with %d\n", rc );
+ return rc;
+}
+
+/* Configuration */
+
+static ConfigLDAPadd variant_ldadd;
+static ConfigLDAPadd variant_regex_ldadd;
+static ConfigLDAPadd variant_attr_ldadd;
+
+static ConfigDriver variant_set_dn;
+static ConfigDriver variant_set_regex;
+static ConfigDriver variant_set_alt_dn;
+static ConfigDriver variant_set_alt_pattern;
+static ConfigDriver variant_set_attribute;
+static ConfigDriver variant_add_alt_attr;
+static ConfigDriver variant_add_alt_attr_regex;
+
+static ConfigCfAdd variant_cfadd;
+
+enum
+{
+ VARIANT_ATTR = 1,
+ VARIANT_ATTR_ALT,
+
+ VARIANT_LAST,
+};
+
+static ConfigTable variant_cfg[] = {
+ { "passReplication", "on|off", 2, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof( variant_info_t, passReplication ),
+ "( OLcfgOvAt:FIXME.1 NAME 'olcVariantPassReplication' "
+ "DESC 'Whether to let searches with replication control "
+ "pass unmodified' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "variantDN", "dn", 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ variant_set_dn,
+ "( OLcfgOvAt:FIXME.2 NAME 'olcVariantEntry' "
+ "DESC 'DN of the variant entry' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "variantRegex", "regex", 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ variant_set_regex,
+ "( OLcfgOvAt:FIXME.6 NAME 'olcVariantEntryRegex' "
+ "DESC 'Pattern for the variant entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ /* These have no equivalent in slapd.conf */
+ { "", NULL, 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|VARIANT_ATTR,
+ variant_set_attribute,
+ "( OLcfgOvAt:FIXME.3 NAME 'olcVariantVariantAttribute' "
+ "DESC 'Attribute to fill in the entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|VARIANT_ATTR_ALT,
+ variant_set_attribute,
+ "( OLcfgOvAt:FIXME.4 NAME 'olcVariantAlternativeAttribute' "
+ "DESC 'Attribute to take from the alternative entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ variant_set_alt_dn,
+ "( OLcfgOvAt:FIXME.5 NAME 'olcVariantAlternativeEntry' "
+ "DESC 'DN of the alternative entry' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ variant_set_alt_pattern,
+ "( OLcfgOvAt:FIXME.7 NAME 'olcVariantAlternativeEntryPattern' "
+ "DESC 'Replacement pattern to locate the alternative entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ /* slapd.conf alternatives for the four above */
+ { "variantSpec", "attr attr2 dn", 4, 4, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ variant_add_alt_attr,
+ NULL, NULL, NULL
+ },
+ { "variantRegexSpec", "attr attr2 pattern", 4, 4, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ variant_add_alt_attr_regex,
+ NULL, NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs variant_ocs[] = {
+ { "( OLcfgOvOc:FIXME.1 "
+ "NAME 'olcVariantConfig' "
+ "DESC 'Variant overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcVariantPassReplication ) )",
+ Cft_Overlay, variant_cfg, NULL, variant_cfadd },
+ { "( OLcfgOvOc:FIXME.2 "
+ "NAME 'olcVariantVariant' "
+ "DESC 'Variant configuration' "
+ "MUST ( olcVariantEntry ) "
+ "MAY ( name ) "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_ldadd },
+ { "( OLcfgOvOc:FIXME.3 "
+ "NAME 'olcVariantAttribute' "
+ "DESC 'Variant attribute description' "
+ "MUST ( olcVariantVariantAttribute $ "
+ "olcVariantAlternativeAttribute $ "
+ "olcVariantAlternativeEntry "
+ ") "
+ "MAY name "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_attr_ldadd },
+ { "( OLcfgOvOc:FIXME.4 "
+ "NAME 'olcVariantRegex' "
+ "DESC 'Variant configuration' "
+ "MUST ( olcVariantEntryRegex ) "
+ "MAY ( name ) "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_regex_ldadd },
+ { "( OLcfgOvOc:FIXME.5 "
+ "NAME 'olcVariantAttributePattern' "
+ "DESC 'Variant attribute description' "
+ "MUST ( olcVariantVariantAttribute $ "
+ "olcVariantAlternativeAttribute $ "
+ "olcVariantAlternativeEntryPattern "
+ ") "
+ "MAY name "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_attr_ldadd },
+
+ { NULL, 0, NULL }
+};
+
+static int
+variant_set_dn( ConfigArgs *ca )
+{
+ variantEntry_info *vei2, *vei = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ int diff;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ value_add_one( &ca->rvalue_vals, &vei->dn );
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vei->dn.bv_val );
+ BER_BVZERO( &vei->dn );
+ return LDAP_SUCCESS;
+ }
+
+ if ( !vei ) {
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_PLAIN;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+ LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
+
+ ca->ca_private = vei;
+ }
+ vei->dn = ca->value_ndn;
+ ber_memfree( ca->value_dn.bv_val );
+
+ /* Each DN should only be listed once */
+ LDAP_STAILQ_FOREACH( vei2, &vei->ov->variants, next ) {
+ if ( vei == vei2 ) continue;
+
+ dnMatch( &diff, 0, NULL, NULL, &vei->dn, &vei2->dn );
+ if ( !diff ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_regex( ConfigArgs *ca )
+{
+ variantEntry_info *vei2, *vei = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_bv = vei->dn;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vei->dn.bv_val );
+ BER_BVZERO( &vei->dn );
+ regfree( vei->regex );
+ return LDAP_SUCCESS;
+ }
+
+ if ( !vei ) {
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_REGEX;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+ LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
+
+ ca->ca_private = vei;
+ }
+ vei->dn = ca->value_bv;
+
+ /* Each regex should only be listed once */
+ LDAP_STAILQ_FOREACH( vei2, &vei->ov->regex_variants, next ) {
+ if ( vei == vei2 ) continue;
+
+ if ( !ber_bvcmp( &ca->value_bv, &vei2->dn ) ) {
+ ch_free( vei );
+ ca->ca_private = NULL;
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+
+ vei->regex = ch_calloc( 1, sizeof(regex_t) );
+ if ( regcomp( vei->regex, vei->dn.bv_val, REG_EXTENDED ) ) {
+ ch_free( vei->regex );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_alt_dn( ConfigArgs *ca )
+{
+ variantAttr_info *vai = ca->ca_private;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ value_add_one( &ca->rvalue_vals, &vai->dn );
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vai->dn.bv_val );
+ BER_BVZERO( &vai->dn );
+ return LDAP_SUCCESS;
+ }
+
+ vai->dn = ca->value_ndn;
+ ber_memfree( ca->value_dn.bv_val );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_alt_pattern( ConfigArgs *ca )
+{
+ variantAttr_info *vai = ca->ca_private;
+ char *p = ca->value_bv.bv_val,
+ *end = ca->value_bv.bv_val + ca->value_bv.bv_len;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_bv = vai->dn;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vai->dn.bv_val );
+ BER_BVZERO( &vai->dn );
+ return LDAP_SUCCESS;
+ }
+
+ while ( (p = memchr( p, '$', end - p )) != NULL ) {
+ p += 1;
+
+ if ( ( ( *p >= '0' ) && ( *p <= '9' ) ) || ( *p == '$' ) ) {
+ p += 1;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "variant_set_alt_pattern: "
+ "invalid replacement pattern supplied '%s'\n",
+ ca->value_bv.bv_val );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+
+ vai->dn = ca->value_bv;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_attribute( ConfigArgs *ca )
+{
+ variantAttr_info *vai2, *vai = ca->ca_private;
+ char *s = ca->value_string;
+ const char *text;
+ AttributeDescription **ad;
+ int rc;
+
+ if ( ca->type == VARIANT_ATTR ) {
+ ad = &vai->attr;
+ } else {
+ ad = &vai->alternative;
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_string = ch_strdup( (*ad)->ad_cname.bv_val );
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ *ad = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( *s == '{' ) {
+ s = strchr( s, '}' );
+ if ( !s ) {
+ ca->reply.err = LDAP_UNDEFINED_TYPE;
+ return ca->reply.err;
+ }
+ s += 1;
+ }
+
+ rc = slap_str2ad( s, ad, &text );
+ ber_memfree( ca->value_string );
+ if ( rc ) {
+ return rc;
+ }
+
+ /* Both attributes have to share the same syntax */
+ if ( vai->attr && vai->alternative &&
+ vai->attr->ad_type->sat_syntax !=
+ vai->alternative->ad_type->sat_syntax ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ if ( ca->type == VARIANT_ATTR ) {
+ /* Each attribute should only be listed once */
+ LDAP_SLIST_FOREACH( vai2, &vai->variant->attributes, next ) {
+ if ( vai == vai2 ) continue;
+ if ( vai->attr == vai2->attr ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_add_alt_attr( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei =
+ LDAP_STAILQ_LAST( &ov->variants, variantEntry_info, next );
+ variantAttr_info *vai;
+ struct berval dn, ndn;
+ int rc;
+
+ vai = ch_calloc( 1, sizeof(variantAttr_info) );
+ vai->variant = vei;
+ LDAP_SLIST_ENTRY_INIT( vai, next );
+ ca->ca_private = vai;
+
+ ca->value_string = ch_strdup( ca->argv[1] );
+ ca->type = VARIANT_ATTR;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->value_string = ch_strdup( ca->argv[2] );
+ ca->type = VARIANT_ATTR_ALT;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ dn.bv_val = ca->argv[3];
+ dn.bv_len = strlen( dn.bv_val );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->type = 0;
+ BER_BVZERO( &ca->value_dn );
+ ca->value_ndn = ndn;
+ rc = variant_set_alt_dn( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ ch_free( ndn.bv_val );
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
+ } else {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+variant_add_alt_attr_regex( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei =
+ LDAP_STAILQ_LAST( &ov->regex_variants, variantEntry_info, next );
+ variantAttr_info *vai;
+ int rc;
+
+ vai = ch_calloc( 1, sizeof(variantAttr_info) );
+ vai->variant = vei;
+ LDAP_SLIST_ENTRY_INIT( vai, next );
+ ca->ca_private = vai;
+
+ ca->value_string = ch_strdup( ca->argv[1] );
+ ca->type = VARIANT_ATTR;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->value_string = ch_strdup( ca->argv[2] );
+ ca->type = VARIANT_ATTR_ALT;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->type = 0;
+ ber_str2bv( ca->argv[3], 0, 1, &ca->value_bv );
+ rc = variant_set_alt_pattern( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
+ } else {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+variant_ldadd_cleanup( ConfigArgs *ca )
+{
+ variantEntry_info *vei = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ assert( LDAP_SLIST_EMPTY(&vei->attributes) );
+ ch_free( vei );
+ return LDAP_SUCCESS;
+ }
+
+ if ( vei->type == VARIANT_INFO_PLAIN ) {
+ LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
+ } else {
+ LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ variant_info_t *ov;
+ variantEntry_info *vei;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != variant_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)cei->ce_bi;
+ ov = on->on_bi.bi_private;
+
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_PLAIN;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = vei;
+ config_push_cleanup( ca, variant_ldadd_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_regex_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ variant_info_t *ov;
+ variantEntry_info *vei;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != variant_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)cei->ce_bi;
+ ov = on->on_bi.bi_private;
+
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_REGEX;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = vei;
+ config_push_cleanup( ca, variant_ldadd_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_attr_ldadd_cleanup( ConfigArgs *ca )
+{
+ variantAttr_info *vai = ca->ca_private;
+ variantEntry_info *vei = vai->variant;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ ch_free( vai );
+ return LDAP_SUCCESS;
+ }
+
+ LDAP_SLIST_INSERT_HEAD(&vei->attributes, vai, next);
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_attr_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ variantEntry_info *vei;
+ variantAttr_info *vai;
+ CfEntryInfo *parent = cei->ce_parent;
+
+ if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
+ parent->ce_bi->bi_cf_ocs != variant_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ vei = (variantEntry_info *)cei->ce_private;
+
+ vai = ch_calloc( 1, sizeof(variantAttr_info) );
+ vai->variant = vei;
+ LDAP_SLIST_ENTRY_INIT(vai, next);
+
+ ca->ca_private = vai;
+ config_push_cleanup( ca, variant_attr_ldadd_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ variantAttr_info *vai;
+ Entry *e;
+ struct berval rdn;
+ int i = 0;
+
+ LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
+ int j = 0;
+ rdn.bv_len = snprintf(
+ ca->cr_msg, sizeof(ca->cr_msg), "name={%d}variant", i++ );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vei;
+ e = config_build_entry(
+ op, rs, p->e_private, ca, &rdn, &variant_ocs[1], NULL );
+ assert( e );
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "olcVariantVariantAttribute={%d}%s", j++,
+ vai->attr->ad_cname.bv_val );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vai;
+ config_build_entry(
+ op, rs, e->e_private, ca, &rdn, &variant_ocs[2], NULL );
+ }
+ }
+
+ LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
+ int j = 0;
+ rdn.bv_len = snprintf(
+ ca->cr_msg, sizeof(ca->cr_msg), "name={%d}regex", i++ );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vei;
+ e = config_build_entry(
+ op, rs, p->e_private, ca, &rdn, &variant_ocs[3], NULL );
+ assert( e );
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "olcVariantVariantAttribute={%d}%s", j++,
+ vai->attr->ad_cname.bv_val );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vai;
+ config_build_entry(
+ op, rs, e->e_private, ca, &rdn, &variant_ocs[4], NULL );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static slap_overinst variant;
+
+static int
+variant_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ variant_info_t *ov;
+
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "variant overlay must be instantiated within "
+ "a database.\n" );
+ return 1;
+ }
+
+ ov = ch_calloc( 1, sizeof(variant_info_t) );
+ LDAP_STAILQ_INIT(&ov->variants);
+ LDAP_STAILQ_INIT(&ov->regex_variants);
+
+ on->on_bi.bi_private = ov;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+
+ if ( ov ) {
+ while ( !LDAP_STAILQ_EMPTY( &ov->variants ) ) {
+ variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->variants );
+ LDAP_STAILQ_REMOVE_HEAD( &ov->variants, next );
+
+ while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
+ variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
+ LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
+
+ ber_memfree( vai->dn.bv_val );
+ ch_free( vai );
+ }
+ ber_memfree( vei->dn.bv_val );
+ ch_free( vei );
+ }
+ while ( !LDAP_STAILQ_EMPTY( &ov->regex_variants ) ) {
+ variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->regex_variants );
+ LDAP_STAILQ_REMOVE_HEAD( &ov->regex_variants, next );
+
+ while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
+ variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
+ LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
+
+ ber_memfree( vai->dn.bv_val );
+ ch_free( vai );
+ }
+ ber_memfree( vei->dn.bv_val );
+ ch_free( vei );
+ }
+ ch_free( ov );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+variant_initialize()
+{
+ int rc;
+
+ variant.on_bi.bi_type = "variant";
+ variant.on_bi.bi_db_init = variant_db_init;
+ variant.on_bi.bi_db_destroy = variant_db_destroy;
+
+ variant.on_bi.bi_op_add = variant_op_add;
+ variant.on_bi.bi_op_compare = variant_op_compare;
+ variant.on_bi.bi_op_modify = variant_op_mod;
+ variant.on_bi.bi_op_search = variant_op_search;
+
+ variant.on_bi.bi_cf_ocs = variant_ocs;
+
+ rc = config_register_schema( variant_cfg, variant_ocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &variant );
+}
+
+#if SLAPD_OVER_VARIANT == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return variant_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_VARIANT */
diff --git a/contrib/slapd-modules/vc/Makefile b/contrib/slapd-modules/vc/Makefile
new file mode 100644
index 0000000..9fe67ab
--- /dev/null
+++ b/contrib/slapd-modules/vc/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = vc.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+vc.la: vc.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/vc/vc.c b/contrib/slapd-modules/vc/vc.c
new file mode 100644
index 0000000..0760af2
--- /dev/null
+++ b/contrib/slapd-modules/vc/vc.c
@@ -0,0 +1,439 @@
+/* vc.c - LDAP Verify Credentials extop (no spec yet) */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAP Verify Credentials: suggested by Kurt Zeilenga
+ * no spec yet
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+#include "ac/string.h"
+
+typedef struct vc_conn_t {
+ struct vc_conn_t *conn;
+ Connection connbuf;
+ OperationBuffer opbuf;
+ Operation *op;
+ int refcnt;
+} vc_conn_t;
+
+static const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS);
+static ldap_pvt_thread_mutex_t vc_mutex;
+static Avlnode *vc_tree;
+
+static int
+vc_conn_cmp( const void *c1, const void *c2 )
+{
+ const vc_conn_t *vc1 = (const vc_conn_t *)c1;
+ const vc_conn_t *vc2 = (const vc_conn_t *)c2;
+
+ return SLAP_PTRCMP( vc1->conn, vc2->conn );
+}
+
+static int
+vc_conn_dup( void *c1, void *c2 )
+{
+ vc_conn_t *vc1 = (vc_conn_t *)c1;
+ vc_conn_t *vc2 = (vc_conn_t *)c2;
+
+ if ( vc1->conn == vc2->conn ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vc_create_response(
+ void *conn,
+ int resultCode,
+ const char *diagnosticMessage,
+ struct berval *servercred,
+ struct berval *authzid,
+ LDAPControl **ctrls,
+ struct berval **val )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval bv;
+ int rc;
+
+ assert( val != NULL );
+
+ *val = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ (void)ber_printf( ber, "{is" /*}*/ , resultCode, diagnosticMessage ? diagnosticMessage : "" );
+
+ if ( conn ) {
+ struct berval cookie;
+
+ cookie.bv_len = sizeof( conn );
+ cookie.bv_val = (char *)&conn;
+ (void)ber_printf( ber, "tO", 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, &cookie );
+ }
+
+ if ( servercred ) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS, servercred );
+ }
+
+#if 0
+ if ( authzid ) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_AUTHZID, authzid );
+ }
+#endif
+
+ if ( ctrls ) {
+ int c;
+
+ rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS );
+ if ( rc == -1 ) goto done;
+
+ for ( c = 0; ctrls[c] != NULL; c++ ) {
+ rc = ber_printf( ber, "{s" /*}*/, ctrls[c]->ldctl_oid );
+
+ if ( ctrls[c]->ldctl_iscritical ) {
+ rc = ber_printf( ber, "b", (ber_int_t)ctrls[c]->ldctl_iscritical ) ;
+ if ( rc == -1 ) goto done;
+ }
+
+ if ( ctrls[c]->ldctl_value.bv_val != NULL ) {
+ rc = ber_printf( ber, "O", &ctrls[c]->ldctl_value );
+ if( rc == -1 ) goto done;
+ }
+
+ rc = ber_printf( ber, /*{*/"N}" );
+ if ( rc == -1 ) goto done;
+ }
+
+ rc = ber_printf( ber, /*{*/"N}" );
+ if ( rc == -1 ) goto done;
+ }
+
+ rc = ber_printf( ber, /*{*/ "}" );
+ if ( rc == -1 ) goto done;
+
+ rc = ber_flatten2( ber, &bv, 0 );
+ if ( rc == 0 ) {
+ *val = ber_bvdup( &bv );
+ }
+
+done:;
+ ber_free_buf( ber );
+
+ return rc;
+}
+
+typedef struct vc_cb_t {
+ struct berval sasldata;
+ LDAPControl **ctrls;
+} vc_cb_t;
+
+static int
+vc_cb(
+ Operation *op,
+ SlapReply *rs )
+{
+ vc_cb_t *vc = (vc_cb_t *)op->o_callback->sc_private;
+
+ if ( rs->sr_tag == LDAP_RES_BIND ) {
+ if ( rs->sr_sasldata != NULL ) {
+ ber_dupbv( &vc->sasldata, rs->sr_sasldata );
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ vc->ctrls = ldap_controls_dup( rs->sr_ctrls );
+ }
+ }
+
+ return 0;
+}
+
+static int
+vc_exop(
+ Operation *op,
+ SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval reqdata = BER_BVNULL;
+
+ struct berval cookie = BER_BVNULL;
+ struct berval bdn = BER_BVNULL;
+ ber_tag_t authtag;
+ struct berval cred = BER_BVNULL;
+ struct berval ndn = BER_BVNULL;
+ struct berval mechanism = BER_BVNULL;
+
+ vc_conn_t *conn = NULL;
+ vc_cb_t vc = { 0 };
+ slap_callback sc = { 0 };
+ SlapReply rs2 = { 0 };
+
+ if ( op->ore_reqdata == NULL || op->ore_reqdata->bv_len == 0 ) {
+ rs->sr_text = "empty request data field in VerifyCredentials exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* optimistic */
+ rs->sr_err = LDAP_SUCCESS;
+
+ ber_dupbv_x( &reqdata, op->ore_reqdata, op->o_tmpmemctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+ if ( tag != LBER_SEQUENCE ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
+ /*
+ * cookie: the pointer to the connection
+ * of this operation
+ */
+
+ ber_scanf( ber, "m", &cookie );
+ if ( cookie.bv_len != sizeof(Connection *) ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ /* DN, authtag */
+ tag = ber_scanf( ber, "mt", &bdn, &authtag );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &bdn, &ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ switch ( authtag ) {
+ case LDAP_AUTH_SIMPLE:
+ /* cookie only makes sense for SASL bind (so far) */
+ if ( !BER_BVISNULL( &cookie ) ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_scanf( ber, "m", &cred );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ break;
+
+ case LDAP_AUTH_SASL:
+ tag = ber_scanf( ber, "{m" /*}*/ , &mechanism );
+ if ( tag == LBER_ERROR ||
+ BER_BVISNULL( &mechanism ) || BER_BVISEMPTY( &mechanism ) )
+ {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_OCTETSTRING ) {
+ ber_scanf( ber, "m", &cred );
+ }
+
+ tag = ber_scanf( ber, /*{*/ "}" );
+ break;
+
+ default:
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ vc_conn_t tmp = { 0 };
+
+ AC_MEMCPY( (char *)&tmp.conn, (const char *)cookie.bv_val, cookie.bv_len );
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ conn = (vc_conn_t *)ldap_avl_find( vc_tree, (caddr_t)&tmp, vc_conn_cmp );
+ if ( conn == NULL || ( conn != NULL && conn->refcnt != 0 ) ) {
+ conn = NULL;
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ conn->refcnt++;
+ operation_counter_init( conn->op, op->o_threadctx );
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+
+ } else {
+ conn = (vc_conn_t *)SLAP_CALLOC( 1, sizeof( vc_conn_t ) );
+ conn->refcnt = 1;
+
+ connection_fake_init2( &conn->connbuf, &conn->opbuf, op->o_threadctx, 0 );
+ conn->op = &conn->opbuf.ob_op;
+ snprintf( conn->op->o_log_prefix, sizeof( conn->op->o_log_prefix ),
+ "%s VERIFYCREDENTIALS", op->o_log_prefix );
+ }
+
+ conn->op->o_tag = LDAP_REQ_BIND;
+ memset( &conn->op->oq_bind, 0, sizeof( conn->op->oq_bind ) );
+ conn->op->o_req_dn = ndn;
+ conn->op->o_req_ndn = ndn;
+ conn->op->o_protocol = LDAP_VERSION3;
+ conn->op->orb_method = authtag;
+ conn->op->o_callback = &sc;
+
+ /* TODO: controls */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
+ conn->op->o_ber = ber;
+ rc = get_ctrls2( conn->op, &rs2, 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ tag = ber_skip_tag( ber, &len );
+ if ( len || tag != LBER_DEFAULT ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ switch ( authtag ) {
+ case LDAP_AUTH_SIMPLE:
+ break;
+
+ case LDAP_AUTH_SASL:
+ conn->op->orb_mech = mechanism;
+ break;
+ }
+
+ conn->op->orb_cred = cred;
+ sc.sc_response = vc_cb;
+ sc.sc_private = &vc;
+
+ conn->op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_bind( conn->op, &rs2 );
+
+ if ( conn->op->o_conn->c_sasl_bind_in_progress ) {
+ rc = vc_create_response( conn, rs2.sr_err, rs2.sr_text,
+ !BER_BVISEMPTY( &vc.sasldata ) ? &vc.sasldata : NULL,
+ NULL,
+ vc.ctrls, &rs->sr_rspdata );
+
+ } else {
+ rc = vc_create_response( NULL, rs2.sr_err, rs2.sr_text,
+ NULL,
+ &conn->op->o_conn->c_dn,
+ vc.ctrls, &rs->sr_rspdata );
+ }
+
+ if ( rc != 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &conn->op->o_conn->c_dn ) &&
+ conn->op->o_conn->c_dn.bv_val != conn->op->o_conn->c_ndn.bv_val )
+ ber_memfree( conn->op->o_conn->c_dn.bv_val );
+ if ( !BER_BVISNULL( &conn->op->o_conn->c_ndn ) )
+ ber_memfree( conn->op->o_conn->c_ndn.bv_val );
+
+done:;
+ if ( conn ) {
+ if ( conn->op->o_conn->c_sasl_bind_in_progress ) {
+ if ( conn->conn == NULL ) {
+ conn->conn = conn;
+ conn->refcnt--;
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ rc = ldap_avl_insert( &vc_tree, (caddr_t)conn,
+ vc_conn_cmp, vc_conn_dup );
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ assert( rc == 0 );
+
+ } else {
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ conn->refcnt--;
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ }
+
+ } else {
+ if ( conn->conn != NULL ) {
+ vc_conn_t *tmp;
+
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ tmp = ldap_avl_delete( &vc_tree, (caddr_t)conn, vc_conn_cmp );
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ }
+ SLAP_FREE( conn );
+ }
+ }
+
+ if ( vc.ctrls ) {
+ ldap_controls_free( vc.ctrls );
+ vc.ctrls = NULL;
+ }
+
+ if ( !BER_BVISNULL( &ndn ) ) {
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &ndn );
+ }
+
+ op->o_tmpfree( reqdata.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &reqdata );
+
+ return rs->sr_err;
+}
+
+static int
+vc_initialize( void )
+{
+ int rc;
+
+ rc = load_extop2( (struct berval *)&vc_exop_oid_bv,
+ SLAP_EXOP_HIDE, vc_exop, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "vc_initialize: unable to register VerifyCredentials exop: %d.\n",
+ rc );
+ }
+
+ ldap_pvt_thread_mutex_init( &vc_mutex );
+
+ return rc;
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return vc_initialize();
+}
+