summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/emptyds/emptyds.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/slapd-modules/emptyds/emptyds.c')
-rw-r--r--contrib/slapd-modules/emptyds/emptyds.c325
1 files changed, 325 insertions, 0 deletions
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();
+}