Commit c809208d authored by Ondřej Kuzník's avatar Ondřej Kuzník Committed by Quanah Gibson-Mount
Browse files

ITS#9472 Add datamorph overlay

parent 0b1ad3fc
# $OpenLDAP$
# This work is part of OpenLDAP Software <http://www.openldap.org/>.
#
# Copyright 1998-2021 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 -Wall
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
%.lo: %.c
$(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $<
%.la: %.lo
$(LIBTOOL) --mode=link $(CC) $(OPT) -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:
/* datamorph.c - enumerated and native integer value support */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2016-2021 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 = 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 = 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 ) {